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 "cpu.h" 31 #include "exec/helper-proto.h" 32 #include "qemu/host-utils.h" 33 #include "exec/exec-all.h" 34 #include "fpu/softfloat.h" 35 36 enum { 37 XTENSA_FP_I = 0x1, 38 XTENSA_FP_U = 0x2, 39 XTENSA_FP_O = 0x4, 40 XTENSA_FP_Z = 0x8, 41 XTENSA_FP_V = 0x10, 42 }; 43 44 enum { 45 XTENSA_FCR_FLAGS_SHIFT = 2, 46 XTENSA_FSR_FLAGS_SHIFT = 7, 47 }; 48 49 static const struct { 50 uint32_t xtensa_fp_flag; 51 int softfloat_fp_flag; 52 } xtensa_fp_flag_map[] = { 53 { XTENSA_FP_I, float_flag_inexact, }, 54 { XTENSA_FP_U, float_flag_underflow, }, 55 { XTENSA_FP_O, float_flag_overflow, }, 56 { XTENSA_FP_Z, float_flag_divbyzero, }, 57 { XTENSA_FP_V, float_flag_invalid, }, 58 }; 59 60 void xtensa_use_first_nan(CPUXtensaState *env, bool use_first) 61 { 62 set_float_2nan_prop_rule(use_first ? float_2nan_prop_ab : float_2nan_prop_ba, 63 &env->fp_status); 64 set_float_3nan_prop_rule(use_first ? float_3nan_prop_abc : float_3nan_prop_cba, 65 &env->fp_status); 66 } 67 68 void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v) 69 { 70 static const int rounding_mode[] = { 71 float_round_nearest_even, 72 float_round_to_zero, 73 float_round_up, 74 float_round_down, 75 }; 76 77 env->uregs[FCR] = v & 0xfffff07f; 78 set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status); 79 } 80 81 void HELPER(wur_fpu_fcr)(CPUXtensaState *env, uint32_t v) 82 { 83 static const int rounding_mode[] = { 84 float_round_nearest_even, 85 float_round_to_zero, 86 float_round_up, 87 float_round_down, 88 }; 89 90 if (v & 0xfffff000) { 91 qemu_log_mask(LOG_GUEST_ERROR, 92 "MBZ field of FCR is written non-zero: %08x\n", v); 93 } 94 env->uregs[FCR] = v & 0x0000007f; 95 set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status); 96 } 97 98 void HELPER(wur_fpu_fsr)(CPUXtensaState *env, uint32_t v) 99 { 100 uint32_t flags = v >> XTENSA_FSR_FLAGS_SHIFT; 101 int fef = 0; 102 unsigned i; 103 104 if (v & 0xfffff000) { 105 qemu_log_mask(LOG_GUEST_ERROR, 106 "MBZ field of FSR is written non-zero: %08x\n", v); 107 } 108 env->uregs[FSR] = v & 0x00000f80; 109 for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) { 110 if (flags & xtensa_fp_flag_map[i].xtensa_fp_flag) { 111 fef |= xtensa_fp_flag_map[i].softfloat_fp_flag; 112 } 113 } 114 set_float_exception_flags(fef, &env->fp_status); 115 } 116 117 uint32_t HELPER(rur_fpu_fsr)(CPUXtensaState *env) 118 { 119 uint32_t flags = 0; 120 int fef = get_float_exception_flags(&env->fp_status); 121 unsigned i; 122 123 for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) { 124 if (fef & xtensa_fp_flag_map[i].softfloat_fp_flag) { 125 flags |= xtensa_fp_flag_map[i].xtensa_fp_flag; 126 } 127 } 128 env->uregs[FSR] = flags << XTENSA_FSR_FLAGS_SHIFT; 129 return flags << XTENSA_FSR_FLAGS_SHIFT; 130 } 131 132 float64 HELPER(abs_d)(float64 v) 133 { 134 return float64_abs(v); 135 } 136 137 float32 HELPER(abs_s)(float32 v) 138 { 139 return float32_abs(v); 140 } 141 142 float64 HELPER(neg_d)(float64 v) 143 { 144 return float64_chs(v); 145 } 146 147 float32 HELPER(neg_s)(float32 v) 148 { 149 return float32_chs(v); 150 } 151 152 float32 HELPER(fpu2k_add_s)(CPUXtensaState *env, float32 a, float32 b) 153 { 154 return float32_add(a, b, &env->fp_status); 155 } 156 157 float32 HELPER(fpu2k_sub_s)(CPUXtensaState *env, float32 a, float32 b) 158 { 159 return float32_sub(a, b, &env->fp_status); 160 } 161 162 float32 HELPER(fpu2k_mul_s)(CPUXtensaState *env, float32 a, float32 b) 163 { 164 return float32_mul(a, b, &env->fp_status); 165 } 166 167 float32 HELPER(fpu2k_madd_s)(CPUXtensaState *env, 168 float32 a, float32 b, float32 c) 169 { 170 return float32_muladd(b, c, a, 0, &env->fp_status); 171 } 172 173 float32 HELPER(fpu2k_msub_s)(CPUXtensaState *env, 174 float32 a, float32 b, float32 c) 175 { 176 return float32_muladd(b, c, a, float_muladd_negate_product, 177 &env->fp_status); 178 } 179 180 float64 HELPER(add_d)(CPUXtensaState *env, float64 a, float64 b) 181 { 182 xtensa_use_first_nan(env, true); 183 return float64_add(a, b, &env->fp_status); 184 } 185 186 float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b) 187 { 188 xtensa_use_first_nan(env, env->config->use_first_nan); 189 return float32_add(a, b, &env->fp_status); 190 } 191 192 float64 HELPER(sub_d)(CPUXtensaState *env, float64 a, float64 b) 193 { 194 xtensa_use_first_nan(env, true); 195 return float64_sub(a, b, &env->fp_status); 196 } 197 198 float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b) 199 { 200 xtensa_use_first_nan(env, env->config->use_first_nan); 201 return float32_sub(a, b, &env->fp_status); 202 } 203 204 float64 HELPER(mul_d)(CPUXtensaState *env, float64 a, float64 b) 205 { 206 xtensa_use_first_nan(env, true); 207 return float64_mul(a, b, &env->fp_status); 208 } 209 210 float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b) 211 { 212 xtensa_use_first_nan(env, env->config->use_first_nan); 213 return float32_mul(a, b, &env->fp_status); 214 } 215 216 float64 HELPER(madd_d)(CPUXtensaState *env, float64 a, float64 b, float64 c) 217 { 218 xtensa_use_first_nan(env, env->config->use_first_nan); 219 return float64_muladd(b, c, a, 0, &env->fp_status); 220 } 221 222 float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) 223 { 224 xtensa_use_first_nan(env, env->config->use_first_nan); 225 return float32_muladd(b, c, a, 0, &env->fp_status); 226 } 227 228 float64 HELPER(msub_d)(CPUXtensaState *env, float64 a, float64 b, float64 c) 229 { 230 xtensa_use_first_nan(env, env->config->use_first_nan); 231 return float64_muladd(b, c, a, float_muladd_negate_product, 232 &env->fp_status); 233 } 234 235 float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) 236 { 237 xtensa_use_first_nan(env, env->config->use_first_nan); 238 return float32_muladd(b, c, a, float_muladd_negate_product, 239 &env->fp_status); 240 } 241 242 float64 HELPER(mkdadj_d)(CPUXtensaState *env, float64 a, float64 b) 243 { 244 xtensa_use_first_nan(env, true); 245 return float64_div(b, a, &env->fp_status); 246 } 247 248 float32 HELPER(mkdadj_s)(CPUXtensaState *env, float32 a, float32 b) 249 { 250 xtensa_use_first_nan(env, env->config->use_first_nan); 251 return float32_div(b, a, &env->fp_status); 252 } 253 254 float64 HELPER(mksadj_d)(CPUXtensaState *env, float64 v) 255 { 256 xtensa_use_first_nan(env, true); 257 return float64_sqrt(v, &env->fp_status); 258 } 259 260 float32 HELPER(mksadj_s)(CPUXtensaState *env, float32 v) 261 { 262 xtensa_use_first_nan(env, env->config->use_first_nan); 263 return float32_sqrt(v, &env->fp_status); 264 } 265 266 uint32_t HELPER(ftoi_d)(CPUXtensaState *env, float64 v, 267 uint32_t rounding_mode, uint32_t scale) 268 { 269 float_status fp_status = env->fp_status; 270 uint32_t res; 271 272 set_float_rounding_mode(rounding_mode, &fp_status); 273 res = float64_to_int32(float64_scalbn(v, scale, &fp_status), &fp_status); 274 set_float_exception_flags(get_float_exception_flags(&fp_status), 275 &env->fp_status); 276 return res; 277 } 278 279 uint32_t HELPER(ftoi_s)(CPUXtensaState *env, float32 v, 280 uint32_t rounding_mode, uint32_t scale) 281 { 282 float_status fp_status = env->fp_status; 283 uint32_t res; 284 285 set_float_rounding_mode(rounding_mode, &fp_status); 286 res = float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status); 287 set_float_exception_flags(get_float_exception_flags(&fp_status), 288 &env->fp_status); 289 return res; 290 } 291 292 uint32_t HELPER(ftoui_d)(CPUXtensaState *env, float64 v, 293 uint32_t rounding_mode, uint32_t scale) 294 { 295 float_status fp_status = env->fp_status; 296 float64 res; 297 uint32_t rv; 298 299 set_float_rounding_mode(rounding_mode, &fp_status); 300 301 res = float64_scalbn(v, scale, &fp_status); 302 303 if (float64_is_neg(v) && !float64_is_any_nan(v)) { 304 set_float_exception_flags(float_flag_invalid, &fp_status); 305 rv = float64_to_int32(res, &fp_status); 306 } else { 307 rv = float64_to_uint32(res, &fp_status); 308 } 309 set_float_exception_flags(get_float_exception_flags(&fp_status), 310 &env->fp_status); 311 return rv; 312 } 313 314 uint32_t HELPER(ftoui_s)(CPUXtensaState *env, float32 v, 315 uint32_t rounding_mode, uint32_t scale) 316 { 317 float_status fp_status = env->fp_status; 318 float32 res; 319 uint32_t rv; 320 321 set_float_rounding_mode(rounding_mode, &fp_status); 322 323 res = float32_scalbn(v, scale, &fp_status); 324 325 if (float32_is_neg(v) && !float32_is_any_nan(v)) { 326 rv = float32_to_int32(res, &fp_status); 327 if (rv) { 328 set_float_exception_flags(float_flag_invalid, &fp_status); 329 } 330 } else { 331 rv = float32_to_uint32(res, &fp_status); 332 } 333 set_float_exception_flags(get_float_exception_flags(&fp_status), 334 &env->fp_status); 335 return rv; 336 } 337 338 float64 HELPER(itof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale) 339 { 340 return float64_scalbn(int32_to_float64(v, &env->fp_status), 341 (int32_t)scale, &env->fp_status); 342 } 343 344 float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale) 345 { 346 return float32_scalbn(int32_to_float32(v, &env->fp_status), 347 (int32_t)scale, &env->fp_status); 348 } 349 350 float64 HELPER(uitof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale) 351 { 352 return float64_scalbn(uint32_to_float64(v, &env->fp_status), 353 (int32_t)scale, &env->fp_status); 354 } 355 356 float32 HELPER(uitof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale) 357 { 358 return float32_scalbn(uint32_to_float32(v, &env->fp_status), 359 (int32_t)scale, &env->fp_status); 360 } 361 362 float64 HELPER(cvtd_s)(CPUXtensaState *env, float32 v) 363 { 364 return float32_to_float64(v, &env->fp_status); 365 } 366 367 float32 HELPER(cvts_d)(CPUXtensaState *env, float64 v) 368 { 369 return float64_to_float32(v, &env->fp_status); 370 } 371 372 uint32_t HELPER(un_d)(CPUXtensaState *env, float64 a, float64 b) 373 { 374 return float64_unordered_quiet(a, b, &env->fp_status); 375 } 376 377 uint32_t HELPER(un_s)(CPUXtensaState *env, float32 a, float32 b) 378 { 379 return float32_unordered_quiet(a, b, &env->fp_status); 380 } 381 382 uint32_t HELPER(oeq_d)(CPUXtensaState *env, float64 a, float64 b) 383 { 384 return float64_eq_quiet(a, b, &env->fp_status); 385 } 386 387 uint32_t HELPER(oeq_s)(CPUXtensaState *env, float32 a, float32 b) 388 { 389 return float32_eq_quiet(a, b, &env->fp_status); 390 } 391 392 uint32_t HELPER(ueq_d)(CPUXtensaState *env, float64 a, float64 b) 393 { 394 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); 395 396 return v == float_relation_equal || 397 v == float_relation_unordered; 398 } 399 400 uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b) 401 { 402 FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); 403 404 return v == float_relation_equal || 405 v == float_relation_unordered; 406 } 407 408 uint32_t HELPER(olt_d)(CPUXtensaState *env, float64 a, float64 b) 409 { 410 return float64_lt(a, b, &env->fp_status); 411 } 412 413 uint32_t HELPER(olt_s)(CPUXtensaState *env, float32 a, float32 b) 414 { 415 return float32_lt(a, b, &env->fp_status); 416 } 417 418 uint32_t HELPER(ult_d)(CPUXtensaState *env, float64 a, float64 b) 419 { 420 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); 421 422 return v == float_relation_less || 423 v == float_relation_unordered; 424 } 425 426 uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b) 427 { 428 FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); 429 430 return v == float_relation_less || 431 v == float_relation_unordered; 432 } 433 434 uint32_t HELPER(ole_d)(CPUXtensaState *env, float64 a, float64 b) 435 { 436 return float64_le(a, b, &env->fp_status); 437 } 438 439 uint32_t HELPER(ole_s)(CPUXtensaState *env, float32 a, float32 b) 440 { 441 return float32_le(a, b, &env->fp_status); 442 } 443 444 uint32_t HELPER(ule_d)(CPUXtensaState *env, float64 a, float64 b) 445 { 446 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); 447 448 return v != float_relation_greater; 449 } 450 451 uint32_t HELPER(ule_s)(CPUXtensaState *env, float32 a, float32 b) 452 { 453 FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); 454 455 return v != float_relation_greater; 456 } 457