@samitouri / QOSamiQemu / commits / 1adf67b3c4

tests/tcg/hexagon: add test for predicated .new branch LSB evaluation

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 <ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

Brian Cain committed Mar 2, 2026 at 21:29 UTC 1adf67b3c445f4060900440ea210e7a1e03042f5
2 files changed +344
tests/tcg/hexagon/Makefile.target
+3
@@ -81,6 +81,8 @@ HEX_TESTS += test_vminh
81 HEX_TESTS += test_vpmpyh
82 HEX_TESTS += test_vspliceb
83
84 +HEX_TESTS += test_pnew_jump_loads
85 +
86 HEX_TESTS += v68_scalar
87 HEX_TESTS += v68_hvx
88 HEX_TESTS += v69_hvx
@@ -104,6 +106,7 @@ overflow: overflow.c hex_test.h
106 preg_alias: preg_alias.c hex_test.h
107 read_write_overlap: read_write_overlap.c hex_test.h
108 reg_mut: reg_mut.c hex_test.h
109 +test_pnew_jump_loads: test_pnew_jump_loads.c hex_test.h
110 unaligned_pc: unaligned_pc.c
111
112 # This test has to be compiled for the -mv67t target
tests/tcg/hexagon/test_pnew_jump_loads.c new
+341
@@ -0,0 +1,341 @@
1 +/*
2 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3 + * SPDX-License-Identifier: GPL-2.0-or-later
4 + *
5 + * Exhaustive test for predicated .new branches with non-standard predicate
6 + * values (non-all-0, non-all-1).
7 + *
8 + * Hexagon predicates are 8 bits wide but conditional branches evaluate only
9 + * bit 0 (the LSB). A predicate value like 0xFE is non-zero yet has bit 0
10 + * clear, so it must evaluate as "false".
11 + *
12 + * This test covers the distinct TCG code paths for predicated .new ops:
13 + *
14 + * 1. gen_cond_jump - J2_jumptnewpt / J2_jumpfnewpt (p0..p3)
15 + * 2. gen_cond_jumpr - J2_jumprtnewpt / J2_jumprfnewpt
16 + * 3. gen_cond_jumpr31 - SL2_jumpr31_tnew / SL2_jumpr31_fnew (duplex)
17 + * 4. gen_testbit0_jumpnv - J4_tstbit0_t/f_jumpnv_t
18 + * 5. Conditional .new loads and stores
19 + */
20 +
21 +#include <stdio.h>
22 +#include <stdint.h>
23 +
24 +int err;
25 +
26 +#include "hex_test.h"
27 +
28 +/*
29 + * Non-standard predicate: non-zero (0xFE) but bit 0 clear => false.
30 + * This distinguishes correct LSB evaluation from incorrect non-zero checks.
31 + */
32 +#define PRED_VAL 0xFEu
33 +#define SENTINEL 0xDEADBEEFu
34 +#define LOAD_VAL 0xAAAABBBBu
35 +
36 +/* gen_cond_jump (J2_jumptnewpt) */
37 +
38 +/*
39 + * Macro to test jumptnew across predicate registers p0..p3.
40 + * { Pn = and(Pn, Pn); if (Pn.new) jump:t TARGET }
41 + *
42 + * Pn.new = PRED_VAL & PRED_VAL = 0xFE => bit0=0 => not taken.
43 + * Different predicate registers produce different instruction encodings.
44 + */
45 +#define TEST_JUMPTNEW(PREG) \
46 +static void test_jumptnew_##PREG(void) \
47 +{ \
48 + uint32_t jumped; \
49 + asm( \
50 + #PREG " = %[pred]\n" \
51 + "{ " #PREG " = and(" #PREG ", " #PREG ")\n" \
52 + " if (" #PREG ".new) jump:t 1f }\n" \
53 + "%[jumped] = #0\n" \
54 + "jump 2f\n" \
55 + "1:\n" \
56 + "%[jumped] = #1\n" \
57 + "2:\n" \
58 + : [jumped] "=r"(jumped) \
59 + : [pred] "r"(PRED_VAL) \
60 + : #PREG \
61 + ); \
62 + check32(jumped, 0); \
63 +}
64 +
65 +TEST_JUMPTNEW(p0)
66 +TEST_JUMPTNEW(p1)
67 +TEST_JUMPTNEW(p2)
68 +TEST_JUMPTNEW(p3)
69 +
70 +/* jumpfnew: bit0=0 => condition "false" => negated => jump IS taken */
71 +static void test_jumpfnew_p0(void)
72 +{
73 + uint32_t jumped;
74 +
75 + asm(
76 + "p0 = %[pred]\n"
77 + "{ p0 = and(p0, p0)\n"
78 + " if (!p0.new) jump:t 1f }\n"
79 + "%[jumped] = #0\n"
80 + "jump 2f\n"
81 + "1:\n"
82 + "%[jumped] = #1\n"
83 + "2:\n"
84 + : [jumped] "=r"(jumped)
85 + : [pred] "r"(PRED_VAL)
86 + : "p0"
87 + );
88 + check32(jumped, 1);
89 +}
90 +
91 +/* gen_cond_jumpr (J2_jumprtnewpt) */
92 +
93 +static void test_jumprtnew_p0(void)
94 +{
95 + uint32_t jumped;
96 +
97 + asm(
98 + "p0 = %[pred]\n"
99 + "r0 = ##1f\n"
100 + "{ p0 = and(p0, p0)\n"
101 + " if (p0.new) jumpr:t r0 }\n"
102 + "%[jumped] = #0\n"
103 + "jump 2f\n"
104 + "1:\n"
105 + "%[jumped] = #1\n"
106 + "2:\n"
107 + : [jumped] "=r"(jumped)
108 + : [pred] "r"(PRED_VAL)
109 + : "p0", "r0"
110 + );
111 + check32(jumped, 0);
112 +}
113 +
114 +static void test_jumprfnew_p0(void)
115 +{
116 + uint32_t jumped;
117 +
118 + asm(
119 + "p0 = %[pred]\n"
120 + "r0 = ##1f\n"
121 + "{ p0 = and(p0, p0)\n"
122 + " if (!p0.new) jumpr:t r0 }\n"
123 + "%[jumped] = #0\n"
124 + "jump 2f\n"
125 + "1:\n"
126 + "%[jumped] = #1\n"
127 + "2:\n"
128 + : [jumped] "=r"(jumped)
129 + : [pred] "r"(PRED_VAL)
130 + : "p0", "r0"
131 + );
132 + check32(jumped, 1);
133 +}
134 +
135 +/* gen_cond_jumpr31 (SL2_jumpr31_tnew) */
136 +
137 +/*
138 + * Duplex sub-instructions: only SA1_cmpeqi and similar can produce .new
139 + * predicates in a duplex packet, and those only yield 0x00/0xFF.
140 + * We test with standard values to exercise the duplex decode path.
141 + *
142 + * { p0 = cmp.eq(r0, #0); if (p0.new) jumpr:nt r31 }
143 + * With r0=0: p0.new = 0xFF => bit0=1 => taken.
144 + */
145 +static void test_jumpr31_tnew(void)
146 +{
147 + uint32_t jumped;
148 +
149 + asm(
150 + "r0 = #0\n"
151 + "r31 = ##1f\n"
152 + "{ p0 = cmp.eq(r0, #0)\n"
153 + " if (p0.new) jumpr:nt r31 }\n"
154 + "%[jumped] = #0\n"
155 + "jump 2f\n"
156 + "1:\n"
157 + "%[jumped] = #1\n"
158 + "2:\n"
159 + : [jumped] "=r"(jumped)
160 + :
161 + : "r0", "r31", "p0"
162 + );
163 + check32(jumped, 1);
164 +}
165 +
166 +/* p0.new = 0xFF => bit0=1 => !true => not taken */
167 +static void test_jumpr31_fnew(void)
168 +{
169 + uint32_t jumped;
170 +
171 + asm(
172 + "r0 = #0\n"
173 + "r31 = ##1f\n"
174 + "{ p0 = cmp.eq(r0, #0)\n"
175 + " if (!p0.new) jumpr:nt r31 }\n"
176 + "%[jumped] = #0\n"
177 + "jump 2f\n"
178 + "1:\n"
179 + "%[jumped] = #1\n"
180 + "2:\n"
181 + : [jumped] "=r"(jumped)
182 + :
183 + : "r0", "r31", "p0"
184 + );
185 + check32(jumped, 0);
186 +}
187 +
188 +/* gen_testbit0_jumpnv (J4_tstbit0) */
189 +
190 +/*
191 + * { r0 = #0xFE; if (tstbit(r0.new, #0)) jump:t TARGET }
192 + * r0.new = 0xFE => bit0=0 => tstbit false => not taken.
193 + */
194 +static void test_tstbit0_t_jumpnv(void)
195 +{
196 + uint32_t jumped;
197 +
198 + asm(
199 + "{ r0 = #0xFE\n"
200 + " if (tstbit(r0.new, #0)) jump:t 1f }\n"
201 + "%[jumped] = #0\n"
202 + "jump 2f\n"
203 + "1:\n"
204 + "%[jumped] = #1\n"
205 + "2:\n"
206 + : [jumped] "=r"(jumped)
207 + :
208 + : "r0"
209 + );
210 + check32(jumped, 0);
211 +}
212 +
213 +/* bit0=0 => tstbit false => negated => taken */
214 +static void test_tstbit0_f_jumpnv(void)
215 +{
216 + uint32_t jumped;
217 +
218 + asm(
219 + "{ r0 = #0xFE\n"
220 + " if (!tstbit(r0.new, #0)) jump:t 1f }\n"
221 + "%[jumped] = #0\n"
222 + "jump 2f\n"
223 + "1:\n"
224 + "%[jumped] = #1\n"
225 + "2:\n"
226 + : [jumped] "=r"(jumped)
227 + :
228 + : "r0"
229 + );
230 + check32(jumped, 1);
231 +}
232 +
233 +/* conditional .new loads and stores */
234 +
235 +static uint32_t load_val;
236 +static uint32_t store_dst;
237 +
238 +/* bit0=0 => condition false => load skipped => sentinel remains */
239 +static void test_cond_load_tnew(void)
240 +{
241 + uint32_t result;
242 +
243 + load_val = LOAD_VAL;
244 + asm(
245 + "p0 = %[pred]\n"
246 + "%[res] = %[sentinel]\n"
247 + "{ p0 = and(p0, p0)\n"
248 + " if (p0.new) %[res] = memw(%[addr]+#0) }\n"
249 + : [res] "=&r"(result)
250 + : [pred] "r"(PRED_VAL),
251 + [addr] "r"(&load_val),
252 + [sentinel] "r"(SENTINEL)
253 + : "p0", "memory"
254 + );
255 + check32(result, SENTINEL);
256 +}
257 +
258 +/* bit0=0 => condition false => negated => load executed */
259 +static void test_cond_load_fnew(void)
260 +{
261 + uint32_t result;
262 +
263 + load_val = LOAD_VAL;
264 + asm(
265 + "p0 = %[pred]\n"
266 + "%[res] = %[sentinel]\n"
267 + "{ p0 = and(p0, p0)\n"
268 + " if (!p0.new) %[res] = memw(%[addr]+#0) }\n"
269 + : [res] "=&r"(result)
270 + : [pred] "r"(PRED_VAL),
271 + [addr] "r"(&load_val),
272 + [sentinel] "r"(SENTINEL)
273 + : "p0", "memory"
274 + );
275 + check32(result, LOAD_VAL);
276 +}
277 +
278 +/* bit0=0 => condition false => store skipped => sentinel remains */
279 +static void test_cond_store_tnew(void)
280 +{
281 + store_dst = SENTINEL;
282 + asm(
283 + "p0 = %[pred]\n"
284 + "{ p0 = and(p0, p0)\n"
285 + " if (p0.new) memw(%[addr]+#0) = %[val] }\n"
286 + :
287 + : [pred] "r"(PRED_VAL),
288 + [addr] "r"(&store_dst),
289 + [val] "r"(LOAD_VAL)
290 + : "p0", "memory"
291 + );
292 + check32(store_dst, SENTINEL);
293 +}
294 +
295 +/* bit0=0 => condition false => negated => store executed */
296 +static void test_cond_store_fnew(void)
297 +{
298 + store_dst = SENTINEL;
299 + asm(
300 + "p0 = %[pred]\n"
301 + "{ p0 = and(p0, p0)\n"
302 + " if (!p0.new) memw(%[addr]+#0) = %[val] }\n"
303 + :
304 + : [pred] "r"(PRED_VAL),
305 + [addr] "r"(&store_dst),
306 + [val] "r"(LOAD_VAL)
307 + : "p0", "memory"
308 + );
309 + check32(store_dst, LOAD_VAL);
310 +}
311 +
312 +int main(void)
313 +{
314 + /* gen_cond_jump with all predicate registers */
315 + test_jumptnew_p0();
316 + test_jumptnew_p1();
317 + test_jumptnew_p2();
318 + test_jumptnew_p3();
319 + test_jumpfnew_p0();
320 +
321 + /* gen_cond_jumpr */
322 + test_jumprtnew_p0();
323 + test_jumprfnew_p0();
324 +
325 + /* gen_cond_jumpr31 (duplex, standard values) */
326 + test_jumpr31_tnew();
327 + test_jumpr31_fnew();
328 +
329 + /* gen_testbit0_jumpnv */
330 + test_tstbit0_t_jumpnv();
331 + test_tstbit0_f_jumpnv();
332 +
333 + /* conditional .new loads and stores */
334 + test_cond_load_tnew();
335 + test_cond_load_fnew();
336 + test_cond_store_tnew();
337 + test_cond_store_fnew();
338 +
339 + puts(err ? "FAIL" : "PASS");
340 + return err ? 1 : 0;
341 +}