| 1 | /* |
| 2 | * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. |
| 3 | * |
| 4 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 5 | */ |
| 6 | |
| 7 | #include <stdio.h> |
| 8 | #include <stdint.h> |
| 9 | #include <stdbool.h> |
| 10 | #include <string.h> |
| 11 | #include <assert.h> |
| 12 | #include <hexagon_types.h> |
| 13 | #include <hvx_hexagon_protos.h> |
| 14 | |
| 15 | #if __HEXAGON_ARCH__ > 75 |
| 16 | #error "After v75, compiler will replace some FP HVX instructions." |
| 17 | #endif |
| 18 | |
| 19 | int err; |
| 20 | #include "hvx_misc.h" |
| 21 | #include "hex_test.h" |
| 22 | |
| 23 | #define MAX_TESTS_hf (MAX_VEC_SIZE_BYTES / 2) |
| 24 | #define MAX_TESTS_sf (MAX_VEC_SIZE_BYTES / 4) |
| 25 | #define MAX_TESTS_bf (MAX_VEC_SIZE_BYTES / 2) |
| 26 | |
| 27 | #define TRUE_MASK_sf 0xffffffff |
| 28 | #define TRUE_MASK_hf 0xffff |
| 29 | #define TRUE_MASK_bf 0xffff |
| 30 | |
| 31 | static const char *comparisons[MAX_TESTS_sf][2]; |
| 32 | static HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; |
| 33 | static HVX_Vector buffers[2], true_vec, false_vec; |
| 34 | static int exp_index; |
| 35 | |
| 36 | #define ADD_TEST_CMP(TYPE, VAL1, VAL2, EXP) do { \ |
| 37 | ((MMVector *)&buffers[0])->TYPE[exp_index] = VAL1; \ |
| 38 | ((MMVector *)&buffers[1])->TYPE[exp_index] = VAL2; \ |
| 39 | expect[0].TYPE[exp_index] = EXP ? TRUE_MASK_##TYPE : 0; \ |
| 40 | comparisons[exp_index][0] = #VAL1; \ |
| 41 | comparisons[exp_index][1] = #VAL2; \ |
| 42 | assert(exp_index < MAX_TESTS_##TYPE); \ |
| 43 | exp_index++; \ |
| 44 | } while (0) |
| 45 | |
| 46 | #define TEST_CMP_GT(TYPE, VAL1, VAL2) do { \ |
| 47 | ADD_TEST_CMP(TYPE, VAL1, VAL2, true); \ |
| 48 | ADD_TEST_CMP(TYPE, VAL2, VAL1, false); \ |
| 49 | } while (0) |
| 50 | |
| 51 | #define PREP_TEST() do { \ |
| 52 | memset(&buffers, 0, sizeof(buffers)); \ |
| 53 | memset(expect, 0, sizeof(expect)); \ |
| 54 | exp_index = 0; \ |
| 55 | } while (0) |
| 56 | |
| 57 | #define CHECK(TYPE, TYPESZ) do { \ |
| 58 | HVX_VectorPred pred = Q6_Q_vcmp_gt_V##TYPE##V##TYPE(buffers[0], buffers[1]); \ |
| 59 | *hvx_output = Q6_V_vmux_QVV(pred, true_vec, false_vec); \ |
| 60 | for (int j = 0; j < MAX_VEC_SIZE_BYTES / TYPESZ; j++) { \ |
| 61 | if (output[0].TYPE[j] != expect[0].TYPE[j]) { \ |
| 62 | printf("ERROR: expected %s %s %s\n", comparisons[j][0], \ |
| 63 | (expect[0].TYPE[j] != 0 ? ">" : "<="), comparisons[j][1]); \ |
| 64 | err++; \ |
| 65 | } \ |
| 66 | } \ |
| 67 | } while (0) |
| 68 | |
| 69 | static void test_cmp_sf(void) |
| 70 | { |
| 71 | /* |
| 72 | * General ordering for sf: |
| 73 | * QNaN > SNaN > +Inf > numbers > -Inf > SNaN_neg > QNaN_neg |
| 74 | */ |
| 75 | |
| 76 | /* Test equality */ |
| 77 | PREP_TEST(); |
| 78 | ADD_TEST_CMP(sf, raw_sf(2.2), raw_sf(2.2), false); |
| 79 | ADD_TEST_CMP(sf, SF_SNaN, SF_SNaN, false); |
| 80 | CHECK(sf, 4); |
| 81 | |
| 82 | /* Common numbers */ |
| 83 | PREP_TEST(); |
| 84 | TEST_CMP_GT(sf, raw_sf(2.2), raw_sf(2.1)); |
| 85 | TEST_CMP_GT(sf, raw_sf(0), raw_sf(-2.2)); |
| 86 | CHECK(sf, 4); |
| 87 | |
| 88 | /* Infinity vs Infinity/NaN */ |
| 89 | PREP_TEST(); |
| 90 | TEST_CMP_GT(sf, SF_QNaN, SF_INF); |
| 91 | TEST_CMP_GT(sf, SF_SNaN, SF_INF); |
| 92 | TEST_CMP_GT(sf, SF_INF, SF_INF_neg); |
| 93 | TEST_CMP_GT(sf, SF_INF, SF_SNaN_neg); |
| 94 | TEST_CMP_GT(sf, SF_INF, SF_QNaN_neg); |
| 95 | TEST_CMP_GT(sf, SF_INF_neg, SF_SNaN_neg); |
| 96 | TEST_CMP_GT(sf, SF_INF_neg, SF_QNaN_neg); |
| 97 | TEST_CMP_GT(sf, SF_SNaN, SF_INF_neg); |
| 98 | TEST_CMP_GT(sf, SF_QNaN, SF_INF_neg); |
| 99 | CHECK(sf, 4); |
| 100 | |
| 101 | /* NaN vs NaN */ |
| 102 | PREP_TEST(); |
| 103 | TEST_CMP_GT(sf, SF_QNaN, SF_SNaN); |
| 104 | TEST_CMP_GT(sf, SF_SNaN, SF_SNaN_neg); |
| 105 | TEST_CMP_GT(sf, SF_SNaN_neg, SF_QNaN_neg); |
| 106 | CHECK(sf, 4); |
| 107 | |
| 108 | /* NaN vs non-NaN */ |
| 109 | PREP_TEST(); |
| 110 | TEST_CMP_GT(sf, SF_QNaN, SF_one); |
| 111 | TEST_CMP_GT(sf, SF_SNaN, SF_one); |
| 112 | TEST_CMP_GT(sf, SF_one, SF_QNaN_neg); |
| 113 | TEST_CMP_GT(sf, SF_one, SF_SNaN_neg); |
| 114 | CHECK(sf, 4); |
| 115 | } |
| 116 | |
| 117 | static void test_cmp_hf(void) |
| 118 | { |
| 119 | /* |
| 120 | * General ordering for hf: |
| 121 | * QNaN > SNaN > +Inf > numbers > -Inf > QSNaN_neg > QNaN_neg |
| 122 | */ |
| 123 | |
| 124 | /* Test equality */ |
| 125 | PREP_TEST(); |
| 126 | ADD_TEST_CMP(hf, raw_hf((_Float16)2.2), raw_hf((_Float16)2.2), false); |
| 127 | ADD_TEST_CMP(hf, HF_SNaN, HF_SNaN, false); |
| 128 | CHECK(hf, 2); |
| 129 | |
| 130 | /* Common numbers */ |
| 131 | PREP_TEST(); |
| 132 | TEST_CMP_GT(hf, raw_hf((_Float16)2.2), raw_hf((_Float16)2.1)); |
| 133 | TEST_CMP_GT(hf, raw_hf((_Float16)0), raw_hf((_Float16) - 2.2)); |
| 134 | CHECK(hf, 2); |
| 135 | |
| 136 | /* Infinity vs Infinity/NaN */ |
| 137 | PREP_TEST(); |
| 138 | TEST_CMP_GT(hf, HF_QNaN, HF_INF); |
| 139 | TEST_CMP_GT(hf, HF_SNaN, HF_INF); |
| 140 | TEST_CMP_GT(hf, HF_INF, HF_INF_neg); |
| 141 | TEST_CMP_GT(hf, HF_INF, HF_SNaN_neg); |
| 142 | TEST_CMP_GT(hf, HF_INF, HF_QNaN_neg); |
| 143 | TEST_CMP_GT(hf, HF_INF_neg, HF_SNaN_neg); |
| 144 | TEST_CMP_GT(hf, HF_INF_neg, HF_QNaN_neg); |
| 145 | TEST_CMP_GT(hf, HF_SNaN, HF_INF_neg); |
| 146 | TEST_CMP_GT(hf, HF_QNaN, HF_INF_neg); |
| 147 | CHECK(hf, 2); |
| 148 | |
| 149 | /* NaN vs NaN */ |
| 150 | PREP_TEST(); |
| 151 | TEST_CMP_GT(hf, HF_QNaN, HF_SNaN); |
| 152 | TEST_CMP_GT(hf, HF_SNaN, HF_SNaN_neg); |
| 153 | TEST_CMP_GT(hf, HF_SNaN_neg, HF_QNaN_neg); |
| 154 | CHECK(hf, 2); |
| 155 | |
| 156 | /* NaN vs non-NaN */ |
| 157 | PREP_TEST(); |
| 158 | TEST_CMP_GT(hf, HF_QNaN, HF_one); |
| 159 | TEST_CMP_GT(hf, HF_SNaN, HF_one); |
| 160 | TEST_CMP_GT(hf, HF_one, HF_QNaN_neg); |
| 161 | TEST_CMP_GT(hf, HF_one, HF_SNaN_neg); |
| 162 | CHECK(hf, 2); |
| 163 | } |
| 164 | |
| 165 | static void test_cmp_bf(void) |
| 166 | { |
| 167 | /* |
| 168 | * General ordering for bf: |
| 169 | * QNaN > SNaN > +Inf > numbers > -Inf > SNaN_neg > QNaN_neg |
| 170 | */ |
| 171 | |
| 172 | /* Test equality */ |
| 173 | PREP_TEST(); |
| 174 | ADD_TEST_CMP(bf, 0, 0, false); |
| 175 | ADD_TEST_CMP(bf, BF_SNaN, BF_SNaN, false); |
| 176 | CHECK(bf, 2); |
| 177 | |
| 178 | /* Common numbers */ |
| 179 | PREP_TEST(); |
| 180 | TEST_CMP_GT(bf, BF_two, BF_one); |
| 181 | TEST_CMP_GT(bf, BF_one, BF_zero); |
| 182 | CHECK(bf, 2); |
| 183 | |
| 184 | /* Infinity vs Infinity/NaN */ |
| 185 | PREP_TEST(); |
| 186 | TEST_CMP_GT(bf, BF_QNaN, BF_INF); |
| 187 | TEST_CMP_GT(bf, BF_SNaN, BF_INF); |
| 188 | TEST_CMP_GT(bf, BF_INF, BF_INF_neg); |
| 189 | TEST_CMP_GT(bf, BF_INF, BF_SNaN_neg); |
| 190 | TEST_CMP_GT(bf, BF_INF, BF_QNaN_neg); |
| 191 | TEST_CMP_GT(bf, BF_INF_neg, BF_SNaN_neg); |
| 192 | TEST_CMP_GT(bf, BF_INF_neg, BF_QNaN_neg); |
| 193 | TEST_CMP_GT(bf, BF_SNaN, BF_INF_neg); |
| 194 | TEST_CMP_GT(bf, BF_QNaN, BF_INF_neg); |
| 195 | CHECK(bf, 2); |
| 196 | |
| 197 | /* NaN vs NaN */ |
| 198 | PREP_TEST(); |
| 199 | TEST_CMP_GT(bf, BF_QNaN, BF_SNaN); |
| 200 | TEST_CMP_GT(bf, BF_SNaN, BF_SNaN_neg); |
| 201 | TEST_CMP_GT(bf, BF_SNaN_neg, BF_QNaN_neg); |
| 202 | CHECK(bf, 2); |
| 203 | |
| 204 | /* NaN vs non-NaN */ |
| 205 | PREP_TEST(); |
| 206 | TEST_CMP_GT(bf, BF_QNaN, BF_one); |
| 207 | TEST_CMP_GT(bf, BF_SNaN, BF_one); |
| 208 | TEST_CMP_GT(bf, BF_one, BF_QNaN_neg); |
| 209 | TEST_CMP_GT(bf, BF_one, BF_SNaN_neg); |
| 210 | CHECK(bf, 2); |
| 211 | } |
| 212 | |
| 213 | static void check_byte_pred(HVX_VectorPred pred, int byte_idx, uint8_t exp_mask, |
| 214 | int line) |
| 215 | { |
| 216 | /* |
| 217 | * Note: ((uint8_t *)&pred)[N] returns the expanded value of bit N: |
| 218 | * 0xFF if bit is set, 0x00 if clear. |
| 219 | */ |
| 220 | for (int i = 0; i < 8; i++) { |
| 221 | int idx = byte_idx * 8 + i; |
| 222 | int val = ((uint8_t *)&pred)[idx]; |
| 223 | int exp = (exp_mask >> i) & 1 ? 0xff : 0x00; |
| 224 | if (exp != val) { |
| 225 | printf("ERROR line %d: pred bit %d is 0x%x, should be 0x%x\n", |
| 226 | line, idx, val, exp); |
| 227 | err++; |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | #define CHECK_BYTE_PRED(PRED, BYTE, EXP) check_byte_pred(PRED, BYTE, EXP, __LINE__) |
| 233 | |
| 234 | static void test_cmp_variants(void) |
| 235 | { |
| 236 | HVX_VectorPred pred; |
| 237 | |
| 238 | /* |
| 239 | * Setup: comparison result will have bits 4-7 set (0xF0 in pred byte 0) |
| 240 | * - sf[0]: SF_zero > SF_one = false -> bits 0-3 = 0 |
| 241 | * - sf[1]: SF_one > SF_zero = true -> bits 4-7 = 1 |
| 242 | */ |
| 243 | PREP_TEST(); |
| 244 | ADD_TEST_CMP(sf, SF_zero, SF_one, false); |
| 245 | ADD_TEST_CMP(sf, SF_one, SF_zero, true); |
| 246 | |
| 247 | /* greater and: 0xF0 & 0xF0 = 0xF0 */ |
| 248 | memset(&pred, 0xF0, sizeof(pred)); |
| 249 | pred = Q6_Q_vcmp_gtand_QVsfVsf(pred, buffers[0], buffers[1]); |
| 250 | CHECK_BYTE_PRED(pred, 0, 0xF0); |
| 251 | |
| 252 | /* greater or: 0x0F | 0xF0 = 0xFF */ |
| 253 | memset(&pred, 0x0F, sizeof(pred)); |
| 254 | pred = Q6_Q_vcmp_gtor_QVsfVsf(pred, buffers[0], buffers[1]); |
| 255 | CHECK_BYTE_PRED(pred, 0, 0xFF); |
| 256 | |
| 257 | /* greater xor: 0xFF ^ 0xF0 = 0x0F */ |
| 258 | memset(&pred, 0xFF, sizeof(pred)); |
| 259 | pred = Q6_Q_vcmp_gtxacc_QVsfVsf(pred, buffers[0], buffers[1]); |
| 260 | CHECK_BYTE_PRED(pred, 0, 0x0F); |
| 261 | } |
| 262 | |
| 263 | int main(void) |
| 264 | { |
| 265 | memset(&true_vec, 0xff, sizeof(true_vec)); |
| 266 | memset(&false_vec, 0, sizeof(false_vec)); |
| 267 | |
| 268 | test_cmp_sf(); |
| 269 | test_cmp_hf(); |
| 270 | test_cmp_bf(); |
| 271 | test_cmp_variants(); |
| 272 | |
| 273 | puts(err ? "FAIL" : "PASS"); |
| 274 | return err ? 1 : 0; |
| 275 | } |