Test conditional callr, compound tstbit0-and-jump instructions and
endloop01.  These exercise previously uncovered branch paths in translate.c.

Reviewed-by: Pierrick Bouvier <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
 tests/tcg/hexagon/test_cond_branch.c | 162 +++++++++++++++++++++++++++
 tests/tcg/hexagon/meson.build        |   1 +
 2 files changed, 163 insertions(+)
 create mode 100644 tests/tcg/hexagon/test_cond_branch.c

diff --git a/tests/tcg/hexagon/test_cond_branch.c 
b/tests/tcg/hexagon/test_cond_branch.c
new file mode 100644
index 00000000000..73be8fbb43c
--- /dev/null
+++ b/tests/tcg/hexagon/test_cond_branch.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Test conditional calls, compound testbit0-and-jump instructions,
+ * and endloop01 (dual hardware loop end).
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+int err;
+
+#include "hex_test.h"
+
+/* Target function for conditional calls */
+static int call_target_val;
+
+__attribute__((noinline))
+static void call_target(void)
+{
+    call_target_val = 42;
+}
+
+/*
+ * Test conditional call: if (Pu) callr Rs / if (!Pu) callr Rs
+ */
+static void check_cond_call(void)
+{
+    void (*fn)(void) = call_target;
+
+    /*
+     * callr clobbers all caller-saved registers.
+     * Hexagon caller-saved: r0-r5, r14-r15, r28, p0-p3.
+     */
+
+    /* if (p0) callr Rs -- predicate true */
+    call_target_val = 0;
+    asm volatile("p0 = cmp.eq(%[one], %[one])\n\t"  /* p0 = true */
+                 "if (p0) callr %[fn]\n\t"
+                 :
+                 : [one] "r"(1), [fn] "r"(fn)
+                 : "r0", "r1", "r2", "r3", "r4", "r5",
+                   "r14", "r15", "r28", "r31",
+                   "p0", "p1", "p2", "p3", "memory");
+    check32(call_target_val, 42);
+
+    /* if (!p0) callr Rs -- predicate true, so !p0 is false */
+    call_target_val = 0;
+    asm volatile("p0 = cmp.eq(%[one], %[one])\n\t"  /* p0 = true */
+                 "if (!p0) callr %[fn]\n\t"
+                 :
+                 : [one] "r"(1), [fn] "r"(fn)
+                 : "r0", "r1", "r2", "r3", "r4", "r5",
+                   "r14", "r15", "r28", "r31",
+                   "p0", "p1", "p2", "p3", "memory");
+    check32(call_target_val, 0);
+
+    /* if (!p0) callr Rs -- predicate false, so !p0 is true */
+    call_target_val = 0;
+    asm volatile("p0 = cmp.eq(%[a], %[b])\n\t"  /* p0 = false (1 != 2) */
+                 "if (!p0) callr %[fn]\n\t"
+                 :
+                 : [a] "r"(1), [b] "r"(2), [fn] "r"(fn)
+                 : "r0", "r1", "r2", "r3", "r4", "r5",
+                   "r14", "r15", "r28", "r31",
+                   "p0", "p1", "p2", "p3", "memory");
+    check32(call_target_val, 42);
+}
+
+/*
+ * Test compound testbit0-and-jump.
+ * p0 = tstbit(Rs, #0); if ([!]p0.new) jump:t/nt target
+ */
+static void check_tstbit0_jump(void)
+{
+    uint32_t result;
+
+    /* Rs has bit0 = 1, test "if (p0.new) jump" -> should jump */
+    result = 0;
+    asm volatile(
+        "{\n\t"
+        "    p0 = tstbit(%[val], #0)\n\t"
+        "    if (p0.new) jump:t 1f\n\t"
+        "}\n\t"
+        "%[res] = #0\n\t"
+        "jump 2f\n\t"
+        "1:\n\t"
+        "%[res] = #1\n\t"
+        "2:\n\t"
+        : [res] "=r"(result)
+        : [val] "r"(0x5)  /* bit 0 is set */
+        : "p0");
+    check32(result, 1);
+
+    /* Rs has bit0 = 0, test "if (p0.new) jump" -> should NOT jump */
+    result = 0;
+    asm volatile(
+        "{\n\t"
+        "    p0 = tstbit(%[val], #0)\n\t"
+        "    if (p0.new) jump:t 1f\n\t"
+        "}\n\t"
+        "%[res] = #0\n\t"
+        "jump 2f\n\t"
+        "1:\n\t"
+        "%[res] = #1\n\t"
+        "2:\n\t"
+        : [res] "=r"(result)
+        : [val] "r"(0x4)  /* bit 0 is clear */
+        : "p0");
+    check32(result, 0);
+
+    /* Rs has bit0 = 0, test "if (!p0.new) jump" -> should jump */
+    result = 0;
+    asm volatile(
+        "{\n\t"
+        "    p0 = tstbit(%[val], #0)\n\t"
+        "    if (!p0.new) jump:t 1f\n\t"
+        "}\n\t"
+        "%[res] = #0\n\t"
+        "jump 2f\n\t"
+        "1:\n\t"
+        "%[res] = #1\n\t"
+        "2:\n\t"
+        : [res] "=r"(result)
+        : [val] "r"(0x4)  /* bit 0 is clear */
+        : "p0");
+    check32(result, 1);
+}
+
+/*
+ * Test endloop01: a packet that terminates both loop0 and loop1.
+ * Outer loop runs 3 iterations, inner loop runs 4.
+ */
+static void check_endloop01(void)
+{
+    uint32_t count;
+
+    asm volatile(
+        "%[cnt] = #0\n\t"
+        "loop1(1f, #3)\n\t"    /* outer loop: 3 iterations */
+        "1:\n\t"
+        "loop0(2f, #4)\n\t"    /* inner loop: 4 iterations */
+        "2:\n\t"
+        "{ %[cnt] = add(%[cnt], #1) }:endloop01\n\t"
+        : [cnt] "=r"(count));
+
+    /* 3 outer * 4 inner = 12 total iterations */
+    check32(count, 12);
+}
+
+int main(void)
+{
+    check_cond_call();
+    check_tstbit0_jump();
+    check_endloop01();
+
+    puts(err ? "FAIL" : "PASS");
+    return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
index 6935d02802b..6074e56f6d6 100644
--- a/tests/tcg/hexagon/meson.build
+++ b/tests/tcg/hexagon/meson.build
@@ -73,6 +73,7 @@ tests += {
   'test_call.S': {'cflags': asmflags},
   'test_clobber.S': {'cflags': asmflags},
   'test_cmp.S': {'cflags': asmflags},
+  'test_cond_branch.c': {'cflags': cflags},
   'test_dotnew.S': {'cflags': asmflags},
   'test_ext.S': {'cflags': asmflags},
   'test_fibonacci.S': {'cflags': asmflags},
-- 
2.34.1

Reply via email to