blob: ededf13cf0990e24a10b528109d17f17f567b89a [file] [log] [blame]
/*
* QEMU TCG support -- s390x vector support instructions
*
* Copyright (C) 2019 Red Hat Inc
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "s390x-internal.h"
#include "vec.h"
#include "tcg/tcg.h"
#include "tcg/tcg-gvec-desc.h"
#include "exec/helper-proto.h"
#include "exec/cpu_ldst.h"
#include "exec/exec-all.h"
void HELPER(gvec_vbperm)(void *v1, const void *v2, const void *v3,
uint32_t desc)
{
S390Vector tmp = {};
uint16_t result = 0;
int i;
for (i = 0; i < 16; i++) {
const uint8_t bit_nr = s390_vec_read_element8(v3, i);
uint16_t bit;
if (bit_nr >= 128) {
continue;
}
bit = (s390_vec_read_element8(v2, bit_nr / 8)
>> (7 - (bit_nr % 8))) & 1;
result |= (bit << (15 - i));
}
s390_vec_write_element16(&tmp, 3, result);
*(S390Vector *)v1 = tmp;
}
void HELPER(vll)(CPUS390XState *env, void *v1, uint64_t addr, uint64_t bytes)
{
if (likely(bytes >= 16)) {
uint64_t t0, t1;
t0 = cpu_ldq_data_ra(env, addr, GETPC());
addr = wrap_address(env, addr + 8);
t1 = cpu_ldq_data_ra(env, addr, GETPC());
s390_vec_write_element64(v1, 0, t0);
s390_vec_write_element64(v1, 1, t1);
} else {
S390Vector tmp = {};
int i;
for (i = 0; i < bytes; i++) {
uint8_t byte = cpu_ldub_data_ra(env, addr, GETPC());
s390_vec_write_element8(&tmp, i, byte);
addr = wrap_address(env, addr + 1);
}
*(S390Vector *)v1 = tmp;
}
}
#define DEF_VPK_HFN(BITS, TBITS) \
typedef uint##TBITS##_t (*vpk##BITS##_fn)(uint##BITS##_t, int *); \
static int vpk##BITS##_hfn(S390Vector *v1, const S390Vector *v2, \
const S390Vector *v3, vpk##BITS##_fn fn) \
{ \
int i, saturated = 0; \
S390Vector tmp; \
\
for (i = 0; i < (128 / TBITS); i++) { \
uint##BITS##_t src; \
\
if (i < (128 / BITS)) { \
src = s390_vec_read_element##BITS(v2, i); \
} else { \
src = s390_vec_read_element##BITS(v3, i - (128 / BITS)); \
} \
s390_vec_write_element##TBITS(&tmp, i, fn(src, &saturated)); \
} \
*v1 = tmp; \
return saturated; \
}
DEF_VPK_HFN(64, 32)
DEF_VPK_HFN(32, 16)
DEF_VPK_HFN(16, 8)
#define DEF_VPK(BITS, TBITS) \
static uint##TBITS##_t vpk##BITS##e(uint##BITS##_t src, int *saturated) \
{ \
return src; \
} \
void HELPER(gvec_vpk##BITS)(void *v1, const void *v2, const void *v3, \
uint32_t desc) \
{ \
vpk##BITS##_hfn(v1, v2, v3, vpk##BITS##e); \
}
DEF_VPK(64, 32)
DEF_VPK(32, 16)
DEF_VPK(16, 8)
#define DEF_VPKS(BITS, TBITS) \
static uint##TBITS##_t vpks##BITS##e(uint##BITS##_t src, int *saturated) \
{ \
if ((int##BITS##_t)src > INT##TBITS##_MAX) { \
(*saturated)++; \
return INT##TBITS##_MAX; \
} else if ((int##BITS##_t)src < INT##TBITS##_MIN) { \
(*saturated)++; \
return INT##TBITS##_MIN; \
} \
return src; \
} \
void HELPER(gvec_vpks##BITS)(void *v1, const void *v2, const void *v3, \
uint32_t desc) \
{ \
vpk##BITS##_hfn(v1, v2, v3, vpks##BITS##e); \
} \
void HELPER(gvec_vpks_cc##BITS)(void *v1, const void *v2, const void *v3, \
CPUS390XState *env, uint32_t desc) \
{ \
int saturated = vpk##BITS##_hfn(v1, v2, v3, vpks##BITS##e); \
\
if (saturated == (128 / TBITS)) { \
env->cc_op = 3; \
} else if (saturated) { \
env->cc_op = 1; \
} else { \
env->cc_op = 0; \
} \
}
DEF_VPKS(64, 32)
DEF_VPKS(32, 16)
DEF_VPKS(16, 8)
#define DEF_VPKLS(BITS, TBITS) \
static uint##TBITS##_t vpkls##BITS##e(uint##BITS##_t src, int *saturated) \
{ \
if (src > UINT##TBITS##_MAX) { \
(*saturated)++; \
return UINT##TBITS##_MAX; \
} \
return src; \
} \
void HELPER(gvec_vpkls##BITS)(void *v1, const void *v2, const void *v3, \
uint32_t desc) \
{ \
vpk##BITS##_hfn(v1, v2, v3, vpkls##BITS##e); \
} \
void HELPER(gvec_vpkls_cc##BITS)(void *v1, const void *v2, const void *v3, \
CPUS390XState *env, uint32_t desc) \
{ \
int saturated = vpk##BITS##_hfn(v1, v2, v3, vpkls##BITS##e); \
\
if (saturated == (128 / TBITS)) { \
env->cc_op = 3; \
} else if (saturated) { \
env->cc_op = 1; \
} else { \
env->cc_op = 0; \
} \
}
DEF_VPKLS(64, 32)
DEF_VPKLS(32, 16)
DEF_VPKLS(16, 8)
void HELPER(gvec_vperm)(void *v1, const void *v2, const void *v3,
const void *v4, uint32_t desc)
{
S390Vector tmp;
int i;
for (i = 0; i < 16; i++) {
const uint8_t selector = s390_vec_read_element8(v4, i) & 0x1f;
uint8_t byte;
if (selector < 16) {
byte = s390_vec_read_element8(v2, selector);
} else {
byte = s390_vec_read_element8(v3, selector - 16);
}
s390_vec_write_element8(&tmp, i, byte);
}
*(S390Vector *)v1 = tmp;
}
void HELPER(vstl)(CPUS390XState *env, const void *v1, uint64_t addr,
uint64_t bytes)
{
/* Probe write access before actually modifying memory */
probe_write_access(env, addr, bytes, GETPC());
if (likely(bytes >= 16)) {
cpu_stq_data_ra(env, addr, s390_vec_read_element64(v1, 0), GETPC());
addr = wrap_address(env, addr + 8);
cpu_stq_data_ra(env, addr, s390_vec_read_element64(v1, 1), GETPC());
} else {
S390Vector tmp = {};
int i;
for (i = 0; i < bytes; i++) {
uint8_t byte = s390_vec_read_element8(v1, i);
cpu_stb_data_ra(env, addr, byte, GETPC());
addr = wrap_address(env, addr + 1);
}
*(S390Vector *)v1 = tmp;
}
}