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