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