Test for predicated .new branches with non-standard predicate values
(non-all-0, non-all-1).  Hexagon predicates are 8 bits wide but conditional
branches evaluate only the LSB.

Reviewed-by: Taylor Simpson <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
 tests/tcg/hexagon/test_pnew_jump_loads.c | 341 +++++++++++++++++++++++
 tests/tcg/hexagon/Makefile.target        |   3 +
 2 files changed, 344 insertions(+)
 create mode 100644 tests/tcg/hexagon/test_pnew_jump_loads.c

diff --git a/tests/tcg/hexagon/test_pnew_jump_loads.c 
b/tests/tcg/hexagon/test_pnew_jump_loads.c
new file mode 100644
index 00000000000..06178bdac11
--- /dev/null
+++ b/tests/tcg/hexagon/test_pnew_jump_loads.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Exhaustive test for predicated .new branches with non-standard predicate
+ * values (non-all-0, non-all-1).
+ *
+ * Hexagon predicates are 8 bits wide but conditional branches evaluate only
+ * bit 0 (the LSB).  A predicate value like 0xFE is non-zero yet has bit 0
+ * clear, so it must evaluate as "false".
+ *
+ * This test covers the distinct TCG code paths for predicated .new ops:
+ *
+ *   1. gen_cond_jump      - J2_jumptnewpt / J2_jumpfnewpt  (p0..p3)
+ *   2. gen_cond_jumpr     - J2_jumprtnewpt / J2_jumprfnewpt
+ *   3. gen_cond_jumpr31   - SL2_jumpr31_tnew / SL2_jumpr31_fnew (duplex)
+ *   4. gen_testbit0_jumpnv - J4_tstbit0_t/f_jumpnv_t
+ *   5. Conditional .new loads and stores
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+int err;
+
+#include "hex_test.h"
+
+/*
+ * Non-standard predicate: non-zero (0xFE) but bit 0 clear => false.
+ * This distinguishes correct LSB evaluation from incorrect non-zero checks.
+ */
+#define PRED_VAL    0xFEu
+#define SENTINEL    0xDEADBEEFu
+#define LOAD_VAL    0xAAAABBBBu
+
+/* gen_cond_jump (J2_jumptnewpt) */
+
+/*
+ * Macro to test jumptnew across predicate registers p0..p3.
+ * { Pn = and(Pn, Pn); if (Pn.new) jump:t TARGET }
+ *
+ * Pn.new = PRED_VAL & PRED_VAL = 0xFE => bit0=0 => not taken.
+ * Different predicate registers produce different instruction encodings.
+ */
+#define TEST_JUMPTNEW(PREG)                                     \
+static void test_jumptnew_##PREG(void)                          \
+{                                                               \
+    uint32_t jumped;                                            \
+    asm(                                                        \
+        #PREG " = %[pred]\n"                                    \
+        "{ " #PREG " = and(" #PREG ", " #PREG ")\n"            \
+        "  if (" #PREG ".new) jump:t 1f }\n"                   \
+        "%[jumped] = #0\n"                                      \
+        "jump 2f\n"                                             \
+        "1:\n"                                                  \
+        "%[jumped] = #1\n"                                      \
+        "2:\n"                                                  \
+        : [jumped] "=r"(jumped)                                 \
+        : [pred] "r"(PRED_VAL)                                  \
+        : #PREG                                                 \
+    );                                                          \
+    check32(jumped, 0);                                         \
+}
+
+TEST_JUMPTNEW(p0)
+TEST_JUMPTNEW(p1)
+TEST_JUMPTNEW(p2)
+TEST_JUMPTNEW(p3)
+
+/* jumpfnew: bit0=0 => condition "false" => negated => jump IS taken */
+static void test_jumpfnew_p0(void)
+{
+    uint32_t jumped;
+
+    asm(
+        "p0 = %[pred]\n"
+        "{ p0 = and(p0, p0)\n"
+        "  if (!p0.new) jump:t 1f }\n"
+        "%[jumped] = #0\n"
+        "jump 2f\n"
+        "1:\n"
+        "%[jumped] = #1\n"
+        "2:\n"
+        : [jumped] "=r"(jumped)
+        : [pred] "r"(PRED_VAL)
+        : "p0"
+    );
+    check32(jumped, 1);
+}
+
+/* gen_cond_jumpr (J2_jumprtnewpt) */
+
+static void test_jumprtnew_p0(void)
+{
+    uint32_t jumped;
+
+    asm(
+        "p0 = %[pred]\n"
+        "r0 = ##1f\n"
+        "{ p0 = and(p0, p0)\n"
+        "  if (p0.new) jumpr:t r0 }\n"
+        "%[jumped] = #0\n"
+        "jump 2f\n"
+        "1:\n"
+        "%[jumped] = #1\n"
+        "2:\n"
+        : [jumped] "=r"(jumped)
+        : [pred] "r"(PRED_VAL)
+        : "p0", "r0"
+    );
+    check32(jumped, 0);
+}
+
+static void test_jumprfnew_p0(void)
+{
+    uint32_t jumped;
+
+    asm(
+        "p0 = %[pred]\n"
+        "r0 = ##1f\n"
+        "{ p0 = and(p0, p0)\n"
+        "  if (!p0.new) jumpr:t r0 }\n"
+        "%[jumped] = #0\n"
+        "jump 2f\n"
+        "1:\n"
+        "%[jumped] = #1\n"
+        "2:\n"
+        : [jumped] "=r"(jumped)
+        : [pred] "r"(PRED_VAL)
+        : "p0", "r0"
+    );
+    check32(jumped, 1);
+}
+
+/* gen_cond_jumpr31 (SL2_jumpr31_tnew) */
+
+/*
+ * Duplex sub-instructions: only SA1_cmpeqi and similar can produce .new
+ * predicates in a duplex packet, and those only yield 0x00/0xFF.
+ * We test with standard values to exercise the duplex decode path.
+ *
+ * { p0 = cmp.eq(r0, #0); if (p0.new) jumpr:nt r31 }
+ * With r0=0: p0.new = 0xFF => bit0=1 => taken.
+ */
+static void test_jumpr31_tnew(void)
+{
+    uint32_t jumped;
+
+    asm(
+        "r0 = #0\n"
+        "r31 = ##1f\n"
+        "{ p0 = cmp.eq(r0, #0)\n"
+        "  if (p0.new) jumpr:nt r31 }\n"
+        "%[jumped] = #0\n"
+        "jump 2f\n"
+        "1:\n"
+        "%[jumped] = #1\n"
+        "2:\n"
+        : [jumped] "=r"(jumped)
+        :
+        : "r0", "r31", "p0"
+    );
+    check32(jumped, 1);
+}
+
+/* p0.new = 0xFF => bit0=1 => !true => not taken */
+static void test_jumpr31_fnew(void)
+{
+    uint32_t jumped;
+
+    asm(
+        "r0 = #0\n"
+        "r31 = ##1f\n"
+        "{ p0 = cmp.eq(r0, #0)\n"
+        "  if (!p0.new) jumpr:nt r31 }\n"
+        "%[jumped] = #0\n"
+        "jump 2f\n"
+        "1:\n"
+        "%[jumped] = #1\n"
+        "2:\n"
+        : [jumped] "=r"(jumped)
+        :
+        : "r0", "r31", "p0"
+    );
+    check32(jumped, 0);
+}
+
+/* gen_testbit0_jumpnv (J4_tstbit0) */
+
+/*
+ * { r0 = #0xFE; if (tstbit(r0.new, #0)) jump:t TARGET }
+ * r0.new = 0xFE => bit0=0 => tstbit false => not taken.
+ */
+static void test_tstbit0_t_jumpnv(void)
+{
+    uint32_t jumped;
+
+    asm(
+        "{ r0 = #0xFE\n"
+        "  if (tstbit(r0.new, #0)) jump:t 1f }\n"
+        "%[jumped] = #0\n"
+        "jump 2f\n"
+        "1:\n"
+        "%[jumped] = #1\n"
+        "2:\n"
+        : [jumped] "=r"(jumped)
+        :
+        : "r0"
+    );
+    check32(jumped, 0);
+}
+
+/* bit0=0 => tstbit false => negated => taken */
+static void test_tstbit0_f_jumpnv(void)
+{
+    uint32_t jumped;
+
+    asm(
+        "{ r0 = #0xFE\n"
+        "  if (!tstbit(r0.new, #0)) jump:t 1f }\n"
+        "%[jumped] = #0\n"
+        "jump 2f\n"
+        "1:\n"
+        "%[jumped] = #1\n"
+        "2:\n"
+        : [jumped] "=r"(jumped)
+        :
+        : "r0"
+    );
+    check32(jumped, 1);
+}
+
+/* conditional .new loads and stores */
+
+static uint32_t load_val;
+static uint32_t store_dst;
+
+/* bit0=0 => condition false => load skipped => sentinel remains */
+static void test_cond_load_tnew(void)
+{
+    uint32_t result;
+
+    load_val = LOAD_VAL;
+    asm(
+        "p0 = %[pred]\n"
+        "%[res] = %[sentinel]\n"
+        "{ p0 = and(p0, p0)\n"
+        "  if (p0.new) %[res] = memw(%[addr]+#0) }\n"
+        : [res] "=&r"(result)
+        : [pred] "r"(PRED_VAL),
+          [addr] "r"(&load_val),
+          [sentinel] "r"(SENTINEL)
+        : "p0", "memory"
+    );
+    check32(result, SENTINEL);
+}
+
+/* bit0=0 => condition false => negated => load executed */
+static void test_cond_load_fnew(void)
+{
+    uint32_t result;
+
+    load_val = LOAD_VAL;
+    asm(
+        "p0 = %[pred]\n"
+        "%[res] = %[sentinel]\n"
+        "{ p0 = and(p0, p0)\n"
+        "  if (!p0.new) %[res] = memw(%[addr]+#0) }\n"
+        : [res] "=&r"(result)
+        : [pred] "r"(PRED_VAL),
+          [addr] "r"(&load_val),
+          [sentinel] "r"(SENTINEL)
+        : "p0", "memory"
+    );
+    check32(result, LOAD_VAL);
+}
+
+/* bit0=0 => condition false => store skipped => sentinel remains */
+static void test_cond_store_tnew(void)
+{
+    store_dst = SENTINEL;
+    asm(
+        "p0 = %[pred]\n"
+        "{ p0 = and(p0, p0)\n"
+        "  if (p0.new) memw(%[addr]+#0) = %[val] }\n"
+        :
+        : [pred] "r"(PRED_VAL),
+          [addr] "r"(&store_dst),
+          [val] "r"(LOAD_VAL)
+        : "p0", "memory"
+    );
+    check32(store_dst, SENTINEL);
+}
+
+/* bit0=0 => condition false => negated => store executed */
+static void test_cond_store_fnew(void)
+{
+    store_dst = SENTINEL;
+    asm(
+        "p0 = %[pred]\n"
+        "{ p0 = and(p0, p0)\n"
+        "  if (!p0.new) memw(%[addr]+#0) = %[val] }\n"
+        :
+        : [pred] "r"(PRED_VAL),
+          [addr] "r"(&store_dst),
+          [val] "r"(LOAD_VAL)
+        : "p0", "memory"
+    );
+    check32(store_dst, LOAD_VAL);
+}
+
+int main(void)
+{
+    /* gen_cond_jump with all predicate registers */
+    test_jumptnew_p0();
+    test_jumptnew_p1();
+    test_jumptnew_p2();
+    test_jumptnew_p3();
+    test_jumpfnew_p0();
+
+    /* gen_cond_jumpr */
+    test_jumprtnew_p0();
+    test_jumprfnew_p0();
+
+    /* gen_cond_jumpr31 (duplex, standard values) */
+    test_jumpr31_tnew();
+    test_jumpr31_fnew();
+
+    /* gen_testbit0_jumpnv */
+    test_tstbit0_t_jumpnv();
+    test_tstbit0_f_jumpnv();
+
+    /* conditional .new loads and stores */
+    test_cond_load_tnew();
+    test_cond_load_fnew();
+    test_cond_store_tnew();
+    test_cond_store_fnew();
+
+    puts(err ? "FAIL" : "PASS");
+    return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/Makefile.target 
b/tests/tcg/hexagon/Makefile.target
index a70ef2f6607..549c95082f6 100644
--- a/tests/tcg/hexagon/Makefile.target
+++ b/tests/tcg/hexagon/Makefile.target
@@ -81,6 +81,8 @@ HEX_TESTS += test_vminh
 HEX_TESTS += test_vpmpyh
 HEX_TESTS += test_vspliceb
 
+HEX_TESTS += test_pnew_jump_loads
+
 HEX_TESTS += v68_scalar
 HEX_TESTS += v68_hvx
 HEX_TESTS += v69_hvx
@@ -104,6 +106,7 @@ overflow: overflow.c hex_test.h
 preg_alias: preg_alias.c hex_test.h
 read_write_overlap: read_write_overlap.c hex_test.h
 reg_mut: reg_mut.c hex_test.h
+test_pnew_jump_loads: test_pnew_jump_loads.c hex_test.h
 unaligned_pc: unaligned_pc.c
 
 # This test has to be compiled for the -mv67t target
-- 
2.34.1

Reply via email to