| 1 | /* |
| 2 | * QEMU TCG support -- s390x vector support instructions |
| 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 "cpu.h" |
| 14 | #include "s390x-internal.h" |
| 15 | #include "vec.h" |
| 16 | #include "tcg/tcg.h" |
| 17 | #include "tcg/tcg-gvec-desc.h" |
| 18 | #include "exec/helper-proto.h" |
| 19 | #include "accel/tcg/cpu-ldst-common.h" |
| 20 | #include "accel/tcg/cpu-mmu-index.h" |
| 21 | |
| 22 | void HELPER(gvec_vbperm)(void *v1, const void *v2, const void *v3, |
| 23 | uint32_t desc) |
| 24 | { |
| 25 | S390Vector tmp = {}; |
| 26 | uint16_t result = 0; |
| 27 | int i; |
| 28 | |
| 29 | for (i = 0; i < 16; i++) { |
| 30 | const uint8_t bit_nr = s390_vec_read_element8(v3, i); |
| 31 | uint16_t bit; |
| 32 | |
| 33 | if (bit_nr >= 128) { |
| 34 | continue; |
| 35 | } |
| 36 | bit = (s390_vec_read_element8(v2, bit_nr / 8) |
| 37 | >> (7 - (bit_nr % 8))) & 1; |
| 38 | result |= (bit << (15 - i)); |
| 39 | } |
| 40 | s390_vec_write_element16(&tmp, 3, result); |
| 41 | *(S390Vector *)v1 = tmp; |
| 42 | } |
| 43 | |
| 44 | void HELPER(vll)(CPUS390XState *env, void *v1, uint64_t addr, uint64_t bytes) |
| 45 | { |
| 46 | const int mmu_idx = cpu_mmu_index(env_cpu(env), false); |
| 47 | const uintptr_t ra = GETPC(); |
| 48 | MemOpIdx oi; |
| 49 | |
| 50 | if (likely(bytes >= 16)) { |
| 51 | uint64_t t0, t1; |
| 52 | |
| 53 | oi = make_memop_idx(MO_BE | MO_64 | MO_UNALN, mmu_idx); |
| 54 | t0 = cpu_ldq_mmu(env, addr, oi, ra); |
| 55 | addr = wrap_address(env, addr + 8); |
| 56 | t1 = cpu_ldq_mmu(env, addr, oi, ra); |
| 57 | s390_vec_write_element64(v1, 0, t0); |
| 58 | s390_vec_write_element64(v1, 1, t1); |
| 59 | } else { |
| 60 | S390Vector tmp = {}; |
| 61 | |
| 62 | oi = make_memop_idx(MO_8, mmu_idx); |
| 63 | for (int i = 0; i < bytes; i++) { |
| 64 | uint8_t byte = cpu_ldb_mmu(env, addr, oi, ra); |
| 65 | |
| 66 | |
| 67 | s390_vec_write_element8(&tmp, i, byte); |
| 68 | addr = wrap_address(env, addr + 1); |
| 69 | } |
| 70 | *(S390Vector *)v1 = tmp; |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | #define DEF_VPK_HFN(BITS, TBITS) \ |
| 75 | typedef uint##TBITS##_t (*vpk##BITS##_fn)(uint##BITS##_t, int *); \ |
| 76 | static int vpk##BITS##_hfn(S390Vector *v1, const S390Vector *v2, \ |
| 77 | const S390Vector *v3, vpk##BITS##_fn fn) \ |
| 78 | { \ |
| 79 | int i, saturated = 0; \ |
| 80 | S390Vector tmp; \ |
| 81 | \ |
| 82 | for (i = 0; i < (128 / TBITS); i++) { \ |
| 83 | uint##BITS##_t src; \ |
| 84 | \ |
| 85 | if (i < (128 / BITS)) { \ |
| 86 | src = s390_vec_read_element##BITS(v2, i); \ |
| 87 | } else { \ |
| 88 | src = s390_vec_read_element##BITS(v3, i - (128 / BITS)); \ |
| 89 | } \ |
| 90 | s390_vec_write_element##TBITS(&tmp, i, fn(src, &saturated)); \ |
| 91 | } \ |
| 92 | *v1 = tmp; \ |
| 93 | return saturated; \ |
| 94 | } |
| 95 | DEF_VPK_HFN(64, 32) |
| 96 | DEF_VPK_HFN(32, 16) |
| 97 | DEF_VPK_HFN(16, 8) |
| 98 | |
| 99 | #define DEF_VPK(BITS, TBITS) \ |
| 100 | static uint##TBITS##_t vpk##BITS##e(uint##BITS##_t src, int *saturated) \ |
| 101 | { \ |
| 102 | return src; \ |
| 103 | } \ |
| 104 | void HELPER(gvec_vpk##BITS)(void *v1, const void *v2, const void *v3, \ |
| 105 | uint32_t desc) \ |
| 106 | { \ |
| 107 | vpk##BITS##_hfn(v1, v2, v3, vpk##BITS##e); \ |
| 108 | } |
| 109 | DEF_VPK(64, 32) |
| 110 | DEF_VPK(32, 16) |
| 111 | DEF_VPK(16, 8) |
| 112 | |
| 113 | #define DEF_VPKS(BITS, TBITS) \ |
| 114 | static uint##TBITS##_t vpks##BITS##e(uint##BITS##_t src, int *saturated) \ |
| 115 | { \ |
| 116 | if ((int##BITS##_t)src > INT##TBITS##_MAX) { \ |
| 117 | (*saturated)++; \ |
| 118 | return INT##TBITS##_MAX; \ |
| 119 | } else if ((int##BITS##_t)src < INT##TBITS##_MIN) { \ |
| 120 | (*saturated)++; \ |
| 121 | return INT##TBITS##_MIN; \ |
| 122 | } \ |
| 123 | return src; \ |
| 124 | } \ |
| 125 | void HELPER(gvec_vpks##BITS)(void *v1, const void *v2, const void *v3, \ |
| 126 | uint32_t desc) \ |
| 127 | { \ |
| 128 | vpk##BITS##_hfn(v1, v2, v3, vpks##BITS##e); \ |
| 129 | } \ |
| 130 | void HELPER(gvec_vpks_cc##BITS)(void *v1, const void *v2, const void *v3, \ |
| 131 | CPUS390XState *env, uint32_t desc) \ |
| 132 | { \ |
| 133 | int saturated = vpk##BITS##_hfn(v1, v2, v3, vpks##BITS##e); \ |
| 134 | \ |
| 135 | if (saturated == (128 / TBITS)) { \ |
| 136 | env->cc_op = 3; \ |
| 137 | } else if (saturated) { \ |
| 138 | env->cc_op = 1; \ |
| 139 | } else { \ |
| 140 | env->cc_op = 0; \ |
| 141 | } \ |
| 142 | } |
| 143 | DEF_VPKS(64, 32) |
| 144 | DEF_VPKS(32, 16) |
| 145 | DEF_VPKS(16, 8) |
| 146 | |
| 147 | #define DEF_VPKLS(BITS, TBITS) \ |
| 148 | static uint##TBITS##_t vpkls##BITS##e(uint##BITS##_t src, int *saturated) \ |
| 149 | { \ |
| 150 | if (src > UINT##TBITS##_MAX) { \ |
| 151 | (*saturated)++; \ |
| 152 | return UINT##TBITS##_MAX; \ |
| 153 | } \ |
| 154 | return src; \ |
| 155 | } \ |
| 156 | void HELPER(gvec_vpkls##BITS)(void *v1, const void *v2, const void *v3, \ |
| 157 | uint32_t desc) \ |
| 158 | { \ |
| 159 | vpk##BITS##_hfn(v1, v2, v3, vpkls##BITS##e); \ |
| 160 | } \ |
| 161 | void HELPER(gvec_vpkls_cc##BITS)(void *v1, const void *v2, const void *v3, \ |
| 162 | CPUS390XState *env, uint32_t desc) \ |
| 163 | { \ |
| 164 | int saturated = vpk##BITS##_hfn(v1, v2, v3, vpkls##BITS##e); \ |
| 165 | \ |
| 166 | if (saturated == (128 / TBITS)) { \ |
| 167 | env->cc_op = 3; \ |
| 168 | } else if (saturated) { \ |
| 169 | env->cc_op = 1; \ |
| 170 | } else { \ |
| 171 | env->cc_op = 0; \ |
| 172 | } \ |
| 173 | } |
| 174 | DEF_VPKLS(64, 32) |
| 175 | DEF_VPKLS(32, 16) |
| 176 | DEF_VPKLS(16, 8) |
| 177 | |
| 178 | void HELPER(gvec_vperm)(void *v1, const void *v2, const void *v3, |
| 179 | const void *v4, uint32_t desc) |
| 180 | { |
| 181 | S390Vector tmp; |
| 182 | int i; |
| 183 | |
| 184 | for (i = 0; i < 16; i++) { |
| 185 | const uint8_t selector = s390_vec_read_element8(v4, i) & 0x1f; |
| 186 | uint8_t byte; |
| 187 | |
| 188 | if (selector < 16) { |
| 189 | byte = s390_vec_read_element8(v2, selector); |
| 190 | } else { |
| 191 | byte = s390_vec_read_element8(v3, selector - 16); |
| 192 | } |
| 193 | s390_vec_write_element8(&tmp, i, byte); |
| 194 | } |
| 195 | *(S390Vector *)v1 = tmp; |
| 196 | } |
| 197 | |
| 198 | void HELPER(vstl)(CPUS390XState *env, const void *v1, uint64_t addr, |
| 199 | uint64_t bytes) |
| 200 | { |
| 201 | const int mmu_idx = cpu_mmu_index(env_cpu(env), false); |
| 202 | const uintptr_t ra = GETPC(); |
| 203 | MemOpIdx oi; |
| 204 | |
| 205 | /* Probe write access before actually modifying memory */ |
| 206 | probe_write_access(env, addr, MIN(bytes, 16), GETPC()); |
| 207 | |
| 208 | if (likely(bytes >= 16)) { |
| 209 | oi = make_memop_idx(MO_BE | MO_64 | MO_UNALN, mmu_idx); |
| 210 | cpu_stq_mmu(env, addr, s390_vec_read_element64(v1, 0), oi, ra); |
| 211 | addr = wrap_address(env, addr + 8); |
| 212 | cpu_stq_mmu(env, addr, s390_vec_read_element64(v1, 1), oi, ra); |
| 213 | } else { |
| 214 | oi = make_memop_idx(MO_8, mmu_idx); |
| 215 | |
| 216 | for (int i = 0; i < bytes; i++) { |
| 217 | uint8_t byte = s390_vec_read_element8(v1, i); |
| 218 | |
| 219 | cpu_stb_mmu(env, addr, byte, oi, ra); |
| 220 | addr = wrap_address(env, addr + 1); |
| 221 | } |
| 222 | } |
| 223 | } |