| This directory contains executable tests for ARM/AArch64 Advanced SIMD |
| (Neon) intrinsics. |
| |
| It is meant to cover execution cases of all the Advanced SIMD |
| intrinsics, but does not scan the generated assembler code. |
| |
| The general framework is composed as follows: |
| - advsimd-intrinsics.exp: main dejagnu driver |
| - *.c: actual tests, generally one per intrinsinc family |
| - arm-neon-ref.h: contains macro definitions to save typing in actual |
| test files |
| - compute-ref-data.h: contains input vectors definitions |
| - *.inc: generic tests, shared by several families of intrinsics. For |
| instance, unary or binary operators |
| |
| A typical .c test file starts with the following contents (look at |
| vld1.c and vaba.c for sample cases): |
| #include <arm_neon.h> |
| #include "arm-neon-ref.h" |
| #include "compute-ref-data.h" |
| |
| Then, definitions of expected results, based on common input values, |
| as defined in compute-ref-data.h. |
| For example: |
| VECT_VAR_DECL(expected,int,16,4) [] = { 0x16, 0x17, 0x18, 0x19 }; |
| defines the expected results of an operator generating int16x4 values. |
| |
| The common input values defined in compute-ref-data.h have been chosen |
| to avoid corner-case values for most operators, yet exposing negative |
| values for signed operators. For this reason, their range is also |
| limited. For instance, the initialization of buffer_int16x4 will be |
| { -16, -15, -14, -13 }. |
| |
| The initialization of floating-point values is done via hex notation, |
| to avoid potential rounding problems. |
| |
| To test special values and corner cases, specific initialization |
| values should be used in dedicated tests, to ensure proper coverage. |
| An example of this is vshl. |
| |
| When a variant of an intrinsic is not available, its expected result |
| should be defined to the value of CLEAN_PATTERN_8 as defined in |
| arm-neon-ref.h. For example: |
| VECT_VAR_DECL(expected,int,64,1) [] = { 0x3333333333333333 }; |
| if the given intrinsic has no variant producing an int64x1 result, |
| like the vcmp family (eg. vclt). |
| |
| This is because the helper function (check_results(), defined in |
| arm-neon-ref.h), iterates over all the possible variants, to save |
| typing in each individual test file. Alternatively, one can directly |
| call the CHECK/CHECK_FP macros to check only a few expected results |
| (see vabs.c for an example). |
| |
| Then, define the TEST_MSG string, which will be used when reporting errors. |
| |
| Next, define the function performing the actual tests, in general |
| relying on the helpers provided by arm-neon-ref.h, which means: |
| |
| * declare necessary vectors of suitable types: using |
| DECL_VARIABLE_ALL_VARIANTS when all variants are supported, or the |
| relevant of subset calls to DECL_VARIABLE. |
| |
| * call clean_results() to initialize the 'results' buffers. |
| |
| * initialize the input vectors, using VLOAD, VDUP or VSET_LANE (vld* |
| tests do not need this step, since their actual purpose is to |
| initialize vectors). |
| |
| * execute the intrinsic on relevant variants, for instance using |
| TEST_MACRO_ALL_VARIANTS_2_5. |
| |
| * call check_results() to check that the results match the expected |
| values. |
| |
| A template test file could be: |
| ================================================================= |
| #include <arm_neon.h> |
| #include "arm-neon-ref.h" |
| #include "compute-ref-data.h" |
| |
| /* Expected results. */ |
| VECT_VAR_DECL(expected,int,8,8) [] = { 0xf6, 0xf7, 0xf8, 0xf9, |
| 0xfa, 0xfb, 0xfc, 0xfd }; |
| /* and as many others as necessary. */ |
| |
| #define TEST_MSG "VMYINTRINSIC" |
| void exec_myintrinsic (void) |
| { |
| /* my test: v4=vmyintrinsic(v1,v2,v3), then store the result. */ |
| #define TEST_VMYINTR(Q, T1, T2, W, N) \ |
| VECT_VAR(vector_res, T1, W, N) = \ |
| vmyintr##Q##_##T2##W(VECT_VAR(vector1, T1, W, N), \ |
| VECT_VAR(vector2, T1, W, N), \ |
| VECT_VAR(vector3, T1, W, N)); \ |
| vst1##Q##_##T2##W(VECT_VAR(result, T1, W, N), VECT_VAR(vector_res, T1, W, N)) |
| |
| #define DECL_VMYINTR_VAR(VAR) \ |
| DECL_VARIABLE(VAR, int, 8, 8); |
| /* And as many others as necessary. */ |
| |
| DECL_VMYINTR_VAR(vector1); |
| DECL_VMYINTR_VAR(vector2); |
| DECL_VMYINTR_VAR(vector3); |
| DECL_VMYINTR_VAR(vector_res); |
| |
| clean_results (); |
| |
| /* Initialize input "vector1" from "buffer". */ |
| VLOAD(vector1, buffer, , int, s, 8, 8); |
| /* And as many others as necessary. */ |
| |
| /* Choose init value arbitrarily. */ |
| VDUP(vector2, , int, s, 8, 8, 1); |
| /* And as many others as necessary. */ |
| |
| /* Choose init value arbitrarily. */ |
| VDUP(vector3, , int, s, 8, 8, -5); |
| /* And as many others as necessary. */ |
| |
| /* Execute the tests. */ |
| TEST_VMYINTR(, int, s, 8, 8); |
| /* And as many others as necessary. */ |
| |
| check_results (TEST_MSG, ""); |
| } |
| |
| int main (void) |
| { |
| exec_vmyintrinsic (); |
| return 0; |
| } |
| ================================================================= |