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