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
+}