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