Add new constant data structure.

This patch provides the data structure and function to convert a
CONST_INT, CONST_DOUBLE, CONST_VECTOR, or VEC_DUPLICATE of a constant) to
an array of bytes, half-words, words, and  double words that can be loaded
into a 128-bit vector register.

The next patches will use this data structure to generate code that
generates load of the vector/floating point registers using the XXSPLTIDP,
XXSPLTIW, and LXVKQ instructions that were added in power10.

2021-12-15  Michael Meissner  <meissner@the-meissners.org>

gcc/

	* config/rs6000/rs6000-protos.h (VECTOR_128BIT_BITS): New macro.
	(VECTOR_128BIT_BYTES): Likewise.
	(VECTOR_128BIT_HALF_WORDS): Likewise.
	(VECTOR_128BIT_WORDS): Likewise.
	(VECTOR_128BIT_DOUBLE_WORDS): Likewise.
	(vec_const_128bit_type): New structure type.
	(vec_const_128bit_to_bytes): New declaration.
	* config/rs6000/rs6000.c (constant_int_to_128bit_vector): New
	helper function.
	(constant_fp_to_128bit_vector): New helper function.
	(vec_const_128bit_to_bytes): New function.
This commit is contained in:
Michael Meissner 2021-12-15 00:56:25 -05:00
parent 71cc9b8c39
commit c6756b3bc1
2 changed files with 281 additions and 0 deletions

View file

@ -222,6 +222,34 @@ address_is_prefixed (rtx addr,
return (iform == INSN_FORM_PREFIXED_NUMERIC
|| iform == INSN_FORM_PCREL_LOCAL);
}
/* Functions and data structures relating to 128-bit constants that are
converted to byte, half-word, word, and double-word values. All fields are
kept in big endian order. We also convert scalar values to 128-bits if they
are going to be loaded into vector registers. */
#define VECTOR_128BIT_BITS 128
#define VECTOR_128BIT_BYTES (128 / 8)
#define VECTOR_128BIT_HALF_WORDS (128 / 16)
#define VECTOR_128BIT_WORDS (128 / 32)
#define VECTOR_128BIT_DOUBLE_WORDS (128 / 64)
typedef struct {
/* Constant as various sized items. */
unsigned HOST_WIDE_INT double_words[VECTOR_128BIT_DOUBLE_WORDS];
unsigned int words[VECTOR_128BIT_WORDS];
unsigned short half_words[VECTOR_128BIT_HALF_WORDS];
unsigned char bytes[VECTOR_128BIT_BYTES];
unsigned original_size; /* Constant size before splat. */
bool fp_constant_p; /* Is the constant floating point? */
bool all_double_words_same; /* Are the double words all equal? */
bool all_words_same; /* Are the words all equal? */
bool all_half_words_same; /* Are the half words all equal? */
bool all_bytes_same; /* Are the bytes all equal? */
} vec_const_128bit_type;
extern bool vec_const_128bit_to_bytes (rtx, machine_mode,
vec_const_128bit_type *);
#endif /* RTX_CODE */
#ifdef TREE_CODE

View file

@ -28334,6 +28334,259 @@ rs6000_output_addr_vec_elt (FILE *file, int value)
fprintf (file, "\n");
}
/* Copy an integer constant to the vector constant structure. */
static void
constant_int_to_128bit_vector (rtx op,
machine_mode mode,
size_t byte_num,
vec_const_128bit_type *info)
{
unsigned HOST_WIDE_INT uvalue = UINTVAL (op);
unsigned bitsize = GET_MODE_BITSIZE (mode);
for (int shift = bitsize - 8; shift >= 0; shift -= 8)
info->bytes[byte_num++] = (uvalue >> shift) & 0xff;
}
/* Copy a floating point constant to the vector constant structure. */
static void
constant_fp_to_128bit_vector (rtx op,
machine_mode mode,
size_t byte_num,
vec_const_128bit_type *info)
{
unsigned bitsize = GET_MODE_BITSIZE (mode);
unsigned num_words = bitsize / 32;
const REAL_VALUE_TYPE *rtype = CONST_DOUBLE_REAL_VALUE (op);
long real_words[VECTOR_128BIT_WORDS];
/* Make sure we don't overflow the real_words array and that it is
filled completely. */
gcc_assert (num_words <= VECTOR_128BIT_WORDS && (bitsize % 32) == 0);
real_to_target (real_words, rtype, mode);
/* Iterate over each 32-bit word in the floating point constant. The
real_to_target function puts out words in target endian fashion. We need
to arrange the order so that the bytes are written in big endian order. */
for (unsigned num = 0; num < num_words; num++)
{
unsigned endian_num = (BYTES_BIG_ENDIAN
? num
: num_words - 1 - num);
unsigned uvalue = real_words[endian_num];
for (int shift = 32 - 8; shift >= 0; shift -= 8)
info->bytes[byte_num++] = (uvalue >> shift) & 0xff;
}
/* Mark that this constant involves floating point. */
info->fp_constant_p = true;
}
/* Convert a vector constant OP with mode MODE to a vector 128-bit constant
structure INFO.
Break out the constant out to bytes, half words, words, and double words.
Return true if we have successfully converted the constant.
We handle CONST_INT, CONST_DOUBLE, CONST_VECTOR, and VEC_DUPLICATE of
constants. Integer and floating point scalar constants are splatted to fill
out the vector. */
bool
vec_const_128bit_to_bytes (rtx op,
machine_mode mode,
vec_const_128bit_type *info)
{
/* Initialize the constant structure. */
memset ((void *)info, 0, sizeof (vec_const_128bit_type));
/* Assume CONST_INTs are DImode. */
if (mode == VOIDmode)
mode = CONST_INT_P (op) ? DImode : GET_MODE (op);
if (mode == VOIDmode)
return false;
unsigned size = GET_MODE_SIZE (mode);
bool splat_p = false;
if (size > VECTOR_128BIT_BYTES)
return false;
/* Set up the bits. */
switch (GET_CODE (op))
{
/* Integer constants, default to double word. */
case CONST_INT:
{
constant_int_to_128bit_vector (op, mode, 0, info);
splat_p = true;
break;
}
/* Floating point constants. */
case CONST_DOUBLE:
{
/* Fail if the floating point constant is the wrong mode. */
if (GET_MODE (op) != mode)
return false;
/* SFmode stored as scalars are stored in DFmode format. */
if (mode == SFmode)
{
mode = DFmode;
size = GET_MODE_SIZE (DFmode);
}
constant_fp_to_128bit_vector (op, mode, 0, info);
splat_p = true;
break;
}
/* Vector constants, iterate over each element. On little endian
systems, we have to reverse the element numbers. */
case CONST_VECTOR:
{
/* Fail if the vector constant is the wrong mode or size. */
if (GET_MODE (op) != mode
|| GET_MODE_SIZE (mode) != VECTOR_128BIT_BYTES)
return false;
machine_mode ele_mode = GET_MODE_INNER (mode);
size_t ele_size = GET_MODE_SIZE (ele_mode);
size_t nunits = GET_MODE_NUNITS (mode);
for (size_t num = 0; num < nunits; num++)
{
rtx ele = CONST_VECTOR_ELT (op, num);
size_t byte_num = (BYTES_BIG_ENDIAN
? num
: nunits - 1 - num) * ele_size;
if (CONST_INT_P (ele))
constant_int_to_128bit_vector (ele, ele_mode, byte_num, info);
else if (CONST_DOUBLE_P (ele))
constant_fp_to_128bit_vector (ele, ele_mode, byte_num, info);
else
return false;
}
break;
}
/* Treat VEC_DUPLICATE of a constant just like a vector constant.
Since we are duplicating the element, we don't have to worry about
endian issues. */
case VEC_DUPLICATE:
{
/* Fail if the vector duplicate is the wrong mode or size. */
if (GET_MODE (op) != mode
|| GET_MODE_SIZE (mode) != VECTOR_128BIT_BYTES)
return false;
machine_mode ele_mode = GET_MODE_INNER (mode);
size_t ele_size = GET_MODE_SIZE (ele_mode);
rtx ele = XEXP (op, 0);
size_t nunits = GET_MODE_NUNITS (mode);
if (!CONST_INT_P (ele) && !CONST_DOUBLE_P (ele))
return false;
for (size_t num = 0; num < nunits; num++)
{
size_t byte_num = num * ele_size;
if (CONST_INT_P (ele))
constant_int_to_128bit_vector (ele, ele_mode, byte_num, info);
else
constant_fp_to_128bit_vector (ele, ele_mode, byte_num, info);
}
break;
}
/* Any thing else, just return failure. */
default:
return false;
}
/* Splat the constant to fill 128 bits if desired. */
if (splat_p && size < VECTOR_128BIT_BYTES)
{
if ((VECTOR_128BIT_BYTES % size) != 0)
return false;
for (size_t offset = size;
offset < VECTOR_128BIT_BYTES;
offset += size)
memcpy ((void *) &info->bytes[offset],
(void *) &info->bytes[0],
size);
}
/* Remember original size. */
info->original_size = size;
/* Determine if the bytes are all the same. */
unsigned char first_byte = info->bytes[0];
info->all_bytes_same = true;
for (size_t i = 1; i < VECTOR_128BIT_BYTES; i++)
if (first_byte != info->bytes[i])
{
info->all_bytes_same = false;
break;
}
/* Pack half words together & determine if all of the half words are the
same. */
for (size_t i = 0; i < VECTOR_128BIT_HALF_WORDS; i++)
info->half_words[i] = ((info->bytes[i * 2] << 8)
| info->bytes[(i * 2) + 1]);
unsigned short first_hword = info->half_words[0];
info->all_half_words_same = true;
for (size_t i = 1; i < VECTOR_128BIT_HALF_WORDS; i++)
if (first_hword != info->half_words[i])
{
info->all_half_words_same = false;
break;
}
/* Pack words together & determine if all of the words are the same. */
for (size_t i = 0; i < VECTOR_128BIT_WORDS; i++)
info->words[i] = ((info->bytes[i * 4] << 24)
| (info->bytes[(i * 4) + 1] << 16)
| (info->bytes[(i * 4) + 2] << 8)
| info->bytes[(i * 4) + 3]);
info->all_words_same
= (info->words[0] == info->words[1]
&& info->words[0] == info->words[1]
&& info->words[0] == info->words[2]
&& info->words[0] == info->words[3]);
/* Pack double words together & determine if all of the double words are the
same. */
for (size_t i = 0; i < VECTOR_128BIT_DOUBLE_WORDS; i++)
{
unsigned HOST_WIDE_INT d_word = 0;
for (size_t j = 0; j < 8; j++)
d_word = (d_word << 8) | info->bytes[(i * 8) + j];
info->double_words[i] = d_word;
}
info->all_double_words_same
= (info->double_words[0] == info->double_words[1]);
return true;
}
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-rs6000.h"