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