1 /* 2 * TriCore emulation for qemu: fpu helper. 3 * 4 * Copyright (c) 2016 Bastian Koppelmann University of Paderborn 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "qemu/osdep.h" 21 #include "cpu.h" 22 #include "exec/helper-proto.h" 23 24 #define QUIET_NAN 0x7fc00000 25 #define ADD_NAN 0x7fc00001 26 #define DIV_NAN 0x7fc00008 27 #define MUL_NAN 0x7fc00002 28 #define FPU_FS PSW_USB_C 29 #define FPU_FI PSW_USB_V 30 #define FPU_FV PSW_USB_SV 31 #define FPU_FZ PSW_USB_AV 32 #define FPU_FU PSW_USB_SAV 33 34 /* we don't care about input_denormal */ 35 static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) 36 { 37 return get_float_exception_flags(&env->fp_status) 38 & (float_flag_invalid 39 | float_flag_overflow 40 | float_flag_underflow 41 | float_flag_output_denormal 42 | float_flag_divbyzero 43 | float_flag_inexact); 44 } 45 46 static inline bool f_is_denormal(float32 arg) 47 { 48 return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg); 49 } 50 51 static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2, 52 float32 arg3, float32 result, 53 uint32_t muladd_negate_c) 54 { 55 uint32_t aSign, bSign, cSign; 56 uint32_t aExp, bExp, cExp; 57 58 if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) || 59 float32_is_any_nan(arg3)) { 60 return QUIET_NAN; 61 } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) { 62 return MUL_NAN; 63 } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) { 64 return MUL_NAN; 65 } else { 66 aSign = arg1 >> 31; 67 bSign = arg2 >> 31; 68 cSign = arg3 >> 31; 69 70 aExp = (arg1 >> 23) & 0xff; 71 bExp = (arg2 >> 23) & 0xff; 72 cExp = (arg3 >> 23) & 0xff; 73 74 if (muladd_negate_c) { 75 cSign ^= 1; 76 } 77 if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) { 78 if (aSign ^ bSign ^ cSign) { 79 return ADD_NAN; 80 } 81 } 82 } 83 84 return result; 85 } 86 87 static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) 88 { 89 uint8_t some_excp = 0; 90 set_float_exception_flags(0, &env->fp_status); 91 92 if (flags & float_flag_invalid) { 93 env->FPU_FI = 1 << 31; 94 some_excp = 1; 95 } 96 97 if (flags & float_flag_overflow) { 98 env->FPU_FV = 1 << 31; 99 some_excp = 1; 100 } 101 102 if (flags & float_flag_underflow || flags & float_flag_output_denormal) { 103 env->FPU_FU = 1 << 31; 104 some_excp = 1; 105 } 106 107 if (flags & float_flag_divbyzero) { 108 env->FPU_FZ = 1 << 31; 109 some_excp = 1; 110 } 111 112 if (flags & float_flag_inexact || flags & float_flag_output_denormal) { 113 env->PSW |= 1 << 26; 114 some_excp = 1; 115 } 116 117 env->FPU_FS = some_excp; 118 } 119 120 #define FADD_SUB(op) \ 121 uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2) \ 122 { \ 123 float32 arg1 = make_float32(r1); \ 124 float32 arg2 = make_float32(r2); \ 125 uint32_t flags; \ 126 float32 f_result; \ 127 \ 128 f_result = float32_##op(arg2, arg1, &env->fp_status); \ 129 flags = f_get_excp_flags(env); \ 130 if (flags) { \ 131 /* If the output is a NaN, but the inputs aren't, \ 132 we return a unique value. */ \ 133 if ((flags & float_flag_invalid) \ 134 && !float32_is_any_nan(arg1) \ 135 && !float32_is_any_nan(arg2)) { \ 136 f_result = ADD_NAN; \ 137 } \ 138 f_update_psw_flags(env, flags); \ 139 } else { \ 140 env->FPU_FS = 0; \ 141 } \ 142 return (uint32_t)f_result; \ 143 } 144 FADD_SUB(add) 145 FADD_SUB(sub) 146 147 uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 148 { 149 uint32_t flags; 150 float32 arg1 = make_float32(r1); 151 float32 arg2 = make_float32(r2); 152 float32 f_result; 153 154 f_result = float32_mul(arg1, arg2, &env->fp_status); 155 156 flags = f_get_excp_flags(env); 157 if (flags) { 158 /* If the output is a NaN, but the inputs aren't, 159 we return a unique value. */ 160 if ((flags & float_flag_invalid) 161 && !float32_is_any_nan(arg1) 162 && !float32_is_any_nan(arg2)) { 163 f_result = MUL_NAN; 164 } 165 f_update_psw_flags(env, flags); 166 } else { 167 env->FPU_FS = 0; 168 } 169 return (uint32_t)f_result; 170 171 } 172 173 uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 174 { 175 uint32_t flags; 176 float32 arg1 = make_float32(r1); 177 float32 arg2 = make_float32(r2); 178 float32 f_result; 179 180 f_result = float32_div(arg1, arg2 , &env->fp_status); 181 182 flags = f_get_excp_flags(env); 183 if (flags) { 184 /* If the output is a NaN, but the inputs aren't, 185 we return a unique value. */ 186 if ((flags & float_flag_invalid) 187 && !float32_is_any_nan(arg1) 188 && !float32_is_any_nan(arg2)) { 189 f_result = DIV_NAN; 190 } 191 f_update_psw_flags(env, flags); 192 } else { 193 env->FPU_FS = 0; 194 } 195 196 return (uint32_t)f_result; 197 } 198 199 uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1, 200 uint32_t r2, uint32_t r3) 201 { 202 uint32_t flags; 203 float32 arg1 = make_float32(r1); 204 float32 arg2 = make_float32(r2); 205 float32 arg3 = make_float32(r3); 206 float32 f_result; 207 208 f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status); 209 210 flags = f_get_excp_flags(env); 211 if (flags) { 212 if (flags & float_flag_invalid) { 213 arg1 = float32_squash_input_denormal(arg1, &env->fp_status); 214 arg2 = float32_squash_input_denormal(arg2, &env->fp_status); 215 arg3 = float32_squash_input_denormal(arg3, &env->fp_status); 216 f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0); 217 } 218 f_update_psw_flags(env, flags); 219 } else { 220 env->FPU_FS = 0; 221 } 222 return (uint32_t)f_result; 223 } 224 225 uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1, 226 uint32_t r2, uint32_t r3) 227 { 228 uint32_t flags; 229 float32 arg1 = make_float32(r1); 230 float32 arg2 = make_float32(r2); 231 float32 arg3 = make_float32(r3); 232 float32 f_result; 233 234 f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product, 235 &env->fp_status); 236 237 flags = f_get_excp_flags(env); 238 if (flags) { 239 if (flags & float_flag_invalid) { 240 arg1 = float32_squash_input_denormal(arg1, &env->fp_status); 241 arg2 = float32_squash_input_denormal(arg2, &env->fp_status); 242 arg3 = float32_squash_input_denormal(arg3, &env->fp_status); 243 244 f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1); 245 } 246 f_update_psw_flags(env, flags); 247 } else { 248 env->FPU_FS = 0; 249 } 250 return (uint32_t)f_result; 251 } 252 253 uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 254 { 255 uint32_t result, flags; 256 float32 arg1 = make_float32(r1); 257 float32 arg2 = make_float32(r2); 258 259 set_flush_inputs_to_zero(0, &env->fp_status); 260 261 result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1); 262 result |= f_is_denormal(arg1) << 4; 263 result |= f_is_denormal(arg2) << 5; 264 265 flags = f_get_excp_flags(env); 266 if (flags) { 267 f_update_psw_flags(env, flags); 268 } else { 269 env->FPU_FS = 0; 270 } 271 272 set_flush_inputs_to_zero(1, &env->fp_status); 273 return result; 274 } 275 276 uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) 277 { 278 float32 f_arg = make_float32(arg); 279 int32_t result, flags; 280 281 result = float32_to_int32(f_arg, &env->fp_status); 282 283 flags = f_get_excp_flags(env); 284 if (flags) { 285 if (float32_is_any_nan(f_arg)) { 286 result = 0; 287 } 288 f_update_psw_flags(env, flags); 289 } else { 290 env->FPU_FS = 0; 291 } 292 return (uint32_t)result; 293 } 294 295 uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) 296 { 297 float32 f_result; 298 uint32_t flags; 299 f_result = int32_to_float32(arg, &env->fp_status); 300 301 flags = f_get_excp_flags(env); 302 if (flags) { 303 f_update_psw_flags(env, flags); 304 } else { 305 env->FPU_FS = 0; 306 } 307 return (uint32_t)f_result; 308 } 309 310 uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg) 311 { 312 float32 f_arg = make_float32(arg); 313 uint32_t result; 314 int32_t flags; 315 316 result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status); 317 318 flags = f_get_excp_flags(env); 319 if (flags & float_flag_invalid) { 320 flags &= ~float_flag_inexact; 321 if (float32_is_any_nan(f_arg)) { 322 result = 0; 323 } 324 } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) { 325 flags = float_flag_invalid; 326 result = 0; 327 } 328 329 if (flags) { 330 f_update_psw_flags(env, flags); 331 } else { 332 env->FPU_FS = 0; 333 } 334 return result; 335 } 336 337 void helper_updfl(CPUTriCoreState *env, uint32_t arg) 338 { 339 env->FPU_FS = extract32(arg, 7, 1) & extract32(arg, 15, 1); 340 env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31; 341 env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31; 342 env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31; 343 env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31; 344 /* clear FX and RM */ 345 env->PSW &= ~(extract32(arg, 10, 1) << 26); 346 env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26; 347 348 fpu_set_state(env); 349 } 350