1 /* 2 * QEMU TCG support -- s390x vector floating point instruction support 3 * 4 * Copyright (C) 2019 Red Hat Inc 5 * 6 * Authors: 7 * David Hildenbrand <david@redhat.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 */ 12 #include "qemu/osdep.h" 13 #include "qemu-common.h" 14 #include "cpu.h" 15 #include "internal.h" 16 #include "vec.h" 17 #include "tcg_s390x.h" 18 #include "tcg/tcg-gvec-desc.h" 19 #include "exec/exec-all.h" 20 #include "exec/helper-proto.h" 21 #include "fpu/softfloat.h" 22 23 #define VIC_INVALID 0x1 24 #define VIC_DIVBYZERO 0x2 25 #define VIC_OVERFLOW 0x3 26 #define VIC_UNDERFLOW 0x4 27 #define VIC_INEXACT 0x5 28 29 /* returns the VEX. If the VEX is 0, there is no trap */ 30 static uint8_t check_ieee_exc(CPUS390XState *env, uint8_t enr, bool XxC, 31 uint8_t *vec_exc) 32 { 33 uint8_t vece_exc = 0, trap_exc; 34 unsigned qemu_exc; 35 36 /* Retrieve and clear the softfloat exceptions */ 37 qemu_exc = env->fpu_status.float_exception_flags; 38 if (qemu_exc == 0) { 39 return 0; 40 } 41 env->fpu_status.float_exception_flags = 0; 42 43 vece_exc = s390_softfloat_exc_to_ieee(qemu_exc); 44 45 /* Add them to the vector-wide s390x exception bits */ 46 *vec_exc |= vece_exc; 47 48 /* Check for traps and construct the VXC */ 49 trap_exc = vece_exc & env->fpc >> 24; 50 if (trap_exc) { 51 if (trap_exc & S390_IEEE_MASK_INVALID) { 52 return enr << 4 | VIC_INVALID; 53 } else if (trap_exc & S390_IEEE_MASK_DIVBYZERO) { 54 return enr << 4 | VIC_DIVBYZERO; 55 } else if (trap_exc & S390_IEEE_MASK_OVERFLOW) { 56 return enr << 4 | VIC_OVERFLOW; 57 } else if (trap_exc & S390_IEEE_MASK_UNDERFLOW) { 58 return enr << 4 | VIC_UNDERFLOW; 59 } else if (!XxC) { 60 g_assert(trap_exc & S390_IEEE_MASK_INEXACT); 61 /* inexact has lowest priority on traps */ 62 return enr << 4 | VIC_INEXACT; 63 } 64 } 65 return 0; 66 } 67 68 static void handle_ieee_exc(CPUS390XState *env, uint8_t vxc, uint8_t vec_exc, 69 uintptr_t retaddr) 70 { 71 if (vxc) { 72 /* on traps, the fpc flags are not updated, instruction is suppressed */ 73 tcg_s390_vector_exception(env, vxc, retaddr); 74 } 75 if (vec_exc) { 76 /* indicate exceptions for all elements combined */ 77 env->fpc |= vec_exc << 16; 78 } 79 } 80 81 typedef uint64_t (*vop64_3_fn)(uint64_t a, uint64_t b, float_status *s); 82 static void vop64_3(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, 83 CPUS390XState *env, bool s, vop64_3_fn fn, 84 uintptr_t retaddr) 85 { 86 uint8_t vxc, vec_exc = 0; 87 S390Vector tmp = {}; 88 int i; 89 90 for (i = 0; i < 2; i++) { 91 const uint64_t a = s390_vec_read_element64(v2, i); 92 const uint64_t b = s390_vec_read_element64(v3, i); 93 94 s390_vec_write_element64(&tmp, i, fn(a, b, &env->fpu_status)); 95 vxc = check_ieee_exc(env, i, false, &vec_exc); 96 if (s || vxc) { 97 break; 98 } 99 } 100 handle_ieee_exc(env, vxc, vec_exc, retaddr); 101 *v1 = tmp; 102 } 103 104 static uint64_t vfa64(uint64_t a, uint64_t b, float_status *s) 105 { 106 return float64_add(a, b, s); 107 } 108 109 void HELPER(gvec_vfa64)(void *v1, const void *v2, const void *v3, 110 CPUS390XState *env, uint32_t desc) 111 { 112 vop64_3(v1, v2, v3, env, false, vfa64, GETPC()); 113 } 114 115 void HELPER(gvec_vfa64s)(void *v1, const void *v2, const void *v3, 116 CPUS390XState *env, uint32_t desc) 117 { 118 vop64_3(v1, v2, v3, env, true, vfa64, GETPC()); 119 } 120 121 static int wfc64(const S390Vector *v1, const S390Vector *v2, 122 CPUS390XState *env, bool signal, uintptr_t retaddr) 123 { 124 /* only the zero-indexed elements are compared */ 125 const float64 a = s390_vec_read_element64(v1, 0); 126 const float64 b = s390_vec_read_element64(v2, 0); 127 uint8_t vxc, vec_exc = 0; 128 int cmp; 129 130 if (signal) { 131 cmp = float64_compare(a, b, &env->fpu_status); 132 } else { 133 cmp = float64_compare_quiet(a, b, &env->fpu_status); 134 } 135 vxc = check_ieee_exc(env, 0, false, &vec_exc); 136 handle_ieee_exc(env, vxc, vec_exc, retaddr); 137 138 return float_comp_to_cc(env, cmp); 139 } 140 141 void HELPER(gvec_wfc64)(const void *v1, const void *v2, CPUS390XState *env, 142 uint32_t desc) 143 { 144 env->cc_op = wfc64(v1, v2, env, false, GETPC()); 145 } 146 147 void HELPER(gvec_wfk64)(const void *v1, const void *v2, CPUS390XState *env, 148 uint32_t desc) 149 { 150 env->cc_op = wfc64(v1, v2, env, true, GETPC()); 151 } 152 153 typedef int (*vfc64_fn)(float64 a, float64 b, float_status *status); 154 static int vfc64(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, 155 CPUS390XState *env, bool s, vfc64_fn fn, uintptr_t retaddr) 156 { 157 uint8_t vxc, vec_exc = 0; 158 S390Vector tmp = {}; 159 int match = 0; 160 int i; 161 162 for (i = 0; i < 2; i++) { 163 const float64 a = s390_vec_read_element64(v2, i); 164 const float64 b = s390_vec_read_element64(v3, i); 165 166 /* swap the order of the parameters, so we can use existing functions */ 167 if (fn(b, a, &env->fpu_status)) { 168 match++; 169 s390_vec_write_element64(&tmp, i, -1ull); 170 } 171 vxc = check_ieee_exc(env, i, false, &vec_exc); 172 if (s || vxc) { 173 break; 174 } 175 } 176 177 handle_ieee_exc(env, vxc, vec_exc, retaddr); 178 *v1 = tmp; 179 if (match) { 180 return s || match == 2 ? 0 : 1; 181 } 182 return 3; 183 } 184 185 void HELPER(gvec_vfce64)(void *v1, const void *v2, const void *v3, 186 CPUS390XState *env, uint32_t desc) 187 { 188 vfc64(v1, v2, v3, env, false, float64_eq_quiet, GETPC()); 189 } 190 191 void HELPER(gvec_vfce64s)(void *v1, const void *v2, const void *v3, 192 CPUS390XState *env, uint32_t desc) 193 { 194 vfc64(v1, v2, v3, env, true, float64_eq_quiet, GETPC()); 195 } 196 197 void HELPER(gvec_vfce64_cc)(void *v1, const void *v2, const void *v3, 198 CPUS390XState *env, uint32_t desc) 199 { 200 env->cc_op = vfc64(v1, v2, v3, env, false, float64_eq_quiet, GETPC()); 201 } 202 203 void HELPER(gvec_vfce64s_cc)(void *v1, const void *v2, const void *v3, 204 CPUS390XState *env, uint32_t desc) 205 { 206 env->cc_op = vfc64(v1, v2, v3, env, true, float64_eq_quiet, GETPC()); 207 } 208 209 void HELPER(gvec_vfch64)(void *v1, const void *v2, const void *v3, 210 CPUS390XState *env, uint32_t desc) 211 { 212 vfc64(v1, v2, v3, env, false, float64_lt_quiet, GETPC()); 213 } 214 215 void HELPER(gvec_vfch64s)(void *v1, const void *v2, const void *v3, 216 CPUS390XState *env, uint32_t desc) 217 { 218 vfc64(v1, v2, v3, env, true, float64_lt_quiet, GETPC()); 219 } 220 221 void HELPER(gvec_vfch64_cc)(void *v1, const void *v2, const void *v3, 222 CPUS390XState *env, uint32_t desc) 223 { 224 env->cc_op = vfc64(v1, v2, v3, env, false, float64_lt_quiet, GETPC()); 225 } 226 227 void HELPER(gvec_vfch64s_cc)(void *v1, const void *v2, const void *v3, 228 CPUS390XState *env, uint32_t desc) 229 { 230 env->cc_op = vfc64(v1, v2, v3, env, true, float64_lt_quiet, GETPC()); 231 } 232 233 void HELPER(gvec_vfche64)(void *v1, const void *v2, const void *v3, 234 CPUS390XState *env, uint32_t desc) 235 { 236 vfc64(v1, v2, v3, env, false, float64_le_quiet, GETPC()); 237 } 238 239 void HELPER(gvec_vfche64s)(void *v1, const void *v2, const void *v3, 240 CPUS390XState *env, uint32_t desc) 241 { 242 vfc64(v1, v2, v3, env, true, float64_le_quiet, GETPC()); 243 } 244 245 void HELPER(gvec_vfche64_cc)(void *v1, const void *v2, const void *v3, 246 CPUS390XState *env, uint32_t desc) 247 { 248 env->cc_op = vfc64(v1, v2, v3, env, false, float64_le_quiet, GETPC()); 249 } 250 251 void HELPER(gvec_vfche64s_cc)(void *v1, const void *v2, const void *v3, 252 CPUS390XState *env, uint32_t desc) 253 { 254 env->cc_op = vfc64(v1, v2, v3, env, true, float64_le_quiet, GETPC()); 255 } 256