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
print_fflags(uint8_t fflags)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. */
clear_fflags()61 static inline void clear_fflags()
62 {
63 __asm__ __volatile__("fsflags zero");
64 }
65
66 /* Read all FP flags. */
get_fflags()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. */
do_fmv_d_x(uint64_t inp)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
do_fcvt_w_d(uint64_t inp,uint8_t * fflags)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
do_fcvtmod_w_d(uint64_t inp,uint8_t * fflags)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
run_fcvtmod_tests()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
main()342 int main()
343 {
344 return run_fcvtmod_tests();
345 }
346