summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/tcg/hexagon/Makefile.target1
-rw-r--r--tests/tcg/hexagon/mem_noshuf.c122
-rw-r--r--tests/tcg/hexagon/mem_noshuf_exception.c146
-rw-r--r--tests/tcg/s390x/Makefile.target7
-rw-r--r--tests/tcg/s390x/vfminmax.c411
5 files changed, 682 insertions, 5 deletions
diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target
index 23b9870534..96a4d7a614 100644
--- a/tests/tcg/hexagon/Makefile.target
+++ b/tests/tcg/hexagon/Makefile.target
@@ -35,6 +35,7 @@ HEX_TESTS += preg_alias
HEX_TESTS += dual_stores
HEX_TESTS += multi_result
HEX_TESTS += mem_noshuf
+HEX_TESTS += mem_noshuf_exception
HEX_TESTS += circ
HEX_TESTS += brev
HEX_TESTS += load_unpack
diff --git a/tests/tcg/hexagon/mem_noshuf.c b/tests/tcg/hexagon/mem_noshuf.c
index dd714d5e98..0f4064e700 100644
--- a/tests/tcg/hexagon/mem_noshuf.c
+++ b/tests/tcg/hexagon/mem_noshuf.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2022 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
@@ -84,6 +84,70 @@ MEM_NOSHUF32(mem_noshuf_sd_luh, long long, unsigned short, memd, memuh)
MEM_NOSHUF32(mem_noshuf_sd_lw, long long, signed int, memd, memw)
MEM_NOSHUF64(mem_noshuf_sd_ld, long long, signed long long, memd, memd)
+static inline int pred_lw_sw(int pred, int *p, int *q, int x, int y)
+{
+ int ret;
+ asm volatile("p0 = cmp.eq(%5, #0)\n\t"
+ "%0 = %3\n\t"
+ "{\n\t"
+ " memw(%1) = %4\n\t"
+ " if (!p0) %0 = memw(%2)\n\t"
+ "}:mem_noshuf\n"
+ : "=&r"(ret)
+ : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred)
+ : "p0", "memory");
+ return ret;
+}
+
+static inline int pred_lw_sw_pi(int pred, int *p, int *q, int x, int y)
+{
+ int ret;
+ asm volatile("p0 = cmp.eq(%5, #0)\n\t"
+ "%0 = %3\n\t"
+ "r7 = %2\n\t"
+ "{\n\t"
+ " memw(%1) = %4\n\t"
+ " if (!p0) %0 = memw(r7++#4)\n\t"
+ "}:mem_noshuf\n"
+ : "=&r"(ret)
+ : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred)
+ : "r7", "p0", "memory");
+ return ret;
+}
+
+static inline long long pred_ld_sd(int pred, long long *p, long long *q,
+ long long x, long long y)
+{
+ unsigned long long ret;
+ asm volatile("p0 = cmp.eq(%5, #0)\n\t"
+ "%0 = %3\n\t"
+ "{\n\t"
+ " memd(%1) = %4\n\t"
+ " if (!p0) %0 = memd(%2)\n\t"
+ "}:mem_noshuf\n"
+ : "=&r"(ret)
+ : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred)
+ : "p0", "memory");
+ return ret;
+}
+
+static inline long long pred_ld_sd_pi(int pred, long long *p, long long *q,
+ long long x, long long y)
+{
+ long long ret;
+ asm volatile("p0 = cmp.eq(%5, #0)\n\t"
+ "%0 = %3\n\t"
+ "r7 = %2\n\t"
+ "{\n\t"
+ " memd(%1) = %4\n\t"
+ " if (!p0) %0 = memd(r7++#8)\n\t"
+ "}:mem_noshuf\n"
+ : "=&r"(ret)
+ : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred)
+ : "p0", "memory");
+ return ret;
+}
+
static inline unsigned int cancel_sw_lb(int pred, int *p, signed char *q, int x)
{
unsigned int ret;
@@ -126,18 +190,22 @@ typedef union {
int err;
-static void check32(int n, int expect)
+#define check32(n, expect) check32_(n, expect, __LINE__)
+
+static void check32_(int n, int expect, int line)
{
if (n != expect) {
- printf("ERROR: 0x%08x != 0x%08x\n", n, expect);
+ printf("ERROR: 0x%08x != 0x%08x, line %d\n", n, expect, line);
err++;
}
}
-static void check64(long long n, long long expect)
+#define check64(n, expect) check64_(n, expect, __LINE__)
+
+static void check64_(long long n, long long expect, int line)
{
if (n != expect) {
- printf("ERROR: 0x%08llx != 0x%08llx\n", n, expect);
+ printf("ERROR: 0x%08llx != 0x%08llx, line %d\n", n, expect, line);
err++;
}
}
@@ -323,6 +391,50 @@ int main()
res64 = mem_noshuf_sd_ld(&n.d[0], &n.d[1], 0x123456789abcdef0LL);
check64(res64, 0xffffffffffffffffLL);
+ n.w[0] = ~0;
+ res32 = pred_lw_sw(0, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda);
+ check32(res32, 0x12345678);
+ check32(n.w[0], 0xc0ffeeda);
+
+ n.w[0] = ~0;
+ res32 = pred_lw_sw(1, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda);
+ check32(res32, 0xc0ffeeda);
+ check32(n.w[0], 0xc0ffeeda);
+
+ n.w[0] = ~0;
+ res32 = pred_lw_sw_pi(0, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda);
+ check32(res32, 0x12345678);
+ check32(n.w[0], 0xc0ffeeda);
+
+ n.w[0] = ~0;
+ res32 = pred_lw_sw_pi(1, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda);
+ check32(res32, 0xc0ffeeda);
+ check32(n.w[0], 0xc0ffeeda);
+
+ n.d[0] = ~0LL;
+ res64 = pred_ld_sd(0, &n.d[0], &n.d[0],
+ 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL);
+ check64(res64, 0x1234567812345678LL);
+ check64(n.d[0], 0xc0ffeedac0ffeedaLL);
+
+ n.d[0] = ~0LL;
+ res64 = pred_ld_sd(1, &n.d[0], &n.d[0],
+ 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL);
+ check64(res64, 0xc0ffeedac0ffeedaLL);
+ check64(n.d[0], 0xc0ffeedac0ffeedaLL);
+
+ n.d[0] = ~0LL;
+ res64 = pred_ld_sd_pi(0, &n.d[0], &n.d[0],
+ 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL);
+ check64(res64, 0x1234567812345678LL);
+ check64(n.d[0], 0xc0ffeedac0ffeedaLL);
+
+ n.d[0] = ~0LL;
+ res64 = pred_ld_sd_pi(1, &n.d[0], &n.d[0],
+ 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL);
+ check64(res64, 0xc0ffeedac0ffeedaLL);
+ check64(n.d[0], 0xc0ffeedac0ffeedaLL);
+
puts(err ? "FAIL" : "PASS");
return err;
}
diff --git a/tests/tcg/hexagon/mem_noshuf_exception.c b/tests/tcg/hexagon/mem_noshuf_exception.c
new file mode 100644
index 0000000000..08660ea3e1
--- /dev/null
+++ b/tests/tcg/hexagon/mem_noshuf_exception.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright(c) 2022 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/>.
+ */
+
+/*
+ * Test the VLIW semantics of exceptions with mem_noshuf
+ *
+ * When a packet has the :mem_noshuf attribute, the semantics dictate
+ * That the load will get the data from the store if the addresses overlap.
+ * To accomplish this, we perform the store first. However, we have to
+ * handle the case where the store raises an exception. In that case, the
+ * store should not alter the machine state.
+ *
+ * We test this with a mem_noshuf packet with a store to a global variable,
+ * "should_not_change" and a load from NULL. After the SIGSEGV is caught,
+ * we check * that the "should_not_change" value is the same.
+ *
+ * We also check that a predicated load where the predicate is false doesn't
+ * raise an exception and allows the store to happen.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <signal.h>
+
+int err;
+int segv_caught;
+
+#define SHOULD_NOT_CHANGE_VAL 5
+int should_not_change = SHOULD_NOT_CHANGE_VAL;
+
+#define OK_TO_CHANGE_VAL 13
+int ok_to_change = OK_TO_CHANGE_VAL;
+
+static void __check(const char *filename, int line, int x, int expect)
+{
+ if (x != expect) {
+ printf("ERROR %s:%d - %d != %d\n",
+ filename, line, x, expect);
+ err++;
+ }
+}
+
+#define check(x, expect) __check(__FILE__, __LINE__, (x), (expect))
+
+static void __chk_error(const char *filename, int line, int ret)
+{
+ if (ret < 0) {
+ printf("ERROR %s:%d - %d\n", filename, line, ret);
+ err++;
+ }
+}
+
+#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret))
+
+jmp_buf jmp_env;
+
+static void sig_segv(int sig, siginfo_t *info, void *puc)
+{
+ check(sig, SIGSEGV);
+ segv_caught = 1;
+ longjmp(jmp_env, 1);
+}
+
+int main()
+{
+ struct sigaction act;
+ int dummy32;
+ long long dummy64;
+ void *p;
+
+ /* SIGSEGV test */
+ act.sa_sigaction = sig_segv;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ chk_error(sigaction(SIGSEGV, &act, NULL));
+ if (setjmp(jmp_env) == 0) {
+ asm volatile("r18 = ##should_not_change\n\t"
+ "r19 = #0\n\t"
+ "{\n\t"
+ " memw(r18) = #7\n\t"
+ " %0 = memw(r19)\n\t"
+ "}:mem_noshuf\n\t"
+ : "=r"(dummy32) : : "r18", "r19", "memory");
+ }
+
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ chk_error(sigaction(SIGSEGV, &act, NULL));
+
+ check(segv_caught, 1);
+ check(should_not_change, SHOULD_NOT_CHANGE_VAL);
+
+ /*
+ * Check that a predicated load where the predicate is false doesn't
+ * raise an exception and allows the store to happen.
+ */
+ asm volatile("r18 = ##ok_to_change\n\t"
+ "r19 = #0\n\t"
+ "p0 = cmp.gt(r0, r0)\n\t"
+ "{\n\t"
+ " memw(r18) = #7\n\t"
+ " if (p0) %0 = memw(r19)\n\t"
+ "}:mem_noshuf\n\t"
+ : "=r"(dummy32) : : "r18", "r19", "p0", "memory");
+
+ check(ok_to_change, 7);
+
+ /*
+ * Also check that the post-increment doesn't happen when the
+ * predicate is false.
+ */
+ ok_to_change = OK_TO_CHANGE_VAL;
+ p = NULL;
+ asm volatile("r18 = ##ok_to_change\n\t"
+ "p0 = cmp.gt(r0, r0)\n\t"
+ "{\n\t"
+ " memw(r18) = #9\n\t"
+ " if (p0) %1 = memd(%0 ++ #8)\n\t"
+ "}:mem_noshuf\n\t"
+ : "+r"(p), "=r"(dummy64) : : "r18", "p0", "memory");
+
+ check(ok_to_change, 9);
+ check((int)p, (int)NULL);
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 3124172736..1a7a4a2f59 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -17,6 +17,13 @@ TESTS+=trap
TESTS+=signals-s390x
TESTS+=branch-relative-long
+Z14_TESTS=vfminmax
+vfminmax: LDFLAGS+=-lm
+$(Z14_TESTS): CFLAGS+=-march=z14 -O2
+
+TESTS+=$(if $(shell $(CC) -march=z14 -S -o /dev/null -xc /dev/null \
+ >/dev/null 2>&1 && echo OK),$(Z14_TESTS))
+
VECTOR_TESTS=vxeh2_vs
VECTOR_TESTS+=vxeh2_vcvt
VECTOR_TESTS+=vxeh2_vlstr
diff --git a/tests/tcg/s390x/vfminmax.c b/tests/tcg/s390x/vfminmax.c
new file mode 100644
index 0000000000..22629df160
--- /dev/null
+++ b/tests/tcg/s390x/vfminmax.c
@@ -0,0 +1,411 @@
+#define _GNU_SOURCE
+#include <fenv.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * vfmin/vfmax instruction execution.
+ */
+#define VFMIN 0xEE
+#define VFMAX 0xEF
+
+extern char insn[6];
+asm(".pushsection .rwx,\"awx\",@progbits\n"
+ ".globl insn\n"
+ /* e7 89 a0 00 2e ef */
+ "insn: vfmaxsb %v24,%v25,%v26,0\n"
+ ".popsection\n");
+
+static void vfminmax(unsigned int op,
+ unsigned int m4, unsigned int m5, unsigned int m6,
+ void *v1, const void *v2, const void *v3)
+{
+ insn[3] = (m6 << 4) | m5;
+ insn[4] = (m4 << 4) | 0x0e;
+ insn[5] = op;
+
+ asm("vl %%v25,%[v2]\n"
+ "vl %%v26,%[v3]\n"
+ "ex 0,%[insn]\n"
+ "vst %%v24,%[v1]\n"
+ : [v1] "=m" (*(char (*)[16])v1)
+ : [v2] "m" (*(char (*)[16])v2)
+ , [v3] "m" (*(char (*)[16])v3)
+ , [insn] "m"(insn)
+ : "v24", "v25", "v26");
+}
+
+/*
+ * Floating-point value classes.
+ */
+#define N_FORMATS 3
+#define N_SIGNED_CLASSES 8
+static const size_t float_sizes[N_FORMATS] = {
+ /* M4 == 2: short */ 4,
+ /* M4 == 3: long */ 8,
+ /* M4 == 4: extended */ 16,
+};
+static const size_t e_bits[N_FORMATS] = {
+ /* M4 == 2: short */ 8,
+ /* M4 == 3: long */ 11,
+ /* M4 == 4: extended */ 15,
+};
+static const unsigned char signed_floats[N_FORMATS][N_SIGNED_CLASSES][2][16] = {
+ /* M4 == 2: short */
+ {
+ /* -inf */ {{0xff, 0x80, 0x00, 0x00},
+ {0xff, 0x80, 0x00, 0x00}},
+ /* -Fn */ {{0xc2, 0x28, 0x00, 0x00},
+ {0xc2, 0x29, 0x00, 0x00}},
+ /* -0 */ {{0x80, 0x00, 0x00, 0x00},
+ {0x80, 0x00, 0x00, 0x00}},
+ /* +0 */ {{0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00}},
+ /* +Fn */ {{0x42, 0x28, 0x00, 0x00},
+ {0x42, 0x2a, 0x00, 0x00}},
+ /* +inf */ {{0x7f, 0x80, 0x00, 0x00},
+ {0x7f, 0x80, 0x00, 0x00}},
+ /* QNaN */ {{0x7f, 0xff, 0xff, 0xff},
+ {0x7f, 0xff, 0xff, 0xfe}},
+ /* SNaN */ {{0x7f, 0xbf, 0xff, 0xff},
+ {0x7f, 0xbf, 0xff, 0xfd}},
+ },
+
+ /* M4 == 3: long */
+ {
+ /* -inf */ {{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* -Fn */ {{0xc0, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* +Fn */ {{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x40, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* +inf */ {{0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}},
+ /* SNaN */ {{0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}},
+ },
+
+ /* M4 == 4: extended */
+ {
+ /* -inf */ {{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* -Fn */ {{0xc0, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xc0, 0x04, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* +Fn */ {{0x40, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x40, 0x04, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* +inf */ {{0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}},
+ /* SNaN */ {{0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}},
+ },
+};
+
+/*
+ * PoP tables as close to the original as possible.
+ */
+struct signed_test {
+ int op;
+ int m6;
+ const char *m6_desc;
+ const char *table[N_SIGNED_CLASSES][N_SIGNED_CLASSES];
+} signed_tests[] = {
+ {
+ .op = VFMIN,
+ .m6 = 0,
+ .m6_desc = "IEEE MinNum",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* +0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"},
+ {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"},
+ },
+ },
+ {
+ .op = VFMIN,
+ .m6 = 1,
+ .m6_desc = "JAVA Math.Min()",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"},
+ {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"},
+ {/* -0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"},
+ {/* +0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"},
+ {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(b)", "Xi: T(b*)"},
+ {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"},
+ {/* QNaN */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"},
+ },
+ },
+ {
+ .op = VFMIN,
+ .m6 = 2,
+ .m6_desc = "C-style Min Macro",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"},
+ {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"},
+ {/* -0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"},
+ {/* +0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"},
+ {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "Xi: T(b)", "Xi: T(b)"},
+ {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b)", "Xi: T(b)"},
+ {/* QNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"},
+ {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"},
+ },
+ },
+ {
+ .op = VFMIN,
+ .m6 = 3,
+ .m6_desc = "C++ algorithm.min()",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* +0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* QNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* SNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"},
+ },
+ },
+ {
+ .op = VFMIN,
+ .m6 = 4,
+ .m6_desc = "fmin()",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"},
+ {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"},
+ {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"},
+ {/* +0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"},
+ {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(a)", "Xi: T(a)"},
+ {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(a)"},
+ {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"},
+ {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(a)", "Xi: T(a)"},
+ },
+ },
+
+ {
+ .op = VFMAX,
+ .m6 = 0,
+ .m6_desc = "IEEE MaxNum",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"},
+ {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"},
+ {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"},
+ {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"},
+ {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(a)", "Xi: T(b*)"},
+ {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"},
+ {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"},
+ },
+ },
+ {
+ .op = VFMAX,
+ .m6 = 1,
+ .m6_desc = "JAVA Math.Max()",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"},
+ {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"},
+ {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"},
+ {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"},
+ {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(b)", "Xi: T(b*)"},
+ {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"},
+ {/* QNaN */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"},
+ {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"},
+ },
+ },
+ {
+ .op = VFMAX,
+ .m6 = 2,
+ .m6_desc = "C-style Max Macro",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"},
+ {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"},
+ {/* -0 */ "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"},
+ {/* +0 */ "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"},
+ {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "Xi: T(b)", "Xi: T(b)"},
+ {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b)", "Xi: T(b)"},
+ {/* QNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"},
+ {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"},
+ },
+ },
+ {
+ .op = VFMAX,
+ .m6 = 3,
+ .m6_desc = "C++ algorithm.max()",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"},
+ {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"},
+ {/* -0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"},
+ {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"},
+ {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "Xi: T(a)", "Xi: T(a)"},
+ {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* QNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"},
+ {/* SNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"},
+ },
+ },
+ {
+ .op = VFMAX,
+ .m6 = 4,
+ .m6_desc = "fmax()",
+ .table = {
+ /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */
+ {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"},
+ {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"},
+ {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"},
+ {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"},
+ {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(a)", "Xi: T(a)"},
+ {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"},
+ {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"},
+ {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(a)", "Xi: T(a)"},
+ },
+ },
+};
+
+static void dump_v(FILE *f, const void *v, size_t n)
+{
+ for (int i = 0; i < n; i++) {
+ fprintf(f, "%02x", ((const unsigned char *)v)[i]);
+ }
+}
+
+static int signed_test(struct signed_test *test, int m4, int m5,
+ const void *v1_exp, bool xi_exp,
+ const void *v2, const void *v3)
+{
+ size_t n = (m5 & 8) ? float_sizes[m4 - 2] : 16;
+ char v1[16];
+ bool xi;
+
+ feclearexcept(FE_ALL_EXCEPT);
+ vfminmax(test->op, m4, m5, test->m6, v1, v2, v3);
+ xi = fetestexcept(FE_ALL_EXCEPT) == FE_INVALID;
+
+ if (memcmp(v1, v1_exp, n) != 0 || xi != xi_exp) {
+ fprintf(stderr, "[ FAILED ] %s ", test->m6_desc);
+ dump_v(stderr, v2, n);
+ fprintf(stderr, ", ");
+ dump_v(stderr, v3, n);
+ fprintf(stderr, ", %d, %d, %d: actual=", m4, m5, test->m6);
+ dump_v(stderr, v1, n);
+ fprintf(stderr, "/%d, expected=", (int)xi);
+ dump_v(stderr, v1_exp, n);
+ fprintf(stderr, "/%d\n", (int)xi_exp);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void snan_to_qnan(char *v, int m4)
+{
+ size_t bit = 1 + e_bits[m4 - 2];
+ v[bit / 8] |= 1 << (7 - (bit % 8));
+}
+
+int main(void)
+{
+ int ret = 0;
+ size_t i;
+
+ for (i = 0; i < sizeof(signed_tests) / sizeof(signed_tests[0]); i++) {
+ struct signed_test *test = &signed_tests[i];
+ int m4;
+
+ for (m4 = 2; m4 <= 4; m4++) {
+ const unsigned char (*floats)[2][16] = signed_floats[m4 - 2];
+ size_t float_size = float_sizes[m4 - 2];
+ int m5;
+
+ for (m5 = 0; m5 <= 8; m5 += 8) {
+ char v1_exp[16], v2[16], v3[16];
+ bool xi_exp = false;
+ int pos = 0;
+ int i2;
+
+ for (i2 = 0; i2 < N_SIGNED_CLASSES * 2; i2++) {
+ int i3;
+
+ for (i3 = 0; i3 < N_SIGNED_CLASSES * 2; i3++) {
+ const char *spec = test->table[i2 / 2][i3 / 2];
+
+ memcpy(&v2[pos], floats[i2 / 2][i2 % 2], float_size);
+ memcpy(&v3[pos], floats[i3 / 2][i3 % 2], float_size);
+ if (strcmp(spec, "T(a)") == 0 ||
+ strcmp(spec, "Xi: T(a)") == 0) {
+ memcpy(&v1_exp[pos], &v2[pos], float_size);
+ } else if (strcmp(spec, "T(b)") == 0 ||
+ strcmp(spec, "Xi: T(b)") == 0) {
+ memcpy(&v1_exp[pos], &v3[pos], float_size);
+ } else if (strcmp(spec, "Xi: T(a*)") == 0) {
+ memcpy(&v1_exp[pos], &v2[pos], float_size);
+ snan_to_qnan(&v1_exp[pos], m4);
+ } else if (strcmp(spec, "Xi: T(b*)") == 0) {
+ memcpy(&v1_exp[pos], &v3[pos], float_size);
+ snan_to_qnan(&v1_exp[pos], m4);
+ } else if (strcmp(spec, "T(M(a,b))") == 0) {
+ /*
+ * Comparing floats is risky, since the compiler
+ * might generate the same instruction that we are
+ * testing. Compare ints instead. This works,
+ * because we get here only for +-Fn, and the
+ * corresponding test values have identical
+ * exponents.
+ */
+ int v2_int = *(int *)&v2[pos];
+ int v3_int = *(int *)&v3[pos];
+
+ if ((v2_int < v3_int) ==
+ ((test->op == VFMIN) != (v2_int < 0))) {
+ memcpy(&v1_exp[pos], &v2[pos], float_size);
+ } else {
+ memcpy(&v1_exp[pos], &v3[pos], float_size);
+ }
+ } else {
+ fprintf(stderr, "Unexpected spec: %s\n", spec);
+ return 1;
+ }
+ xi_exp |= spec[0] == 'X';
+ pos += float_size;
+
+ if ((m5 & 8) || pos == 16) {
+ ret |= signed_test(test, m4, m5,
+ v1_exp, xi_exp, v2, v3);
+ pos = 0;
+ xi_exp = false;
+ }
+ }
+ }
+
+ if (pos != 0) {
+ ret |= signed_test(test, m4, m5, v1_exp, xi_exp, v2, v3);
+ }
+ }
+ }
+ }
+
+ return ret;
+}