1 /* 2 * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * * Neither the name of the Open Source and Linux Lab nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "qemu/osdep.h" 29 #include "qemu/log.h" 30 #include "qemu/main-loop.h" 31 #include "cpu.h" 32 #include "exec/helper-proto.h" 33 #include "qemu/host-utils.h" 34 #include "exec/exec-all.h" 35 #include "fpu/softfloat.h" 36 37 enum { 38 XTENSA_FP_I = 0x1, 39 XTENSA_FP_U = 0x2, 40 XTENSA_FP_O = 0x4, 41 XTENSA_FP_Z = 0x8, 42 XTENSA_FP_V = 0x10, 43 }; 44 45 enum { 46 XTENSA_FCR_FLAGS_SHIFT = 2, 47 XTENSA_FSR_FLAGS_SHIFT = 7, 48 }; 49 50 static const struct { 51 uint32_t xtensa_fp_flag; 52 int softfloat_fp_flag; 53 } xtensa_fp_flag_map[] = { 54 { XTENSA_FP_I, float_flag_inexact, }, 55 { XTENSA_FP_U, float_flag_underflow, }, 56 { XTENSA_FP_O, float_flag_overflow, }, 57 { XTENSA_FP_Z, float_flag_divbyzero, }, 58 { XTENSA_FP_V, float_flag_invalid, }, 59 }; 60 61 void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v) 62 { 63 static const int rounding_mode[] = { 64 float_round_nearest_even, 65 float_round_to_zero, 66 float_round_up, 67 float_round_down, 68 }; 69 70 env->uregs[FCR] = v & 0xfffff07f; 71 set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status); 72 } 73 74 void HELPER(wur_fpu_fcr)(CPUXtensaState *env, uint32_t v) 75 { 76 static const int rounding_mode[] = { 77 float_round_nearest_even, 78 float_round_to_zero, 79 float_round_up, 80 float_round_down, 81 }; 82 83 if (v & 0xfffff000) { 84 qemu_log_mask(LOG_GUEST_ERROR, 85 "MBZ field of FCR is written non-zero: %08x\n", v); 86 } 87 env->uregs[FCR] = v & 0x0000007f; 88 set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status); 89 } 90 91 void HELPER(wur_fpu_fsr)(CPUXtensaState *env, uint32_t v) 92 { 93 uint32_t flags = v >> XTENSA_FSR_FLAGS_SHIFT; 94 int fef = 0; 95 unsigned i; 96 97 if (v & 0xfffff000) { 98 qemu_log_mask(LOG_GUEST_ERROR, 99 "MBZ field of FSR is written non-zero: %08x\n", v); 100 } 101 env->uregs[FSR] = v & 0x00000f80; 102 for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) { 103 if (flags & xtensa_fp_flag_map[i].xtensa_fp_flag) { 104 fef |= xtensa_fp_flag_map[i].softfloat_fp_flag; 105 } 106 } 107 set_float_exception_flags(fef, &env->fp_status); 108 } 109 110 uint32_t HELPER(rur_fpu_fsr)(CPUXtensaState *env) 111 { 112 uint32_t flags = 0; 113 int fef = get_float_exception_flags(&env->fp_status); 114 unsigned i; 115 116 for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) { 117 if (fef & xtensa_fp_flag_map[i].softfloat_fp_flag) { 118 flags |= xtensa_fp_flag_map[i].xtensa_fp_flag; 119 } 120 } 121 env->uregs[FSR] = flags << XTENSA_FSR_FLAGS_SHIFT; 122 return flags << XTENSA_FSR_FLAGS_SHIFT; 123 } 124 125 float64 HELPER(abs_d)(float64 v) 126 { 127 return float64_abs(v); 128 } 129 130 float32 HELPER(abs_s)(float32 v) 131 { 132 return float32_abs(v); 133 } 134 135 float64 HELPER(neg_d)(float64 v) 136 { 137 return float64_chs(v); 138 } 139 140 float32 HELPER(neg_s)(float32 v) 141 { 142 return float32_chs(v); 143 } 144 145 float32 HELPER(fpu2k_add_s)(CPUXtensaState *env, float32 a, float32 b) 146 { 147 return float32_add(a, b, &env->fp_status); 148 } 149 150 float32 HELPER(fpu2k_sub_s)(CPUXtensaState *env, float32 a, float32 b) 151 { 152 return float32_sub(a, b, &env->fp_status); 153 } 154 155 float32 HELPER(fpu2k_mul_s)(CPUXtensaState *env, float32 a, float32 b) 156 { 157 return float32_mul(a, b, &env->fp_status); 158 } 159 160 float32 HELPER(fpu2k_madd_s)(CPUXtensaState *env, 161 float32 a, float32 b, float32 c) 162 { 163 return float32_muladd(b, c, a, 0, &env->fp_status); 164 } 165 166 float32 HELPER(fpu2k_msub_s)(CPUXtensaState *env, 167 float32 a, float32 b, float32 c) 168 { 169 return float32_muladd(b, c, a, float_muladd_negate_product, 170 &env->fp_status); 171 } 172 173 float64 HELPER(add_d)(CPUXtensaState *env, float64 a, float64 b) 174 { 175 set_use_first_nan(true, &env->fp_status); 176 return float64_add(a, b, &env->fp_status); 177 } 178 179 float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b) 180 { 181 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 182 return float32_add(a, b, &env->fp_status); 183 } 184 185 float64 HELPER(sub_d)(CPUXtensaState *env, float64 a, float64 b) 186 { 187 set_use_first_nan(true, &env->fp_status); 188 return float64_sub(a, b, &env->fp_status); 189 } 190 191 float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b) 192 { 193 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 194 return float32_sub(a, b, &env->fp_status); 195 } 196 197 float64 HELPER(mul_d)(CPUXtensaState *env, float64 a, float64 b) 198 { 199 set_use_first_nan(true, &env->fp_status); 200 return float64_mul(a, b, &env->fp_status); 201 } 202 203 float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b) 204 { 205 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 206 return float32_mul(a, b, &env->fp_status); 207 } 208 209 float64 HELPER(madd_d)(CPUXtensaState *env, float64 a, float64 b, float64 c) 210 { 211 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 212 return float64_muladd(b, c, a, 0, &env->fp_status); 213 } 214 215 float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) 216 { 217 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 218 return float32_muladd(b, c, a, 0, &env->fp_status); 219 } 220 221 float64 HELPER(msub_d)(CPUXtensaState *env, float64 a, float64 b, float64 c) 222 { 223 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 224 return float64_muladd(b, c, a, float_muladd_negate_product, 225 &env->fp_status); 226 } 227 228 float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) 229 { 230 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 231 return float32_muladd(b, c, a, float_muladd_negate_product, 232 &env->fp_status); 233 } 234 235 float64 HELPER(mkdadj_d)(CPUXtensaState *env, float64 a, float64 b) 236 { 237 set_use_first_nan(true, &env->fp_status); 238 return float64_div(b, a, &env->fp_status); 239 } 240 241 float32 HELPER(mkdadj_s)(CPUXtensaState *env, float32 a, float32 b) 242 { 243 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 244 return float32_div(b, a, &env->fp_status); 245 } 246 247 float64 HELPER(mksadj_d)(CPUXtensaState *env, float64 v) 248 { 249 set_use_first_nan(true, &env->fp_status); 250 return float64_sqrt(v, &env->fp_status); 251 } 252 253 float32 HELPER(mksadj_s)(CPUXtensaState *env, float32 v) 254 { 255 set_use_first_nan(env->config->use_first_nan, &env->fp_status); 256 return float32_sqrt(v, &env->fp_status); 257 } 258 259 uint32_t HELPER(ftoi_d)(CPUXtensaState *env, float64 v, 260 uint32_t rounding_mode, uint32_t scale) 261 { 262 float_status fp_status = env->fp_status; 263 uint32_t res; 264 265 set_float_rounding_mode(rounding_mode, &fp_status); 266 res = float64_to_int32(float64_scalbn(v, scale, &fp_status), &fp_status); 267 set_float_exception_flags(get_float_exception_flags(&fp_status), 268 &env->fp_status); 269 return res; 270 } 271 272 uint32_t HELPER(ftoi_s)(CPUXtensaState *env, float32 v, 273 uint32_t rounding_mode, uint32_t scale) 274 { 275 float_status fp_status = env->fp_status; 276 uint32_t res; 277 278 set_float_rounding_mode(rounding_mode, &fp_status); 279 res = float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status); 280 set_float_exception_flags(get_float_exception_flags(&fp_status), 281 &env->fp_status); 282 return res; 283 } 284 285 uint32_t HELPER(ftoui_d)(CPUXtensaState *env, float64 v, 286 uint32_t rounding_mode, uint32_t scale) 287 { 288 float_status fp_status = env->fp_status; 289 float64 res; 290 uint32_t rv; 291 292 set_float_rounding_mode(rounding_mode, &fp_status); 293 294 res = float64_scalbn(v, scale, &fp_status); 295 296 if (float64_is_neg(v) && !float64_is_any_nan(v)) { 297 set_float_exception_flags(float_flag_invalid, &fp_status); 298 rv = float64_to_int32(res, &fp_status); 299 } else { 300 rv = float64_to_uint32(res, &fp_status); 301 } 302 set_float_exception_flags(get_float_exception_flags(&fp_status), 303 &env->fp_status); 304 return rv; 305 } 306 307 uint32_t HELPER(ftoui_s)(CPUXtensaState *env, float32 v, 308 uint32_t rounding_mode, uint32_t scale) 309 { 310 float_status fp_status = env->fp_status; 311 float32 res; 312 uint32_t rv; 313 314 set_float_rounding_mode(rounding_mode, &fp_status); 315 316 res = float32_scalbn(v, scale, &fp_status); 317 318 if (float32_is_neg(v) && !float32_is_any_nan(v)) { 319 rv = float32_to_int32(res, &fp_status); 320 if (rv) { 321 set_float_exception_flags(float_flag_invalid, &fp_status); 322 } 323 } else { 324 rv = float32_to_uint32(res, &fp_status); 325 } 326 set_float_exception_flags(get_float_exception_flags(&fp_status), 327 &env->fp_status); 328 return rv; 329 } 330 331 float64 HELPER(itof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale) 332 { 333 return float64_scalbn(int32_to_float64(v, &env->fp_status), 334 (int32_t)scale, &env->fp_status); 335 } 336 337 float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale) 338 { 339 return float32_scalbn(int32_to_float32(v, &env->fp_status), 340 (int32_t)scale, &env->fp_status); 341 } 342 343 float64 HELPER(uitof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale) 344 { 345 return float64_scalbn(uint32_to_float64(v, &env->fp_status), 346 (int32_t)scale, &env->fp_status); 347 } 348 349 float32 HELPER(uitof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale) 350 { 351 return float32_scalbn(uint32_to_float32(v, &env->fp_status), 352 (int32_t)scale, &env->fp_status); 353 } 354 355 float64 HELPER(cvtd_s)(CPUXtensaState *env, float32 v) 356 { 357 return float32_to_float64(v, &env->fp_status); 358 } 359 360 float32 HELPER(cvts_d)(CPUXtensaState *env, float64 v) 361 { 362 return float64_to_float32(v, &env->fp_status); 363 } 364 365 uint32_t HELPER(un_d)(CPUXtensaState *env, float64 a, float64 b) 366 { 367 return float64_unordered_quiet(a, b, &env->fp_status); 368 } 369 370 uint32_t HELPER(un_s)(CPUXtensaState *env, float32 a, float32 b) 371 { 372 return float32_unordered_quiet(a, b, &env->fp_status); 373 } 374 375 uint32_t HELPER(oeq_d)(CPUXtensaState *env, float64 a, float64 b) 376 { 377 return float64_eq_quiet(a, b, &env->fp_status); 378 } 379 380 uint32_t HELPER(oeq_s)(CPUXtensaState *env, float32 a, float32 b) 381 { 382 return float32_eq_quiet(a, b, &env->fp_status); 383 } 384 385 uint32_t HELPER(ueq_d)(CPUXtensaState *env, float64 a, float64 b) 386 { 387 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); 388 389 return v == float_relation_equal || 390 v == float_relation_unordered; 391 } 392 393 uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b) 394 { 395 FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); 396 397 return v == float_relation_equal || 398 v == float_relation_unordered; 399 } 400 401 uint32_t HELPER(olt_d)(CPUXtensaState *env, float64 a, float64 b) 402 { 403 return float64_lt(a, b, &env->fp_status); 404 } 405 406 uint32_t HELPER(olt_s)(CPUXtensaState *env, float32 a, float32 b) 407 { 408 return float32_lt(a, b, &env->fp_status); 409 } 410 411 uint32_t HELPER(ult_d)(CPUXtensaState *env, float64 a, float64 b) 412 { 413 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); 414 415 return v == float_relation_less || 416 v == float_relation_unordered; 417 } 418 419 uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b) 420 { 421 FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); 422 423 return v == float_relation_less || 424 v == float_relation_unordered; 425 } 426 427 uint32_t HELPER(ole_d)(CPUXtensaState *env, float64 a, float64 b) 428 { 429 return float64_le(a, b, &env->fp_status); 430 } 431 432 uint32_t HELPER(ole_s)(CPUXtensaState *env, float32 a, float32 b) 433 { 434 return float32_le(a, b, &env->fp_status); 435 } 436 437 uint32_t HELPER(ule_d)(CPUXtensaState *env, float64 a, float64 b) 438 { 439 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); 440 441 return v != float_relation_greater; 442 } 443 444 uint32_t HELPER(ule_s)(CPUXtensaState *env, float32 a, float32 b) 445 { 446 FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); 447 448 return v != float_relation_greater; 449 } 450