xref: /qemu/tests/tcg/riscv64/test-fcvtmod.c (revision 8b7b9c5c)
1 #include <stdio.h>
2 #include <stddef.h>
3 #include <stdint.h>
4 
5 #define FFLAG_NX_SHIFT 0 /* inexact */
6 #define FFLAG_UF_SHIFT 1 /* underflow */
7 #define FFLAG_OF_SHIFT 2 /* overflow */
8 #define FFLAG_DZ_SHIFT 3 /* divide by zero */
9 #define FFLAG_NV_SHIFT 4 /* invalid operation */
10 
11 #define FFLAG_NV (1UL << FFLAG_NV_SHIFT)
12 #define FFLAG_DZ (1UL << FFLAG_DZ_SHIFT)
13 #define FFLAG_OF (1UL << FFLAG_OF_SHIFT)
14 #define FFLAG_UF (1UL << FFLAG_UF_SHIFT)
15 #define FFLAG_NX (1UL << FFLAG_NX_SHIFT)
16 
17 typedef struct fp64_fcvt_fcvtmod_testcase {
18     const char* name;
19     union {
20         uint64_t inp_lu;
21         double inp_lf;
22     };
23     uint64_t exp_fcvt;
24     uint8_t exp_fcvt_fflags;
25     uint64_t exp_fcvtmod;
26     uint8_t exp_fcvtmod_fflags;
27 } fp64_fcvt_fcvtmod_testcase_t;
28 
29 void print_fflags(uint8_t fflags)
30 {
31     int set = 0;
32 
33     if (fflags == 0) {
34         printf("-");
35         return;
36     }
37 
38     if (fflags & FFLAG_NV) {
39         printf("%sFFLAG_NV", set ? " | " : "");
40         set = 1;
41     }
42     if (fflags & FFLAG_DZ) {
43         printf("%sFFLAG_DZ", set ? " | " : "");
44         set = 1;
45     }
46     if (fflags & FFLAG_OF) {
47         printf("%sFFLAG_OF", set ? " | " : "");
48         set = 1;
49     }
50     if (fflags & FFLAG_UF) {
51         printf("%sFFLAG_UF", set ? " | " : "");
52         set = 1;
53     }
54     if (fflags & FFLAG_NX) {
55         printf("%sFFLAG_NX", set ? " | " : "");
56         set = 1;
57     }
58 }
59 
60 /* Clear all FP flags. */
61 static inline void clear_fflags()
62 {
63     __asm__ __volatile__("fsflags zero");
64 }
65 
66 /* Read all FP flags. */
67 static inline uint8_t get_fflags()
68 {
69     uint64_t v;
70     __asm__ __volatile__("frflags %0" : "=r"(v));
71     return (uint8_t)v;
72 }
73 
74 /* Move input value (without conversations) into an FP register. */
75 static inline double do_fmv_d_x(uint64_t inp)
76 {
77     double fpr;
78     __asm__ __volatile__("fmv.d.x %0, %1" : "=f"(fpr) : "r"(inp));
79     return fpr;
80 }
81 
82 static inline uint64_t do_fcvt_w_d(uint64_t inp, uint8_t *fflags)
83 {
84     uint64_t ret;
85     double fpr = do_fmv_d_x(inp);
86 
87     clear_fflags();
88 
89     __asm__ __volatile__("fcvt.w.d %0, %1, rtz" : "=r"(ret) : "f"(fpr));
90 
91     *fflags = get_fflags();
92 
93     return ret;
94 }
95 
96 static inline uint64_t do_fcvtmod_w_d(uint64_t inp, uint8_t *fflags)
97 {
98     uint64_t ret;
99     double fpr = do_fmv_d_x(inp);
100 
101     clear_fflags();
102 
103     /* fcvtmod.w.d rd, rs1, rtz = 1100001 01000 rs1 001 rd 1010011 */
104     asm(".insn r  0x53, 0x1, 0x61, %0, %1, f8" : "=r"(ret) : "f"(fpr));
105 
106     *fflags = get_fflags();
107 
108     return ret;
109 }
110 
111 static const fp64_fcvt_fcvtmod_testcase_t tests[] = {
112     /* Zero (exp=0, frac=0) */
113     { .name = "+0.0",
114       .inp_lf = 0x0p0,
115       .exp_fcvt = 0x0000000000000000,
116       .exp_fcvt_fflags = 0,
117       .exp_fcvtmod = 0x0000000000000000,
118       .exp_fcvtmod_fflags = 0 },
119     { .name = "-0.0",
120       .inp_lf = -0x0p0,
121       .exp_fcvt = 0x0000000000000000,
122       .exp_fcvt_fflags = 0,
123       .exp_fcvtmod = 0x0000000000000000,
124       .exp_fcvtmod_fflags = 0 },
125 
126     /* Subnormal: exp=0 frac!=0 */
127     { .name = "Subnormal frac=1",
128       .inp_lu = 0x0000000000000001,
129       .exp_fcvt = 0x0000000000000000,
130       .exp_fcvt_fflags = FFLAG_NX,
131       .exp_fcvtmod = 0,
132       .exp_fcvtmod_fflags = FFLAG_NX },
133     { .name = "Subnormal frac=0xf..f",
134       .inp_lu = 0x0000ffffffffffff,
135       .exp_fcvt = 0x0000000000000000,
136       .exp_fcvt_fflags = FFLAG_NX,
137       .exp_fcvtmod = 0,
138       .exp_fcvtmod_fflags = FFLAG_NX },
139     { .name = "Neg subnormal frac=1",
140       .inp_lu = 0x0000000000000001,
141       .exp_fcvt = 0x0000000000000000,
142       .exp_fcvt_fflags = FFLAG_NX,
143       .exp_fcvtmod = 0,
144       .exp_fcvtmod_fflags = FFLAG_NX },
145     { .name = "Neg subnormal frac=0xf..f",
146       .inp_lu = 0x8000ffffffffffff,
147       .exp_fcvt = 0x0000000000000000,
148       .exp_fcvt_fflags = FFLAG_NX,
149       .exp_fcvtmod = 0,
150       .exp_fcvtmod_fflags = FFLAG_NX },
151 
152     /* Infinity: exp=0x7ff, frac=0 */
153     { .name = "+INF",
154       .inp_lu = 0x7ff0000000000000,
155       .exp_fcvt = 0x000000007fffffff, /* int32 max */
156       .exp_fcvt_fflags = FFLAG_NV,
157       .exp_fcvtmod = 0,
158       .exp_fcvtmod_fflags = FFLAG_NV },
159     { .name = "-INF",
160       .inp_lu = 0xfff0000000000000,
161       .exp_fcvt = 0xffffffff80000000, /* int32 min */
162       .exp_fcvt_fflags = FFLAG_NV,
163       .exp_fcvtmod = 0,
164       .exp_fcvtmod_fflags = FFLAG_NV },
165 
166     /* NaN: exp=7ff, frac!=0 */
167     { .name = "canonical NaN",
168       .inp_lu = 0x7ff8000000000000,
169       .exp_fcvt = 0x000000007fffffff, /* int32 max */
170       .exp_fcvt_fflags = FFLAG_NV,
171       .exp_fcvtmod = 0,
172       .exp_fcvtmod_fflags = FFLAG_NV },
173     { .name = "non-canonical NaN",
174       .inp_lu = 0x7ff8000000100000,
175       .exp_fcvt = 0x000000007fffffff, /* int32 min */
176       .exp_fcvt_fflags = FFLAG_NV,
177       .exp_fcvtmod = 0,
178       .exp_fcvtmod_fflags = FFLAG_NV },
179 
180     /* Normal numbers: exp!=0, exp!=7ff */
181     { .name = "+smallest normal value",
182       .inp_lu = 0x0010000000000000,
183       .exp_fcvt = 0,
184       .exp_fcvt_fflags = FFLAG_NX,
185       .exp_fcvtmod = 0,
186       .exp_fcvtmod_fflags = FFLAG_NX },
187     { .name = "-smallest normal value",
188       .inp_lu = 0x8010000000000000,
189       .exp_fcvt = 0,
190       .exp_fcvt_fflags = FFLAG_NX,
191       .exp_fcvtmod = 0,
192       .exp_fcvtmod_fflags = FFLAG_NX },
193 
194     { .name = "+0.5",
195       .inp_lf = 0x1p-1,
196       .exp_fcvt = 0,
197       .exp_fcvt_fflags = FFLAG_NX,
198       .exp_fcvtmod = 0,
199       .exp_fcvtmod_fflags = FFLAG_NX },
200     { .name = "-0.5",
201       .inp_lf = -0x1p-1,
202       .exp_fcvt = 0,
203       .exp_fcvt_fflags = FFLAG_NX,
204       .exp_fcvtmod = 0,
205       .exp_fcvtmod_fflags = FFLAG_NX },
206 
207     { .name = "+value just below 1.0",
208       .inp_lu = 0x3fefffffffffffff,
209       .exp_fcvt = 0,
210       .exp_fcvt_fflags = FFLAG_NX,
211       .exp_fcvtmod = 0,
212       .exp_fcvtmod_fflags = FFLAG_NX },
213     { .name = "-value just above -1.0",
214       .inp_lu = 0xbfefffffffffffff,
215       .exp_fcvt = 0,
216       .exp_fcvt_fflags = FFLAG_NX,
217       .exp_fcvtmod = 0,
218       .exp_fcvtmod_fflags = FFLAG_NX },
219 
220     { .name = "+1.0",
221       .inp_lf = 0x1p0,
222       .exp_fcvt = 0x0000000000000001,
223       .exp_fcvt_fflags = 0,
224       .exp_fcvtmod = 0x0000000000000001,
225       .exp_fcvtmod_fflags = 0 },
226     { .name = "-1.0",
227       .inp_lf = -0x1p0,
228       .exp_fcvt = 0xffffffffffffffff,
229       .exp_fcvt_fflags = 0,
230       .exp_fcvtmod = 0xffffffffffffffff,
231       .exp_fcvtmod_fflags = 0 },
232 
233     { .name = "+1.5",
234       .inp_lu = 0x3ff8000000000000,
235       .exp_fcvt = 1,
236       .exp_fcvt_fflags = FFLAG_NX,
237       .exp_fcvtmod = 1,
238       .exp_fcvtmod_fflags = FFLAG_NX },
239     { .name = "-1.5",
240       .inp_lu = 0xbff8000000000000,
241       .exp_fcvt = 0xffffffffffffffff,
242       .exp_fcvt_fflags = FFLAG_NX,
243       .exp_fcvtmod = 0xffffffffffffffff,
244       .exp_fcvtmod_fflags = FFLAG_NX },
245 
246     { .name = "+max int32 (2147483647)",
247       .inp_lu = 0x41dfffffffc00000,
248       .exp_fcvt = 0x000000007fffffff,
249       .exp_fcvt_fflags = 0,
250       .exp_fcvtmod = 0x000000007fffffff,
251       .exp_fcvtmod_fflags = 0 },
252     { .name = "+max int32 +1 (2147483648)",
253       .inp_lf = 0x1p31,
254       .exp_fcvt = 0x000000007fffffff,
255       .exp_fcvt_fflags = FFLAG_NV,
256       .exp_fcvtmod = (uint64_t)-2147483648l, /* int32 min */
257       .exp_fcvtmod_fflags = FFLAG_NV },
258     { .name = "+max int32 +2 (2147483649)",
259       .inp_lu = 0x41e0000000200000,
260       .exp_fcvt = 0x000000007fffffff,
261       .exp_fcvt_fflags = FFLAG_NV,
262       .exp_fcvtmod = (uint64_t)-2147483647l, /* int32 min +1 */
263       .exp_fcvtmod_fflags = FFLAG_NV },
264 
265     { .name = "-max int32 (-2147483648)",
266       .inp_lf = -0x1p31,
267       .exp_fcvt = 0xffffffff80000000,
268       .exp_fcvt_fflags = 0,
269       .exp_fcvtmod = 0xffffffff80000000,
270       .exp_fcvtmod_fflags = 0 },
271     { .name = "-max int32 -1 (-2147483649)",
272       .inp_lf = -0x1.00000002p+31,
273       .exp_fcvt = 0xffffffff80000000,
274       .exp_fcvt_fflags = FFLAG_NV,
275       .exp_fcvtmod = 2147483647, /* int32 max */
276       .exp_fcvtmod_fflags = FFLAG_NV },
277     { .name = "-max int32 -2 (-2147483650)",
278       .inp_lf = -0x1.00000004p+31,
279       .exp_fcvt = 0xffffffff80000000,
280       .exp_fcvt_fflags = FFLAG_NV,
281       .exp_fcvtmod = 2147483646, /* int32 max -1 */
282       .exp_fcvtmod_fflags = FFLAG_NV },
283 };
284 
285 int run_fcvtmod_tests()
286 {
287     uint64_t act_fcvt;
288     uint8_t act_fcvt_fflags;
289     uint64_t act_fcvtmod;
290     uint8_t act_fcvtmod_fflags;
291 
292     for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
293         const fp64_fcvt_fcvtmod_testcase_t *t = &tests[i];
294 
295         act_fcvt = do_fcvt_w_d(t->inp_lu, &act_fcvt_fflags);
296         int fcvt_correct = act_fcvt == t->exp_fcvt &&
297                     act_fcvt_fflags == t->exp_fcvt_fflags;
298         act_fcvtmod = do_fcvtmod_w_d(t->inp_lu, &act_fcvtmod_fflags);
299         int fcvtmod_correct = act_fcvtmod == t->exp_fcvtmod &&
300                        act_fcvtmod_fflags == t->exp_fcvtmod_fflags;
301 
302         if (fcvt_correct && fcvtmod_correct) {
303             continue;
304         }
305 
306         printf("Test %zu (%s) failed!\n", i, t->name);
307 
308         double fpr = do_fmv_d_x(t->inp_lu);
309         printf("inp_lu: 0x%016lx == %lf\n", t->inp_lu, fpr);
310         printf("inp_lf: %lf\n", t->inp_lf);
311 
312         uint32_t sign = (t->inp_lu >> 63);
313         uint32_t exp = (uint32_t)(t->inp_lu >> 52) & 0x7ff;
314         uint64_t frac = t->inp_lu & 0xfffffffffffffull; /* significand */
315         int true_exp = exp - 1023;
316         int shift = true_exp - 52;
317         uint64_t true_frac = frac | 1ull << 52;
318 
319         printf("sign=%d, exp=0x%03x, frac=0x%012lx\n", sign, exp, frac);
320         printf("true_exp=%d, shift=%d, true_frac=0x%016lx\n", true_exp, shift, true_frac);
321 
322         if (!fcvt_correct) {
323             printf("act_fcvt: 0x%016lx == %li\n", act_fcvt, act_fcvt);
324             printf("exp_fcvt: 0x%016lx == %li\n", t->exp_fcvt, t->exp_fcvt);
325             printf("act_fcvt_fflags: "); print_fflags(act_fcvt_fflags); printf("\n");
326             printf("exp_fcvt_fflags: "); print_fflags(t->exp_fcvt_fflags); printf("\n");
327         }
328 
329         if (!fcvtmod_correct) {
330             printf("act_fcvtmod: 0x%016lx == %li\n", act_fcvtmod, act_fcvtmod);
331             printf("exp_fcvtmod: 0x%016lx == %li\n", t->exp_fcvtmod, t->exp_fcvtmod);
332             printf("act_fcvtmod_fflags: "); print_fflags(act_fcvtmod_fflags); printf("\n");
333             printf("exp_fcvtmod_fflags: "); print_fflags(t->exp_fcvtmod_fflags); printf("\n");
334         }
335 
336         return 1;
337     }
338 
339     return 0;
340 }
341 
342 int main()
343 {
344     return run_fcvtmod_tests();
345 }
346