xref: /openbmc/qemu/tests/tcg/s390x/vfminmax.c (revision 7d87775f)
1 #define _GNU_SOURCE
2 #include <fenv.h>
3 #include <stdbool.h>
4 #include <stdio.h>
5 #include <string.h>
6 
7 #include "float.h"
8 
9 /*
10  * vfmin/vfmax instruction execution.
11  */
12 #define VFMIN 0xEE
13 #define VFMAX 0xEF
14 
15 extern char insn[6];
16 asm(".pushsection .rwx,\"awx\",@progbits\n"
17     ".globl insn\n"
18     /* e7 89 a0 00 2e ef */
19     "insn: vfmaxsb %v24,%v25,%v26,0\n"
20     ".popsection\n");
21 
22 static void vfminmax(unsigned int op,
23                      unsigned int m4, unsigned int m5, unsigned int m6,
24                      void *v1, const void *v2, const void *v3)
25 {
26     insn[3] = (m6 << 4) | m5;
27     insn[4] = (m4 << 4) | 0x0e;
28     insn[5] = op;
29 
30     asm("vl %%v25,%[v2]\n"
31         "vl %%v26,%[v3]\n"
32         "ex 0,%[insn]\n"
33         "vst %%v24,%[v1]\n"
34         : [v1] "=m" (*(char (*)[16])v1)
35         : [v2] "m" (*(const char (*)[16])v2)
36         , [v3] "m" (*(const char (*)[16])v3)
37         , [insn] "m" (insn)
38         : "v24", "v25", "v26");
39 }
40 
41 /*
42  * PoP tables as close to the original as possible.
43  */
44 struct signed_test {
45     int op;
46     int m6;
47     const char *m6_desc;
48     const char *table[N_SIGNED_CLASSES][N_SIGNED_CLASSES];
49 } signed_tests[] = {
50     {
51         .op = VFMIN,
52         .m6 = 0,
53         .m6_desc = "IEEE MinNum",
54         .table = {
55              /*         -inf         -Fn          -0           +0           +Fn          +inf         QNaN         SNaN     */
56             {/* -inf */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "Xi: T(b*)"},
57             {/* -Fn  */ "T(b)",      "T(M(a,b))", "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "Xi: T(b*)"},
58             {/* -0   */ "T(b)",      "T(b)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "Xi: T(b*)"},
59             {/* +0   */ "T(b)",      "T(b)",      "T(b)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "Xi: T(b*)"},
60             {/* +Fn  */ "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(M(a,b))", "T(a)",      "T(a)",      "Xi: T(b*)"},
61             {/* +inf */ "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(a)",      "T(a)",      "Xi: T(b*)"},
62             {/* QNaN */ "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(a)",      "Xi: T(b*)"},
63             {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"},
64         },
65     },
66     {
67         .op = VFMIN,
68         .m6 = 1,
69         .m6_desc = "JAVA Math.Min()",
70         .table = {
71              /*         -inf         -Fn          -0           +0           +Fn          +inf         QNaN         SNaN     */
72             {/* -inf */ "T(b)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(b)",      "Xi: T(b*)"},
73             {/* -Fn  */ "T(b)",      "T(M(a,b))", "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(b)",      "Xi: T(b*)"},
74             {/* -0   */ "T(b)",      "T(b)",      "T(b)",      "T(a)",      "T(a)",      "T(a)",      "T(b)",      "Xi: T(b*)"},
75             {/* +0   */ "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(a)",      "T(a)",      "T(b)",      "Xi: T(b*)"},
76             {/* +Fn  */ "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(M(a,b))", "T(a)",      "T(b)",      "Xi: T(b*)"},
77             {/* +inf */ "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "Xi: T(b*)"},
78             {/* QNaN */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "Xi: T(b*)"},
79             {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"},
80         },
81     },
82     {
83         .op = VFMIN,
84         .m6 = 2,
85         .m6_desc = "C-style Min Macro",
86         .table = {
87              /*         -inf        -Fn          -0          +0          +Fn          +inf        QNaN        SNaN    */
88             {/* -inf */ "T(b)",     "T(a)",      "T(a)",     "T(a)",     "T(a)",      "T(a)",     "Xi: T(b)", "Xi: T(b)"},
89             {/* -Fn  */ "T(b)",     "T(M(a,b))", "T(a)",     "T(a)",     "T(a)",      "T(a)",     "Xi: T(b)", "Xi: T(b)"},
90             {/* -0   */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(a)",      "T(a)",     "Xi: T(b)", "Xi: T(b)"},
91             {/* +0   */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(a)",      "T(a)",     "Xi: T(b)", "Xi: T(b)"},
92             {/* +Fn  */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(M(a,b))", "T(a)",     "Xi: T(b)", "Xi: T(b)"},
93             {/* +inf */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(b)",      "T(a)",     "Xi: T(b)", "Xi: T(b)"},
94             {/* QNaN */ "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"},
95             {/* SNaN */ "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"},
96         },
97     },
98     {
99         .op = VFMIN,
100         .m6 = 3,
101         .m6_desc = "C++ algorithm.min()",
102         .table = {
103              /*         -inf        -Fn          -0          +0          +Fn          +inf        QNaN        SNaN    */
104             {/* -inf */ "T(b)",     "T(a)",      "T(a)",     "T(a)",     "T(a)",      "T(a)",     "Xi: T(a)", "Xi: T(a)"},
105             {/* -Fn  */ "T(b)",     "T(M(a,b))", "T(a)",     "T(a)",     "T(a)",      "T(a)",     "Xi: T(a)", "Xi: T(a)"},
106             {/* -0   */ "T(b)",     "T(b)",      "T(a)",     "T(a)",     "T(a)",      "T(a)",     "Xi: T(a)", "Xi: T(a)"},
107             {/* +0   */ "T(b)",     "T(b)",      "T(a)",     "T(a)",     "T(a)",      "T(a)",     "Xi: T(a)", "Xi: T(a)"},
108             {/* +Fn  */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(M(a,b))", "T(a)",     "Xi: T(a)", "Xi: T(a)"},
109             {/* +inf */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(b)",      "T(a)",     "Xi: T(a)", "Xi: T(a)"},
110             {/* QNaN */ "Xi: T(a)", "Xi: T(a)",  "Xi: T(a)", "Xi: T(a)", "Xi: T(a)",  "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"},
111             {/* SNaN */ "Xi: T(a)", "Xi: T(a)",  "Xi: T(a)", "Xi: T(a)", "Xi: T(a)",  "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"},
112         },
113     },
114     {
115         .op = VFMIN,
116         .m6 = 4,
117         .m6_desc = "fmin()",
118         .table = {
119              /*         -inf        -Fn          -0          +0          +Fn          +inf        QNaN        SNaN    */
120             {/* -inf */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(a)",      "T(a)",     "T(a)",     "Xi: T(a)"},
121             {/* -Fn  */ "T(b)",     "T(M(a,b))", "T(a)",     "T(a)",     "T(a)",      "T(a)",     "T(a)",     "Xi: T(a)"},
122             {/* -0   */ "T(b)",     "T(b)",      "T(a)",     "T(a)",     "T(a)",      "T(a)",     "T(a)",     "Xi: T(a)"},
123             {/* +0   */ "T(b)",     "T(b)",      "T(b)",     "T(a)",     "T(a)",      "T(a)",     "T(a)",     "Xi: T(a)"},
124             {/* +Fn  */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(M(a,b))", "T(a)",     "T(a)",     "Xi: T(a)"},
125             {/* +inf */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(b)",      "T(a)",     "T(a)",     "Xi: T(a)"},
126             {/* QNaN */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(b)",      "T(b)",     "T(a)",     "Xi: T(a)"},
127             {/* SNaN */ "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(a)", "Xi: T(a)"},
128         },
129     },
130 
131     {
132         .op = VFMAX,
133         .m6 = 0,
134         .m6_desc = "IEEE MaxNum",
135         .table = {
136              /*         -inf         -Fn          -0           +0           +Fn          +inf         QNaN         SNaN     */
137             {/* -inf */ "T(a)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(a)",      "Xi: T(b*)"},
138             {/* -Fn  */ "T(a)",      "T(M(a,b))", "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(a)",      "Xi: T(b*)"},
139             {/* -0   */ "T(a)",      "T(a)",      "T(a)",      "T(b)",      "T(b)",      "T(b)",      "T(a)",      "Xi: T(b*)"},
140             {/* +0   */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(b)",      "T(b)",      "T(a)",      "Xi: T(b*)"},
141             {/* +Fn  */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(M(a,b))", "T(b)",      "T(a)",      "Xi: T(b*)"},
142             {/* +inf */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "Xi: T(b*)"},
143             {/* QNaN */ "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(a)",      "Xi: T(b*)"},
144             {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"},
145         },
146     },
147     {
148         .op = VFMAX,
149         .m6 = 1,
150         .m6_desc = "JAVA Math.Max()",
151         .table = {
152              /*         -inf         -Fn          -0           +0           +Fn          +inf         QNaN         SNaN     */
153             {/* -inf */ "T(a)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "Xi: T(b*)"},
154             {/* -Fn  */ "T(a)",      "T(M(a,b))", "T(b)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "Xi: T(b*)"},
155             {/* -0   */ "T(a)",      "T(a)",      "T(a)",      "T(b)",      "T(b)",      "T(b)",      "T(b)",      "Xi: T(b*)"},
156             {/* +0   */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(b)",      "T(b)",      "T(b)",      "Xi: T(b*)"},
157             {/* +Fn  */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(M(a,b))", "T(b)",      "T(b)",      "Xi: T(b*)"},
158             {/* +inf */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(b)",      "Xi: T(b*)"},
159             {/* QNaN */ "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "T(a)",      "Xi: T(b*)"},
160             {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"},
161         },
162     },
163     {
164         .op = VFMAX,
165         .m6 = 2,
166         .m6_desc = "C-style Max Macro",
167         .table = {
168              /*         -inf        -Fn          -0          +0          +Fn          +inf        QNaN        SNaN    */
169             {/* -inf */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(b)",      "T(b)",     "Xi: T(b)", "Xi: T(b)"},
170             {/* -Fn  */ "T(a)",     "T(M(a,b))", "T(b)",     "T(b)",     "T(b)",      "T(b)",     "Xi: T(b)", "Xi: T(b)"},
171             {/* -0   */ "T(a)",     "T(a)",      "T(b)",     "T(b)",     "T(b)",      "T(b)",     "Xi: T(b)", "Xi: T(b)"},
172             {/* +0   */ "T(a)",     "T(a)",      "T(b)",     "T(b)",     "T(b)",      "T(b)",     "Xi: T(b)", "Xi: T(b)"},
173             {/* +Fn  */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(M(a,b))", "T(b)",     "Xi: T(b)", "Xi: T(b)"},
174             {/* +inf */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(a)",      "T(b)",     "Xi: T(b)", "Xi: T(b)"},
175             {/* QNaN */ "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"},
176             {/* SNaN */ "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"},
177         },
178     },
179     {
180         .op = VFMAX,
181         .m6 = 3,
182         .m6_desc = "C++ algorithm.max()",
183         .table = {
184              /*         -inf        -Fn          -0          +0          +Fn          +inf        QNaN        SNaN    */
185             {/* -inf */ "T(a)",     "T(b)",      "T(b)",     "T(b)",     "T(b)",      "T(b)",     "Xi: T(a)", "Xi: T(a)"},
186             {/* -Fn  */ "T(a)",     "T(M(a,b))", "T(b)",     "T(b)",     "T(b)",      "T(b)",     "Xi: T(a)", "Xi: T(a)"},
187             {/* -0   */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(b)",      "T(b)",     "Xi: T(a)", "Xi: T(a)"},
188             {/* +0   */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(b)",      "T(b)",     "Xi: T(a)", "Xi: T(a)"},
189             {/* +Fn  */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(M(a,b))", "T(b)",     "Xi: T(a)", "Xi: T(a)"},
190             {/* +inf */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(a)",      "T(a)",     "Xi: T(a)", "Xi: T(a)"},
191             {/* QNaN */ "Xi: T(a)", "Xi: T(a)",  "Xi: T(a)", "Xi: T(a)", "Xi: T(a)",  "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"},
192             {/* SNaN */ "Xi: T(a)", "Xi: T(a)",  "Xi: T(a)", "Xi: T(a)", "Xi: T(a)",  "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"},
193         },
194     },
195     {
196         .op = VFMAX,
197         .m6 = 4,
198         .m6_desc = "fmax()",
199         .table = {
200              /*         -inf        -Fn          -0          +0          +Fn          +inf        QNaN        SNaN    */
201             {/* -inf */ "T(a)",     "T(b)",      "T(b)",     "T(b)",     "T(b)",      "T(b)",     "T(a)",     "Xi: T(a)"},
202             {/* -Fn  */ "T(a)",     "T(M(a,b))", "T(b)",     "T(b)",     "T(b)",      "T(b)",     "T(a)",     "Xi: T(a)"},
203             {/* -0   */ "T(a)",     "T(a)",      "T(a)",     "T(b)",     "T(b)",      "T(b)",     "T(a)",     "Xi: T(a)"},
204             {/* +0   */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(b)",      "T(b)",     "T(a)",     "Xi: T(a)"},
205             {/* +Fn  */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(M(a,b))", "T(b)",     "T(a)",     "Xi: T(a)"},
206             {/* +inf */ "T(a)",     "T(a)",      "T(a)",     "T(a)",     "T(a)",      "T(a)",     "T(a)",     "Xi: T(a)"},
207             {/* QNaN */ "T(b)",     "T(b)",      "T(b)",     "T(b)",     "T(b)",      "T(b)",     "T(a)",     "Xi: T(a)"},
208             {/* SNaN */ "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(b)", "Xi: T(b)",  "Xi: T(b)", "Xi: T(a)", "Xi: T(a)"},
209         },
210     },
211 };
212 
213 static int signed_test(struct signed_test *test, int m4, int m5,
214                        const void *v1_exp, bool xi_exp,
215                        const void *v2, const void *v3)
216 {
217     size_t n = (m5 & 8) ? float_sizes[m4 - 2] : 16;
218     char v1[16];
219     bool xi;
220 
221     feclearexcept(FE_ALL_EXCEPT);
222     vfminmax(test->op, m4, m5, test->m6, v1, v2, v3);
223     xi = fetestexcept(FE_ALL_EXCEPT) == FE_INVALID;
224 
225     if (memcmp(v1, v1_exp, n) != 0 || xi != xi_exp) {
226         fprintf(stderr, "[  FAILED  ] %s ", test->m6_desc);
227         dump_v(stderr, v2, n);
228         fprintf(stderr, ", ");
229         dump_v(stderr, v3, n);
230         fprintf(stderr, ", %d, %d, %d: actual=", m4, m5, test->m6);
231         dump_v(stderr, v1, n);
232         fprintf(stderr, "/%d, expected=", (int)xi);
233         dump_v(stderr, v1_exp, n);
234         fprintf(stderr, "/%d\n", (int)xi_exp);
235         return 1;
236     }
237 
238     return 0;
239 }
240 
241 struct iter {
242     int cls[2];
243     int val[2];
244 };
245 
246 static bool iter_next(struct iter *it, int fmt)
247 {
248     int i;
249 
250     for (i = 1; i >= 0; i--) {
251         if (++it->val[i] != signed_floats[fmt][it->cls[i]].n) {
252             return true;
253         }
254         it->val[i] = 0;
255 
256         if (++it->cls[i] != N_SIGNED_CLASSES) {
257             return true;
258         }
259         it->cls[i] = 0;
260     }
261 
262     return false;
263 }
264 
265 int main(void)
266 {
267     int ret = 0;
268     size_t i;
269 
270     for (i = 0; i < sizeof(signed_tests) / sizeof(signed_tests[0]); i++) {
271         struct signed_test *test = &signed_tests[i];
272         int fmt;
273 
274         for (fmt = 0; fmt < N_FORMATS; fmt++) {
275             size_t float_size = float_sizes[fmt];
276             int m4 = fmt + 2;
277             int m5;
278 
279             for (m5 = 0; m5 <= 8; m5 += 8) {
280                 char v1_exp[16], v2[16], v3[16];
281                 bool xi_exp = false;
282                 struct iter it = {};
283                 int pos = 0;
284 
285                 do {
286                     const char *spec = test->table[it.cls[0]][it.cls[1]];
287 
288                     memcpy(&v2[pos],
289                            signed_floats[fmt][it.cls[0]].v[it.val[0]],
290                            float_size);
291                     memcpy(&v3[pos],
292                            signed_floats[fmt][it.cls[1]].v[it.val[1]],
293                            float_size);
294                     if (strcmp(spec, "T(a)") == 0 ||
295                         strcmp(spec, "Xi: T(a)") == 0) {
296                         memcpy(&v1_exp[pos], &v2[pos], float_size);
297                     } else if (strcmp(spec, "T(b)") == 0 ||
298                                strcmp(spec, "Xi: T(b)") == 0) {
299                         memcpy(&v1_exp[pos], &v3[pos], float_size);
300                     } else if (strcmp(spec, "Xi: T(a*)") == 0) {
301                         memcpy(&v1_exp[pos], &v2[pos], float_size);
302                         snan_to_qnan(&v1_exp[pos], fmt);
303                     } else if (strcmp(spec, "Xi: T(b*)") == 0) {
304                         memcpy(&v1_exp[pos], &v3[pos], float_size);
305                         snan_to_qnan(&v1_exp[pos], fmt);
306                     } else if (strcmp(spec, "T(M(a,b))") == 0) {
307                         /*
308                          * Comparing floats is risky, since the compiler might
309                          * generate the same instruction that we are testing.
310                          * Compare ints instead. This works, because we get
311                          * here only for +-Fn, and the corresponding test
312                          * values have identical exponents.
313                          */
314                         int v2_int = *(int *)&v2[pos];
315                         int v3_int = *(int *)&v3[pos];
316 
317                         if ((v2_int < v3_int) ==
318                             ((test->op == VFMIN) != (v2_int < 0))) {
319                             memcpy(&v1_exp[pos], &v2[pos], float_size);
320                         } else {
321                             memcpy(&v1_exp[pos], &v3[pos], float_size);
322                         }
323                     } else {
324                         fprintf(stderr, "Unexpected spec: %s\n", spec);
325                         return 1;
326                     }
327                     xi_exp |= spec[0] == 'X';
328                     pos += float_size;
329 
330                     if ((m5 & 8) || pos == 16) {
331                         ret |= signed_test(test, m4, m5,
332                                            v1_exp, xi_exp, v2, v3);
333                         pos = 0;
334                         xi_exp = false;
335                     }
336                 } while (iter_next(&it, fmt));
337 
338                 if (pos != 0) {
339                     ret |= signed_test(test, m4, m5, v1_exp, xi_exp, v2, v3);
340                 }
341             }
342         }
343     }
344 
345     return ret;
346 }
347