1// See LICENSE for license details. 2 3#include "fp_emulation.h" 4#include "unprivileged_memory.h" 5#include "softfloat.h" 6#include "internals.h" 7#include "config.h" 8 9DECLARE_EMULATION_FUNC(emulate_fp) 10{ 11 asm (".pushsection .rodata\n" 12 "fp_emulation_table:\n" 13 " .word emulate_fadd\n" 14 " .word emulate_fsub\n" 15 " .word emulate_fmul\n" 16 " .word emulate_fdiv\n" 17 " .word emulate_fsgnj\n" 18 " .word emulate_fmin\n" 19 " .word truly_illegal_insn\n" 20 " .word truly_illegal_insn\n" 21 " .word emulate_fcvt_ff\n" 22 " .word truly_illegal_insn\n" 23 " .word truly_illegal_insn\n" 24 " .word emulate_fsqrt\n" 25 " .word truly_illegal_insn\n" 26 " .word truly_illegal_insn\n" 27 " .word truly_illegal_insn\n" 28 " .word truly_illegal_insn\n" 29 " .word truly_illegal_insn\n" 30 " .word truly_illegal_insn\n" 31 " .word truly_illegal_insn\n" 32 " .word truly_illegal_insn\n" 33 " .word emulate_fcmp\n" 34 " .word truly_illegal_insn\n" 35 " .word truly_illegal_insn\n" 36 " .word truly_illegal_insn\n" 37 " .word emulate_fcvt_if\n" 38 " .word truly_illegal_insn\n" 39 " .word emulate_fcvt_fi\n" 40 " .word truly_illegal_insn\n" 41 " .word emulate_fmv_if\n" 42 " .word truly_illegal_insn\n" 43 " .word emulate_fmv_fi\n" 44 " .word truly_illegal_insn\n" 45 " .popsection"); 46 47 // if FPU is disabled, punt back to the OS 48 if (unlikely((mstatus & MSTATUS_FS) == 0)) 49 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 50 51 extern uint32_t fp_emulation_table[]; 52 uint32_t* pf = (void*)fp_emulation_table + ((insn >> 25) & 0x7c); 53 emulation_func f = (emulation_func)(uintptr_t)*pf; 54 55 SETUP_STATIC_ROUNDING(insn); 56 return f(regs, mcause, mepc, mstatus, insn); 57} 58 59#define f32(x) ((float32_t){ .v = x }) 60#define f64(x) ((float64_t){ .v = x }) 61 62void emulate_any_fadd(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc, uintptr_t mstatus, insn_t insn, int32_t neg_b) 63{ 64 if (GET_PRECISION(insn) == PRECISION_S) { 65 uint32_t rs1 = GET_F32_RS1(insn, regs); 66 uint32_t rs2 = GET_F32_RS2(insn, regs) ^ neg_b; 67 SET_F32_RD(insn, regs, f32_add(f32(rs1), f32(rs2)).v); 68 } else if (GET_PRECISION(insn) == PRECISION_D) { 69 uint64_t rs1 = GET_F64_RS1(insn, regs); 70 uint64_t rs2 = GET_F64_RS2(insn, regs) ^ ((uint64_t)neg_b << 32); 71 SET_F64_RD(insn, regs, f64_add(f64(rs1), f64(rs2)).v); 72 } else { 73 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 74 } 75} 76 77DECLARE_EMULATION_FUNC(emulate_fadd) 78{ 79 return emulate_any_fadd(regs, mcause, mepc, mstatus, insn, 0); 80} 81 82DECLARE_EMULATION_FUNC(emulate_fsub) 83{ 84 return emulate_any_fadd(regs, mcause, mepc, mstatus, insn, INT32_MIN); 85} 86 87DECLARE_EMULATION_FUNC(emulate_fmul) 88{ 89 if (GET_PRECISION(insn) == PRECISION_S) { 90 uint32_t rs1 = GET_F32_RS1(insn, regs); 91 uint32_t rs2 = GET_F32_RS2(insn, regs); 92 SET_F32_RD(insn, regs, f32_mul(f32(rs1), f32(rs2)).v); 93 } else if (GET_PRECISION(insn) == PRECISION_D) { 94 uint64_t rs1 = GET_F64_RS1(insn, regs); 95 uint64_t rs2 = GET_F64_RS2(insn, regs); 96 SET_F64_RD(insn, regs, f64_mul(f64(rs1), f64(rs2)).v); 97 } else { 98 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 99 } 100} 101 102DECLARE_EMULATION_FUNC(emulate_fdiv) 103{ 104 if (GET_PRECISION(insn) == PRECISION_S) { 105 uint32_t rs1 = GET_F32_RS1(insn, regs); 106 uint32_t rs2 = GET_F32_RS2(insn, regs); 107 SET_F32_RD(insn, regs, f32_div(f32(rs1), f32(rs2)).v); 108 } else if (GET_PRECISION(insn) == PRECISION_D) { 109 uint64_t rs1 = GET_F64_RS1(insn, regs); 110 uint64_t rs2 = GET_F64_RS2(insn, regs); 111 SET_F64_RD(insn, regs, f64_div(f64(rs1), f64(rs2)).v); 112 } else { 113 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 114 } 115} 116 117DECLARE_EMULATION_FUNC(emulate_fsqrt) 118{ 119 if ((insn >> 20) & 0x1f) 120 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 121 122 if (GET_PRECISION(insn) == PRECISION_S) { 123 SET_F32_RD(insn, regs, f32_sqrt(f32(GET_F32_RS1(insn, regs))).v); 124 } else if (GET_PRECISION(insn) == PRECISION_D) { 125 SET_F64_RD(insn, regs, f64_sqrt(f64(GET_F64_RS1(insn, regs))).v); 126 } else { 127 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 128 } 129} 130 131DECLARE_EMULATION_FUNC(emulate_fsgnj) 132{ 133 int rm = GET_RM(insn); 134 if (rm >= 3) 135 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 136 137 #define DO_FSGNJ(rs1, rs2, rm) ({ \ 138 typeof(rs1) rs1_sign = (rs1) >> (8*sizeof(rs1)-1); \ 139 typeof(rs1) rs2_sign = (rs2) >> (8*sizeof(rs1)-1); \ 140 rs1_sign &= (rm) >> 1; \ 141 rs1_sign ^= (rm) ^ rs2_sign; \ 142 ((rs1) << 1 >> 1) | (rs1_sign << (8*sizeof(rs1)-1)); }) 143 144 if (GET_PRECISION(insn) == PRECISION_S) { 145 uint32_t rs1 = GET_F32_RS1(insn, regs); 146 uint32_t rs2 = GET_F32_RS2(insn, regs); 147 SET_F32_RD(insn, regs, DO_FSGNJ(rs1, rs2, rm)); 148 } else if (GET_PRECISION(insn) == PRECISION_D) { 149 uint64_t rs1 = GET_F64_RS1(insn, regs); 150 uint64_t rs2 = GET_F64_RS2(insn, regs); 151 SET_F64_RD(insn, regs, DO_FSGNJ(rs1, rs2, rm)); 152 } else { 153 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 154 } 155} 156 157DECLARE_EMULATION_FUNC(emulate_fmin) 158{ 159 int rm = GET_RM(insn); 160 if (rm >= 2) 161 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 162 163 if (GET_PRECISION(insn) == PRECISION_S) { 164 uint32_t rs1 = GET_F32_RS1(insn, regs); 165 uint32_t rs2 = GET_F32_RS2(insn, regs); 166 uint32_t arg1 = rm ? rs2 : rs1; 167 uint32_t arg2 = rm ? rs1 : rs2; 168 int use_rs1 = f32_lt_quiet(f32(arg1), f32(arg2)) || isNaNF32UI(rs2); 169 SET_F32_RD(insn, regs, use_rs1 ? rs1 : rs2); 170 } else if (GET_PRECISION(insn) == PRECISION_D) { 171 uint64_t rs1 = GET_F64_RS1(insn, regs); 172 uint64_t rs2 = GET_F64_RS2(insn, regs); 173 uint64_t arg1 = rm ? rs2 : rs1; 174 uint64_t arg2 = rm ? rs1 : rs2; 175 int use_rs1 = f64_lt_quiet(f64(arg1), f64(arg2)) || isNaNF64UI(rs2); 176 SET_F64_RD(insn, regs, use_rs1 ? rs1 : rs2); 177 } else { 178 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 179 } 180} 181 182DECLARE_EMULATION_FUNC(emulate_fcvt_ff) 183{ 184 int rs2_num = (insn >> 20) & 0x1f; 185 if (GET_PRECISION(insn) == PRECISION_S) { 186 if (rs2_num != 1) 187 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 188 SET_F32_RD(insn, regs, f64_to_f32(f64(GET_F64_RS1(insn, regs))).v); 189 } else if (GET_PRECISION(insn) == PRECISION_D) { 190 if (rs2_num != 0) 191 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 192 SET_F64_RD(insn, regs, f32_to_f64(f32(GET_F32_RS1(insn, regs))).v); 193 } else { 194 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 195 } 196} 197 198DECLARE_EMULATION_FUNC(emulate_fcvt_fi) 199{ 200 if (GET_PRECISION(insn) != PRECISION_S && GET_PRECISION(insn) != PRECISION_D) 201 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 202 203 int negative = 0; 204 uint64_t uint_val = GET_RS1(insn, regs); 205 206 switch ((insn >> 20) & 0x1f) 207 { 208 case 0: // int32 209 negative = (int32_t)uint_val < 0; 210 uint_val = (uint32_t)(negative ? -uint_val : uint_val); 211 break; 212 case 1: // uint32 213 uint_val = (uint32_t)uint_val; 214 break; 215#if __riscv_xlen == 64 216 case 2: // int64 217 negative = (int64_t)uint_val < 0; 218 uint_val = negative ? -uint_val : uint_val; 219 case 3: // uint64 220 break; 221#endif 222 default: 223 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 224 } 225 226 uint64_t float64 = ui64_to_f64(uint_val).v; 227 if (negative) 228 float64 ^= INT64_MIN; 229 230 if (GET_PRECISION(insn) == PRECISION_S) 231 SET_F32_RD(insn, regs, f64_to_f32(f64(float64)).v); 232 else 233 SET_F64_RD(insn, regs, float64); 234} 235 236DECLARE_EMULATION_FUNC(emulate_fcvt_if) 237{ 238 int rs2_num = (insn >> 20) & 0x1f; 239#if __riscv_xlen == 64 240 if (rs2_num >= 4) 241 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 242#else 243 if (rs2_num >= 2) 244 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 245#endif 246 247 int64_t float64; 248 if (GET_PRECISION(insn) == PRECISION_S) 249 float64 = f32_to_f64(f32(GET_F32_RS1(insn, regs))).v; 250 else if (GET_PRECISION(insn) == PRECISION_D) 251 float64 = GET_F64_RS1(insn, regs); 252 else 253 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 254 255 int negative = 0; 256 if (float64 < 0) { 257 negative = 1; 258 float64 ^= INT64_MIN; 259 } 260 uint64_t uint_val = f64_to_ui64(f64(float64), softfloat_roundingMode, true); 261 uint64_t result, limit, limit_result; 262 263 switch (rs2_num) 264 { 265 case 0: // int32 266 if (negative) { 267 result = (int32_t)-uint_val; 268 limit_result = limit = (uint32_t)INT32_MIN; 269 } else { 270 result = (int32_t)uint_val; 271 limit_result = limit = INT32_MAX; 272 } 273 break; 274 275 case 1: // uint32 276 limit = limit_result = UINT32_MAX; 277 if (negative) 278 result = limit = 0; 279 else 280 result = (uint32_t)uint_val; 281 break; 282 283 case 2: // int32 284 if (negative) { 285 result = (int64_t)-uint_val; 286 limit_result = limit = (uint64_t)INT64_MIN; 287 } else { 288 result = (int64_t)uint_val; 289 limit_result = limit = INT64_MAX; 290 } 291 break; 292 293 case 3: // uint64 294 limit = limit_result = UINT64_MAX; 295 if (negative) 296 result = limit = 0; 297 else 298 result = (uint64_t)uint_val; 299 break; 300 301 default: 302 __builtin_unreachable(); 303 } 304 305 if (uint_val > limit) { 306 result = limit_result; 307 softfloat_raiseFlags(softfloat_flag_invalid); 308 } 309 310 SET_FS_DIRTY(); 311 SET_RD(insn, regs, result); 312} 313 314DECLARE_EMULATION_FUNC(emulate_fcmp) 315{ 316 int rm = GET_RM(insn); 317 if (rm >= 3) 318 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 319 320 uintptr_t result; 321 if (GET_PRECISION(insn) == PRECISION_S) { 322 uint32_t rs1 = GET_F32_RS1(insn, regs); 323 uint32_t rs2 = GET_F32_RS2(insn, regs); 324 if (rm != 1) 325 result = f32_eq(f32(rs1), f32(rs2)); 326 if (rm == 1 || (rm == 0 && !result)) 327 result = f32_lt(f32(rs1), f32(rs2)); 328 goto success; 329 } else if (GET_PRECISION(insn) == PRECISION_D) { 330 uint64_t rs1 = GET_F64_RS1(insn, regs); 331 uint64_t rs2 = GET_F64_RS2(insn, regs); 332 if (rm != 1) 333 result = f64_eq(f64(rs1), f64(rs2)); 334 if (rm == 1 || (rm == 0 && !result)) 335 result = f64_lt(f64(rs1), f64(rs2)); 336 goto success; 337 } 338 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 339success: 340 SET_FS_DIRTY(); 341 SET_RD(insn, regs, result); 342} 343 344DECLARE_EMULATION_FUNC(emulate_fmv_if) 345{ 346 uintptr_t result; 347 if ((insn >> 20) & 0x1f) 348 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 349 350 if (GET_PRECISION(insn) == PRECISION_S) { 351 result = GET_F32_RS1(insn, regs); 352 switch (GET_RM(insn)) { 353 case GET_RM(MATCH_FMV_X_W): break; 354 case GET_RM(MATCH_FCLASS_S): result = f32_classify(f32(result)); break; 355 default: return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 356 } 357 } else if (GET_PRECISION(insn) == PRECISION_D) { 358 result = GET_F64_RS1(insn, regs); 359 switch (GET_RM(insn)) { 360 case GET_RM(MATCH_FMV_X_D): break; 361 case GET_RM(MATCH_FCLASS_D): result = f64_classify(f64(result)); break; 362 default: return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 363 } 364 } else { 365 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 366 } 367 368 SET_FS_DIRTY(); 369 SET_RD(insn, regs, result); 370} 371 372DECLARE_EMULATION_FUNC(emulate_fmv_fi) 373{ 374 uintptr_t rs1 = GET_RS1(insn, regs); 375 376 if ((insn & MASK_FMV_W_X) == MATCH_FMV_W_X) 377 SET_F32_RD(insn, regs, rs1); 378#if __riscv_xlen == 64 379 else if ((insn & MASK_FMV_D_X) == MATCH_FMV_D_X) 380 SET_F64_RD(insn, regs, rs1); 381#endif 382 else 383 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 384} 385 386DECLARE_EMULATION_FUNC(emulate_fmadd) 387{ 388 // if FPU is disabled, punt back to the OS 389 if (unlikely((mstatus & MSTATUS_FS) == 0)) 390 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 391 392 bool negA = (insn >> 3) & 1; 393 bool negC = (insn >> 2) & 1; 394 SETUP_STATIC_ROUNDING(insn); 395 if (GET_PRECISION(insn) == PRECISION_S) { 396 uint32_t rs1 = GET_F32_RS1(insn, regs) ^ (negA ? INT32_MIN : 0); 397 uint32_t rs2 = GET_F32_RS2(insn, regs); 398 uint32_t rs3 = GET_F32_RS3(insn, regs) ^ (negC ? INT32_MIN : 0); 399 SET_F32_RD(insn, regs, softfloat_mulAddF32(rs1, rs2, rs3, 0).v); 400 } else if (GET_PRECISION(insn) == PRECISION_D) { 401 uint64_t rs1 = GET_F64_RS1(insn, regs) ^ (negA ? INT64_MIN : 0); 402 uint64_t rs2 = GET_F64_RS2(insn, regs); 403 uint64_t rs3 = GET_F64_RS3(insn, regs) ^ (negC ? INT64_MIN : 0); 404 SET_F64_RD(insn, regs, softfloat_mulAddF64(rs1, rs2, rs3, 0).v); 405 } else { 406 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 407 } 408} 409