13a0eae85SDavid Hildenbrand /* 23a0eae85SDavid Hildenbrand * QEMU TCG support -- s390x vector floating point instruction support 33a0eae85SDavid Hildenbrand * 43a0eae85SDavid Hildenbrand * Copyright (C) 2019 Red Hat Inc 53a0eae85SDavid Hildenbrand * 63a0eae85SDavid Hildenbrand * Authors: 73a0eae85SDavid Hildenbrand * David Hildenbrand <david@redhat.com> 83a0eae85SDavid Hildenbrand * 93a0eae85SDavid Hildenbrand * This work is licensed under the terms of the GNU GPL, version 2 or later. 103a0eae85SDavid Hildenbrand * See the COPYING file in the top-level directory. 113a0eae85SDavid Hildenbrand */ 123a0eae85SDavid Hildenbrand #include "qemu/osdep.h" 133a0eae85SDavid Hildenbrand #include "qemu-common.h" 143a0eae85SDavid Hildenbrand #include "cpu.h" 153a0eae85SDavid Hildenbrand #include "internal.h" 163a0eae85SDavid Hildenbrand #include "vec.h" 173a0eae85SDavid Hildenbrand #include "tcg_s390x.h" 183a0eae85SDavid Hildenbrand #include "tcg/tcg-gvec-desc.h" 193a0eae85SDavid Hildenbrand #include "exec/exec-all.h" 203a0eae85SDavid Hildenbrand #include "exec/helper-proto.h" 213a0eae85SDavid Hildenbrand #include "fpu/softfloat.h" 223a0eae85SDavid Hildenbrand 233a0eae85SDavid Hildenbrand #define VIC_INVALID 0x1 243a0eae85SDavid Hildenbrand #define VIC_DIVBYZERO 0x2 253a0eae85SDavid Hildenbrand #define VIC_OVERFLOW 0x3 263a0eae85SDavid Hildenbrand #define VIC_UNDERFLOW 0x4 273a0eae85SDavid Hildenbrand #define VIC_INEXACT 0x5 283a0eae85SDavid Hildenbrand 293a0eae85SDavid Hildenbrand /* returns the VEX. If the VEX is 0, there is no trap */ 303a0eae85SDavid Hildenbrand static uint8_t check_ieee_exc(CPUS390XState *env, uint8_t enr, bool XxC, 313a0eae85SDavid Hildenbrand uint8_t *vec_exc) 323a0eae85SDavid Hildenbrand { 333a0eae85SDavid Hildenbrand uint8_t vece_exc = 0, trap_exc; 343a0eae85SDavid Hildenbrand unsigned qemu_exc; 353a0eae85SDavid Hildenbrand 363a0eae85SDavid Hildenbrand /* Retrieve and clear the softfloat exceptions */ 373a0eae85SDavid Hildenbrand qemu_exc = env->fpu_status.float_exception_flags; 383a0eae85SDavid Hildenbrand if (qemu_exc == 0) { 393a0eae85SDavid Hildenbrand return 0; 403a0eae85SDavid Hildenbrand } 413a0eae85SDavid Hildenbrand env->fpu_status.float_exception_flags = 0; 423a0eae85SDavid Hildenbrand 433a0eae85SDavid Hildenbrand vece_exc = s390_softfloat_exc_to_ieee(qemu_exc); 443a0eae85SDavid Hildenbrand 453a0eae85SDavid Hildenbrand /* Add them to the vector-wide s390x exception bits */ 463a0eae85SDavid Hildenbrand *vec_exc |= vece_exc; 473a0eae85SDavid Hildenbrand 483a0eae85SDavid Hildenbrand /* Check for traps and construct the VXC */ 493a0eae85SDavid Hildenbrand trap_exc = vece_exc & env->fpc >> 24; 503a0eae85SDavid Hildenbrand if (trap_exc) { 513a0eae85SDavid Hildenbrand if (trap_exc & S390_IEEE_MASK_INVALID) { 523a0eae85SDavid Hildenbrand return enr << 4 | VIC_INVALID; 533a0eae85SDavid Hildenbrand } else if (trap_exc & S390_IEEE_MASK_DIVBYZERO) { 543a0eae85SDavid Hildenbrand return enr << 4 | VIC_DIVBYZERO; 553a0eae85SDavid Hildenbrand } else if (trap_exc & S390_IEEE_MASK_OVERFLOW) { 563a0eae85SDavid Hildenbrand return enr << 4 | VIC_OVERFLOW; 573a0eae85SDavid Hildenbrand } else if (trap_exc & S390_IEEE_MASK_UNDERFLOW) { 583a0eae85SDavid Hildenbrand return enr << 4 | VIC_UNDERFLOW; 593a0eae85SDavid Hildenbrand } else if (!XxC) { 603a0eae85SDavid Hildenbrand g_assert(trap_exc & S390_IEEE_MASK_INEXACT); 613a0eae85SDavid Hildenbrand /* inexact has lowest priority on traps */ 623a0eae85SDavid Hildenbrand return enr << 4 | VIC_INEXACT; 633a0eae85SDavid Hildenbrand } 643a0eae85SDavid Hildenbrand } 653a0eae85SDavid Hildenbrand return 0; 663a0eae85SDavid Hildenbrand } 673a0eae85SDavid Hildenbrand 683a0eae85SDavid Hildenbrand static void handle_ieee_exc(CPUS390XState *env, uint8_t vxc, uint8_t vec_exc, 693a0eae85SDavid Hildenbrand uintptr_t retaddr) 703a0eae85SDavid Hildenbrand { 713a0eae85SDavid Hildenbrand if (vxc) { 723a0eae85SDavid Hildenbrand /* on traps, the fpc flags are not updated, instruction is suppressed */ 733a0eae85SDavid Hildenbrand tcg_s390_vector_exception(env, vxc, retaddr); 743a0eae85SDavid Hildenbrand } 753a0eae85SDavid Hildenbrand if (vec_exc) { 763a0eae85SDavid Hildenbrand /* indicate exceptions for all elements combined */ 773a0eae85SDavid Hildenbrand env->fpc |= vec_exc << 16; 783a0eae85SDavid Hildenbrand } 793a0eae85SDavid Hildenbrand } 803a0eae85SDavid Hildenbrand 813a0eae85SDavid Hildenbrand typedef uint64_t (*vop64_3_fn)(uint64_t a, uint64_t b, float_status *s); 823a0eae85SDavid Hildenbrand static void vop64_3(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, 833a0eae85SDavid Hildenbrand CPUS390XState *env, bool s, vop64_3_fn fn, 843a0eae85SDavid Hildenbrand uintptr_t retaddr) 853a0eae85SDavid Hildenbrand { 863a0eae85SDavid Hildenbrand uint8_t vxc, vec_exc = 0; 873a0eae85SDavid Hildenbrand S390Vector tmp = {}; 883a0eae85SDavid Hildenbrand int i; 893a0eae85SDavid Hildenbrand 903a0eae85SDavid Hildenbrand for (i = 0; i < 2; i++) { 913a0eae85SDavid Hildenbrand const uint64_t a = s390_vec_read_element64(v2, i); 923a0eae85SDavid Hildenbrand const uint64_t b = s390_vec_read_element64(v3, i); 933a0eae85SDavid Hildenbrand 943a0eae85SDavid Hildenbrand s390_vec_write_element64(&tmp, i, fn(a, b, &env->fpu_status)); 953a0eae85SDavid Hildenbrand vxc = check_ieee_exc(env, i, false, &vec_exc); 963a0eae85SDavid Hildenbrand if (s || vxc) { 973a0eae85SDavid Hildenbrand break; 983a0eae85SDavid Hildenbrand } 993a0eae85SDavid Hildenbrand } 1003a0eae85SDavid Hildenbrand handle_ieee_exc(env, vxc, vec_exc, retaddr); 1013a0eae85SDavid Hildenbrand *v1 = tmp; 1023a0eae85SDavid Hildenbrand } 1033a0eae85SDavid Hildenbrand 1043a0eae85SDavid Hildenbrand static uint64_t vfa64(uint64_t a, uint64_t b, float_status *s) 1053a0eae85SDavid Hildenbrand { 1063a0eae85SDavid Hildenbrand return float64_add(a, b, s); 1073a0eae85SDavid Hildenbrand } 1083a0eae85SDavid Hildenbrand 1093a0eae85SDavid Hildenbrand void HELPER(gvec_vfa64)(void *v1, const void *v2, const void *v3, 1103a0eae85SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 1113a0eae85SDavid Hildenbrand { 1123a0eae85SDavid Hildenbrand vop64_3(v1, v2, v3, env, false, vfa64, GETPC()); 1133a0eae85SDavid Hildenbrand } 1143a0eae85SDavid Hildenbrand 1153a0eae85SDavid Hildenbrand void HELPER(gvec_vfa64s)(void *v1, const void *v2, const void *v3, 1163a0eae85SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 1173a0eae85SDavid Hildenbrand { 1183a0eae85SDavid Hildenbrand vop64_3(v1, v2, v3, env, true, vfa64, GETPC()); 1193a0eae85SDavid Hildenbrand } 1205b89f0fbSDavid Hildenbrand 1215b89f0fbSDavid Hildenbrand static int wfc64(const S390Vector *v1, const S390Vector *v2, 1225b89f0fbSDavid Hildenbrand CPUS390XState *env, bool signal, uintptr_t retaddr) 1235b89f0fbSDavid Hildenbrand { 1245b89f0fbSDavid Hildenbrand /* only the zero-indexed elements are compared */ 1255b89f0fbSDavid Hildenbrand const float64 a = s390_vec_read_element64(v1, 0); 1265b89f0fbSDavid Hildenbrand const float64 b = s390_vec_read_element64(v2, 0); 1275b89f0fbSDavid Hildenbrand uint8_t vxc, vec_exc = 0; 1285b89f0fbSDavid Hildenbrand int cmp; 1295b89f0fbSDavid Hildenbrand 1305b89f0fbSDavid Hildenbrand if (signal) { 1315b89f0fbSDavid Hildenbrand cmp = float64_compare(a, b, &env->fpu_status); 1325b89f0fbSDavid Hildenbrand } else { 1335b89f0fbSDavid Hildenbrand cmp = float64_compare_quiet(a, b, &env->fpu_status); 1345b89f0fbSDavid Hildenbrand } 1355b89f0fbSDavid Hildenbrand vxc = check_ieee_exc(env, 0, false, &vec_exc); 1365b89f0fbSDavid Hildenbrand handle_ieee_exc(env, vxc, vec_exc, retaddr); 1375b89f0fbSDavid Hildenbrand 1385b89f0fbSDavid Hildenbrand return float_comp_to_cc(env, cmp); 1395b89f0fbSDavid Hildenbrand } 1405b89f0fbSDavid Hildenbrand 1415b89f0fbSDavid Hildenbrand void HELPER(gvec_wfc64)(const void *v1, const void *v2, CPUS390XState *env, 1425b89f0fbSDavid Hildenbrand uint32_t desc) 1435b89f0fbSDavid Hildenbrand { 1445b89f0fbSDavid Hildenbrand env->cc_op = wfc64(v1, v2, env, false, GETPC()); 1455b89f0fbSDavid Hildenbrand } 1465b89f0fbSDavid Hildenbrand 1475b89f0fbSDavid Hildenbrand void HELPER(gvec_wfk64)(const void *v1, const void *v2, CPUS390XState *env, 1485b89f0fbSDavid Hildenbrand uint32_t desc) 1495b89f0fbSDavid Hildenbrand { 1505b89f0fbSDavid Hildenbrand env->cc_op = wfc64(v1, v2, env, true, GETPC()); 1515b89f0fbSDavid Hildenbrand } 152*2c806ab4SDavid Hildenbrand 153*2c806ab4SDavid Hildenbrand typedef int (*vfc64_fn)(float64 a, float64 b, float_status *status); 154*2c806ab4SDavid Hildenbrand static int vfc64(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, 155*2c806ab4SDavid Hildenbrand CPUS390XState *env, bool s, vfc64_fn fn, uintptr_t retaddr) 156*2c806ab4SDavid Hildenbrand { 157*2c806ab4SDavid Hildenbrand uint8_t vxc, vec_exc = 0; 158*2c806ab4SDavid Hildenbrand S390Vector tmp = {}; 159*2c806ab4SDavid Hildenbrand int match = 0; 160*2c806ab4SDavid Hildenbrand int i; 161*2c806ab4SDavid Hildenbrand 162*2c806ab4SDavid Hildenbrand for (i = 0; i < 2; i++) { 163*2c806ab4SDavid Hildenbrand const float64 a = s390_vec_read_element64(v2, i); 164*2c806ab4SDavid Hildenbrand const float64 b = s390_vec_read_element64(v3, i); 165*2c806ab4SDavid Hildenbrand 166*2c806ab4SDavid Hildenbrand /* swap the order of the parameters, so we can use existing functions */ 167*2c806ab4SDavid Hildenbrand if (fn(b, a, &env->fpu_status)) { 168*2c806ab4SDavid Hildenbrand match++; 169*2c806ab4SDavid Hildenbrand s390_vec_write_element64(&tmp, i, -1ull); 170*2c806ab4SDavid Hildenbrand } 171*2c806ab4SDavid Hildenbrand vxc = check_ieee_exc(env, i, false, &vec_exc); 172*2c806ab4SDavid Hildenbrand if (s || vxc) { 173*2c806ab4SDavid Hildenbrand break; 174*2c806ab4SDavid Hildenbrand } 175*2c806ab4SDavid Hildenbrand } 176*2c806ab4SDavid Hildenbrand 177*2c806ab4SDavid Hildenbrand handle_ieee_exc(env, vxc, vec_exc, retaddr); 178*2c806ab4SDavid Hildenbrand *v1 = tmp; 179*2c806ab4SDavid Hildenbrand if (match) { 180*2c806ab4SDavid Hildenbrand return s || match == 2 ? 0 : 1; 181*2c806ab4SDavid Hildenbrand } 182*2c806ab4SDavid Hildenbrand return 3; 183*2c806ab4SDavid Hildenbrand } 184*2c806ab4SDavid Hildenbrand 185*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfce64)(void *v1, const void *v2, const void *v3, 186*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 187*2c806ab4SDavid Hildenbrand { 188*2c806ab4SDavid Hildenbrand vfc64(v1, v2, v3, env, false, float64_eq_quiet, GETPC()); 189*2c806ab4SDavid Hildenbrand } 190*2c806ab4SDavid Hildenbrand 191*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfce64s)(void *v1, const void *v2, const void *v3, 192*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 193*2c806ab4SDavid Hildenbrand { 194*2c806ab4SDavid Hildenbrand vfc64(v1, v2, v3, env, true, float64_eq_quiet, GETPC()); 195*2c806ab4SDavid Hildenbrand } 196*2c806ab4SDavid Hildenbrand 197*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfce64_cc)(void *v1, const void *v2, const void *v3, 198*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 199*2c806ab4SDavid Hildenbrand { 200*2c806ab4SDavid Hildenbrand env->cc_op = vfc64(v1, v2, v3, env, false, float64_eq_quiet, GETPC()); 201*2c806ab4SDavid Hildenbrand } 202*2c806ab4SDavid Hildenbrand 203*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfce64s_cc)(void *v1, const void *v2, const void *v3, 204*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 205*2c806ab4SDavid Hildenbrand { 206*2c806ab4SDavid Hildenbrand env->cc_op = vfc64(v1, v2, v3, env, true, float64_eq_quiet, GETPC()); 207*2c806ab4SDavid Hildenbrand } 208*2c806ab4SDavid Hildenbrand 209*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfch64)(void *v1, const void *v2, const void *v3, 210*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 211*2c806ab4SDavid Hildenbrand { 212*2c806ab4SDavid Hildenbrand vfc64(v1, v2, v3, env, false, float64_lt_quiet, GETPC()); 213*2c806ab4SDavid Hildenbrand } 214*2c806ab4SDavid Hildenbrand 215*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfch64s)(void *v1, const void *v2, const void *v3, 216*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 217*2c806ab4SDavid Hildenbrand { 218*2c806ab4SDavid Hildenbrand vfc64(v1, v2, v3, env, true, float64_lt_quiet, GETPC()); 219*2c806ab4SDavid Hildenbrand } 220*2c806ab4SDavid Hildenbrand 221*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfch64_cc)(void *v1, const void *v2, const void *v3, 222*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 223*2c806ab4SDavid Hildenbrand { 224*2c806ab4SDavid Hildenbrand env->cc_op = vfc64(v1, v2, v3, env, false, float64_lt_quiet, GETPC()); 225*2c806ab4SDavid Hildenbrand } 226*2c806ab4SDavid Hildenbrand 227*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfch64s_cc)(void *v1, const void *v2, const void *v3, 228*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 229*2c806ab4SDavid Hildenbrand { 230*2c806ab4SDavid Hildenbrand env->cc_op = vfc64(v1, v2, v3, env, true, float64_lt_quiet, GETPC()); 231*2c806ab4SDavid Hildenbrand } 232*2c806ab4SDavid Hildenbrand 233*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfche64)(void *v1, const void *v2, const void *v3, 234*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 235*2c806ab4SDavid Hildenbrand { 236*2c806ab4SDavid Hildenbrand vfc64(v1, v2, v3, env, false, float64_le_quiet, GETPC()); 237*2c806ab4SDavid Hildenbrand } 238*2c806ab4SDavid Hildenbrand 239*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfche64s)(void *v1, const void *v2, const void *v3, 240*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 241*2c806ab4SDavid Hildenbrand { 242*2c806ab4SDavid Hildenbrand vfc64(v1, v2, v3, env, true, float64_le_quiet, GETPC()); 243*2c806ab4SDavid Hildenbrand } 244*2c806ab4SDavid Hildenbrand 245*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfche64_cc)(void *v1, const void *v2, const void *v3, 246*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 247*2c806ab4SDavid Hildenbrand { 248*2c806ab4SDavid Hildenbrand env->cc_op = vfc64(v1, v2, v3, env, false, float64_le_quiet, GETPC()); 249*2c806ab4SDavid Hildenbrand } 250*2c806ab4SDavid Hildenbrand 251*2c806ab4SDavid Hildenbrand void HELPER(gvec_vfche64s_cc)(void *v1, const void *v2, const void *v3, 252*2c806ab4SDavid Hildenbrand CPUS390XState *env, uint32_t desc) 253*2c806ab4SDavid Hildenbrand { 254*2c806ab4SDavid Hildenbrand env->cc_op = vfc64(v1, v2, v3, env, true, float64_le_quiet, GETPC()); 255*2c806ab4SDavid Hildenbrand } 256