1 #ifndef S390_TEST_VECTOR_H
2 #define S390_TEST_VECTOR_H
3 
4 #include "stdbool.h"
5 #include "stdint.h"
6 #include "stdio.h"
7 #include "string.h"
8 
9 /* How many times should every test be executed? */
10 #ifndef S390_TEST_COUNT
11 #define S390_TEST_COUNT 10
12 #endif
13 
14 /* Test the instruction exactly one time. */
15 #define test_once(insn) test_##insn ()
16 
17 /* Test the instruction exactly S390_TEST_COUNT times.
18    "..." arguments specifies code which must be executed after each tests
19  */
20 #define test(insn, ...) \
21    for(iteration = 0; iteration < S390_TEST_COUNT; iteration++) \
22      { test_once(insn); \
23         __VA_ARGS__; \
24      }
25 
26 #define test_with_selective_printing(insn, info) \
27    for(iteration = 0; iteration < S390_TEST_COUNT; iteration++) \
28      { test_ ## insn ## _selective(info); }
29 
30 #ifdef __GNUC__
31 /* GCC complains about __int128 with -pedantic */
32 /* Hope that we will have int128_t in C standard someday. */
33 #pragma GCC diagnostic ignored "-Wpedantic"
34 #endif
35 
36 typedef union {
37    uint8_t u8[16];
38    int8_t s8[16];
39 
40    uint16_t u16[8];
41    int16_t s16[8];
42 
43    uint32_t u32[4];
44    int32_t s32[4];
45    float   f32[4];
46 
47    uint64_t u64[2];
48    int64_t s64[2];
49    double f64[2];
50 
51    unsigned __int128 u128[1];
52    __int128 s128[1];
53 } V128;
54 
55 typedef enum {
56    V128_NO_PRINTING = 0,
57    V128_V_RES_AS_INT  = 1 << 0,
58    V128_V_ARG1_AS_INT = 1 << 1,
59    V128_V_ARG2_AS_INT = 1 << 2,
60    V128_V_ARG3_AS_INT = 1 << 3,
61    V128_V_RES_AS_FLOAT64 = 1 << 4,
62    V128_V_ARG1_AS_FLOAT64 = 1 << 5,
63    V128_V_ARG2_AS_FLOAT64 = 1 << 6,
64    V128_V_ARG3_AS_FLOAT64 = 1 << 7,
65    V128_V_RES_AS_FLOAT32 = 1 << 8,
66    V128_V_ARG1_AS_FLOAT32 = 1 << 9,
67    V128_V_ARG2_AS_FLOAT32 = 1 << 10,
68    V128_V_ARG3_AS_FLOAT32 = 1 << 11,
69    V128_R_RES = 1 << 12,
70    V128_R_ARG1 = 1 << 13,
71    V128_R_ARG2 = 1 << 14,
72    V128_R_ARG3 = 1 << 15,
73    V128_V_RES_EVEN_ONLY = 1 << 16,
74    V128_V_RES_ZERO_ONLY = 1 << 17,
75    V128_PRINT_ALL = (V128_V_RES_AS_INT |
76                      V128_V_ARG1_AS_INT |
77                      V128_V_ARG2_AS_INT |
78                      V128_V_ARG3_AS_INT |
79                      V128_R_RES |
80                      V128_R_ARG1 |
81                      V128_R_ARG2 |
82                      V128_R_ARG3),
83 } s390x_test_usageInfo;
84 
print_hex(const V128 value)85 void print_hex(const V128 value) {
86    printf("%016lx | %016lx\n", value.u64[0], value.u64[1]);
87 }
88 
print_f32(const V128 value,int even_only,int zero_only)89 void print_f32(const V128 value, int even_only, int zero_only) {
90    if (zero_only)
91       printf("%a | -- | -- | --\n", value.f32[0]);
92    else if (even_only)
93       printf("%a | -- | %a | --\n", value.f32[0], value.f32[2]);
94    else
95       printf("%a | %a | %a | %a\n",
96              value.f32[0], value.f32[1], value.f32[2], value.f32[3]);
97 }
98 
print_f64(const V128 value,int zero_only)99 void print_f64(const V128 value, int zero_only) {
100    if (zero_only)
101       printf("%a | --\n", value.f64[0]);
102    else
103       printf("%a | %a\n", value.f64[0], value.f64[1]);
104 }
105 
print_uint64_t(const uint64_t value)106 void print_uint64_t(const uint64_t value) {
107    printf("%016lx\n", value);
108 }
109 
random_element(void)110 uint8_t random_element ( void )
111 {
112    static uint32_t seed = 80021;
113    seed = 1103515245 * seed + 12345;
114    return (seed >> 17) & 0xFF;
115 }
116 
random_V128(V128 * block)117 void random_V128 (V128 *block)
118 {
119    size_t i;
120    for(i = 0; i < 16; i++)
121    {
122       block->u8[i] = random_element();
123    }
124 }
125 
random_uint64_t()126 uint64_t random_uint64_t()
127 {
128    uint64_t result = 0ULL;
129    uint8_t  *ptr = (uint8_t *) &result;
130    size_t i;
131    for(i = 0; i < 8; i++)
132    {
133       ptr[i] = random_element();
134    }
135 
136    return result;
137 }
138 
139 /* Memory pool with some random data. Used for some instruction which need
140    an address to some memory chunk.
141    Pool should be large enough for all insn that use it.
142    (64 bytes and aligning are needed by VLBB insn)
143 
144    Content of this pool must be updated every iteration but not from test to test.
145 */
146 uint8_t random_memory_pool[64] __attribute__ ((aligned (64)));
randomize_memory_pool()147 void randomize_memory_pool()
148 {
149    size_t i;
150    for(i = 0; i < sizeof(random_memory_pool) / sizeof(random_memory_pool[0]); i++)
151    {
152       random_memory_pool[i] = random_element();
153    }
154 }
155 
156 /* Define a test for input. Takes up theese arguments:
157       insn        -- instruction name
158       asm_string  -- line (or multiple lines) with asm mnemonics for instruction
159 
160    The folowing registers layout expected:
161       ("r" for register form and m for memory form)
162       v1         -- vector arg1
163       v2         -- vector arg2
164       v3         -- vector arg3
165       v5         -- vector result
166       [{r,m}_arg1]   -- integer arg1
167       [{r,m}_arg2]   -- integer arg2
168       [{r,m}_arg3]   -- integer arg3
169       [{r,m}_result]      -- integer result
170       [{r,m}_memory_pool] -- address of random memory pool. Usefull for some instructions
171 
172 */
173 #define s390_test_generate(insn, asm_string) \
174 static void test_##insn##_selective(const s390x_test_usageInfo info) \
175 { \
176    V128 v_result = { .u64 = {0ULL, 0ULL} }; \
177    V128 v_arg1; \
178    V128 v_arg2; \
179    V128 v_arg3; \
180    uint64_t r_arg1 = random_uint64_t(); \
181    uint64_t r_arg2 = random_uint64_t(); \
182    uint64_t r_arg3 = random_uint64_t(); \
183    uint64_t r_result = 0ULL; \
184     \
185    random_V128(&v_arg1); \
186    random_V128(&v_arg2); \
187    random_V128(&v_arg3); \
188     \
189    __asm__ volatile( \
190        "vl  %%v1, %[v_arg1]\n" \
191        "vl  %%v2, %[v_arg2]\n" \
192        "vl  %%v3, %[v_arg3]\n" \
193        "vone %%v5\n" \
194        "srnmb 1(0)\n " \
195        asm_string "\n"\
196        "vst %%v5, %[v_result]\n" \
197        "vst %%v1, %[v_arg1]\n" \
198        "vst %%v2, %[v_arg2]\n" \
199        "vst %%v3, %[v_arg3]\n" \
200        : [v_result]      "=m" (v_result), \
201          [m_result]      "=m" (r_result), \
202          [r_result]      "+d" (r_result), \
203          [r_arg1]        "+d" (r_arg1), \
204          [r_arg2]        "+d" (r_arg2), \
205          [r_arg3]        "+d" (r_arg3) \
206        : [v_arg1]        "m" (v_arg1), \
207          [v_arg2]        "m" (v_arg2), \
208          [v_arg3]        "m" (v_arg3), \
209          [m_arg1]        "m" (r_arg1), \
210          [m_arg2]        "m" (r_arg2), \
211          [m_arg3]        "m" (r_arg3), \
212          [r_memory_pool] "r" (random_memory_pool), \
213          [m_memory_pool] "m" (random_memory_pool) \
214        : "memory", "cc", \
215          "r1", "r2", "r3", "r5", \
216          "v1", "v2", "v3", "v5"); \
217     \
218    printf("insn %s:\n", #insn); \
219    if (info & V128_V_ARG1_AS_INT) \
220       {printf("  v_arg1   = "); print_hex(v_arg1);} \
221    if (info & V128_V_ARG2_AS_INT) \
222       {printf("  v_arg2   = "); print_hex(v_arg2);} \
223    if (info & V128_V_ARG3_AS_INT) \
224       {printf("  v_arg3   = "); print_hex(v_arg3);} \
225    if (info & V128_V_RES_AS_INT) \
226       {printf("  v_result = "); print_hex(v_result);} \
227    \
228    if (info & V128_V_ARG1_AS_FLOAT64) \
229       {printf("  v_arg1   = "); print_f64(v_arg1, 0);} \
230    if (info & V128_V_ARG2_AS_FLOAT64) \
231       {printf("  v_arg2   = "); print_f64(v_arg2, 0);} \
232    if (info & V128_V_ARG3_AS_FLOAT64) \
233       {printf("  v_arg3   = "); print_f64(v_arg3, 0);} \
234    if (info & V128_V_RES_AS_FLOAT64) { \
235       printf("  v_result = "); \
236       print_f64(v_result, info & V128_V_RES_ZERO_ONLY); \
237    } \
238    \
239    if (info & V128_V_ARG1_AS_FLOAT32) \
240       {printf("  v_arg1   = "); print_f32(v_arg1, 0, 0);} \
241    if (info & V128_V_ARG2_AS_FLOAT32) \
242       {printf("  v_arg2   = "); print_f32(v_arg2, 0, 0);} \
243    if (info & V128_V_ARG3_AS_FLOAT32) \
244       {printf("  v_arg3   = "); print_f32(v_arg3, 0, 0);} \
245    if (info & V128_V_RES_AS_FLOAT32) { \
246       printf("  v_result = "); \
247       print_f32(v_result, info & V128_V_RES_EVEN_ONLY, \
248                 info & V128_V_RES_ZERO_ONLY); \
249    } \
250    if (info & V128_R_ARG1) \
251       {printf("  r_arg1   = "); print_uint64_t(r_arg1);} \
252    if (info & V128_R_ARG2) \
253       {printf("  r_arg2   = "); print_uint64_t(r_arg2);} \
254    if (info & V128_R_ARG3) \
255       {printf("  r_arg3   = "); print_uint64_t(r_arg3);} \
256    if (info & V128_R_RES) \
257       {printf("  r_result = "); print_uint64_t(r_result);} \
258 } \
259 __attribute__((unused)) static void test_##insn() \
260 { \
261    test_##insn##_selective (V128_PRINT_ALL); \
262 }
263 
264 /* Stores CC to %[r_result].
265    Usefull when testing instructions which modify condition code.
266 */
267 #define S390_TEST_PUT_CC_TO_RESULT "ipm %[r_result] \n srl %[r_result], 28 \n"
268 
269 #endif
270