expr: Fix up constant_byte_string bitfield handling [PR98366]

constant_byte_string now uses a convert_to_bytes function, which doesn't
handle bitfields at all (don't punt on them, just puts them into wrong bits
or bytes).  Furthermore, I don't see a reason why that function should exist
at all, it duplicates native_encode_initializer functionality.
Except that native_encode_initializer punted on flexible array members and 2
tests in the testsuite relied on constant_byte_string handling those.
So, this patch throws away convert_to_bytes, uses native_encode_initializer
instead, but teaches it to handle flexible array members (only in the
non-mask mode with off == -1 for now), furthermore, it adds various corner
case checks that the old implementation was missing (like that STRING_CSTs
use int as length and therefore we shouldn't try to build larger than that
strings, or that native_encode*/native_interpret* APIs require sane
host and target bytes (8-bit on both).

2020-12-19  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/98366
	* fold-const.c (native_encode_initializer): Don't try to
	memset more than total_bytes with off == -1 even if len is large.
	Handle flexible array member initializers if off == -1 and mask is
	NULL.
	* expr.c (convert_to_bytes): Remove.
	(constant_byte_string): Use native_encode_initializer instead of
	convert_to_bytes.  Remove extraneous semicolon.  Punt on various
	corner-cases the APIs don't handle, like sizes > INT_MAX,
	BITS_PER_UNIT != 8, CHAR_BIT != 8.

	* gcc.c-torture/execute/pr98366.c: New test.
This commit is contained in:
Jakub Jelinek 2020-12-19 22:24:10 +01:00
parent e9e4ddfc5a
commit 69165332a9
3 changed files with 103 additions and 123 deletions

View file

@ -11631,111 +11631,6 @@ is_aligning_offset (const_tree offset, const_tree exp)
return TREE_CODE (offset) == ADDR_EXPR && TREE_OPERAND (offset, 0) == exp;
}
/* If EXPR is a constant initializer (either an expression or CONSTRUCTOR),
attempt to obtain its native representation as an array of nonzero BYTES.
Return true on success and false on failure (the latter without modifying
BYTES). */
static bool
convert_to_bytes (tree type, tree expr, vec<unsigned char> *bytes)
{
if (TREE_CODE (expr) == CONSTRUCTOR)
{
/* Set to the size of the CONSTRUCTOR elements. */
unsigned HOST_WIDE_INT ctor_size = bytes->length ();
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree val, idx;
tree eltype = TREE_TYPE (type);
unsigned HOST_WIDE_INT elsize =
tree_to_uhwi (TYPE_SIZE_UNIT (eltype));
/* Jump through hoops to determine the lower bound for languages
like Ada that can set it to an (almost) arbitrary value. */
tree dom = TYPE_DOMAIN (type);
if (!dom)
return false;
tree min = TYPE_MIN_VALUE (dom);
if (!min || !tree_fits_uhwi_p (min))
return false;
unsigned HOST_WIDE_INT i, last_idx = tree_to_uhwi (min) - 1;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, idx, val)
{
/* Append zeros for elements with no initializers. */
if (!tree_fits_uhwi_p (idx))
return false;
unsigned HOST_WIDE_INT cur_idx = tree_to_uhwi (idx);
if (unsigned HOST_WIDE_INT size = cur_idx - (last_idx + 1))
{
size = size * elsize + bytes->length ();
bytes->safe_grow_cleared (size, true);
}
if (!convert_to_bytes (eltype, val, bytes))
return false;
last_idx = cur_idx;
}
}
else if (TREE_CODE (type) == RECORD_TYPE)
{
tree val, fld;
unsigned HOST_WIDE_INT i;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, fld, val)
{
/* Append zeros for members with no initializers and
any padding. */
unsigned HOST_WIDE_INT cur_off = int_byte_position (fld);
if (bytes->length () < cur_off)
bytes->safe_grow_cleared (cur_off, true);
if (!convert_to_bytes (TREE_TYPE (val), val, bytes))
return false;
}
}
else
return false;
/* Compute the size of the COSNTRUCTOR elements. */
ctor_size = bytes->length () - ctor_size;
/* Append zeros to the byte vector to the full size of the type.
The type size can be less than the size of the CONSTRUCTOR
if the latter contains initializers for a flexible array
member. */
tree size = TYPE_SIZE_UNIT (type);
unsigned HOST_WIDE_INT type_size = tree_to_uhwi (size);
if (ctor_size < type_size)
if (unsigned HOST_WIDE_INT size_grow = type_size - ctor_size)
bytes->safe_grow_cleared (bytes->length () + size_grow, true);
return true;
}
/* Except for RECORD_TYPE which may have an initialized flexible array
member, the size of a type is the same as the size of the initializer
(including any implicitly zeroed out members and padding). Allocate
just enough for that many bytes. */
tree expr_size = TYPE_SIZE_UNIT (TREE_TYPE (expr));
if (!expr_size || !tree_fits_uhwi_p (expr_size))
return false;
const unsigned HOST_WIDE_INT expr_bytes = tree_to_uhwi (expr_size);
const unsigned bytes_sofar = bytes->length ();
/* native_encode_expr can convert at most INT_MAX bytes. vec is limited
to at most UINT_MAX. */
if (bytes_sofar + expr_bytes > INT_MAX)
return false;
/* Unlike for RECORD_TYPE, there is no need to clear the memory since
it's completely overwritten by native_encode_expr. */
bytes->safe_grow (bytes_sofar + expr_bytes, true);
unsigned char *pnext = bytes->begin () + bytes_sofar;
int nbytes = native_encode_expr (expr, pnext, expr_bytes, 0);
/* NBYTES is zero on failure. Otherwise it should equal EXPR_BYTES. */
return (unsigned HOST_WIDE_INT) nbytes == expr_bytes;
}
/* Return a STRING_CST corresponding to ARG's constant initializer either
if it's a string constant, or, when VALREP is set, any other constant,
or null otherwise.
@ -11748,7 +11643,7 @@ static tree
constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl,
bool valrep = false)
{
tree dummy = NULL_TREE;;
tree dummy = NULL_TREE;
if (!mem_size)
mem_size = &dummy;
@ -11903,18 +11798,42 @@ constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl,
if (!base_off.is_constant (&cstoff))
return NULL_TREE;
/* Check that the host and target are sane. */
if (CHAR_BIT != 8 || BITS_PER_UNIT != 8)
return NULL_TREE;
HOST_WIDE_INT typesz = int_size_in_bytes (TREE_TYPE (init));
if (typesz <= 0 || (int) typesz != typesz)
return NULL_TREE;
HOST_WIDE_INT size = typesz;
if (VAR_P (array)
&& DECL_SIZE_UNIT (array)
&& tree_fits_shwi_p (DECL_SIZE_UNIT (array)))
{
size = tree_to_shwi (DECL_SIZE_UNIT (array));
gcc_checking_assert (size >= typesz);
}
/* If value representation was requested convert the initializer
for the whole array or object into a string of bytes forming
its value representation and return it. */
auto_vec<unsigned char> bytes;
if (!convert_to_bytes (TREE_TYPE (init), init, &bytes))
return NULL_TREE;
unsigned char *bytes = XNEWVEC (unsigned char, size);
int r = native_encode_initializer (init, bytes, size);
if (r < typesz)
{
XDELETEVEC (bytes);
return NULL_TREE;
}
unsigned n = bytes.length ();
const char *p = reinterpret_cast<const char *>(bytes.address ());
init = build_string_literal (n, p, char_type_node);
if (r < size)
memset (bytes + r, '\0', size - r);
const char *p = reinterpret_cast<const char *>(bytes);
init = build_string_literal (size, p, char_type_node);
init = TREE_OPERAND (init, 0);
init = TREE_OPERAND (init, 0);
XDELETE (bytes);
*mem_size = size_int (TREE_STRING_LENGTH (init));
*ptr_offset = wide_int_to_tree (ssizetype, base_off);
@ -11965,6 +11884,10 @@ constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl,
&& (TREE_CODE (TREE_TYPE (array)) == INTEGER_TYPE
|| TYPE_MAIN_VARIANT (inittype) == char_type_node))
{
/* Check that the host and target are sane. */
if (CHAR_BIT != 8 || BITS_PER_UNIT != 8)
return NULL_TREE;
/* For a reference to (address of) a single constant character,
store the native representation of the character in CHARBUF.
If the reference is to an element of an array or a member
@ -12007,6 +11930,9 @@ constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl,
initsize = integer_zero_node;
unsigned HOST_WIDE_INT size = tree_to_uhwi (initsize);
if (size > (unsigned HOST_WIDE_INT) INT_MAX)
return NULL_TREE;
init = build_string_literal (size, NULL, chartype, size);
init = TREE_OPERAND (init, 0);
init = TREE_OPERAND (init, 0);

View file

@ -8197,7 +8197,7 @@ native_encode_initializer (tree init, unsigned char *ptr, int len,
gcc_assert (TREE_CODE (type) == RECORD_TYPE || mask == NULL);
if (ptr != NULL)
memset (ptr, '\0', MIN (total_bytes - off, len));
memset (ptr, '\0', MIN (total_bytes - o, len));
for (cnt = 0; ; cnt++)
{
tree val = NULL_TREE, field = NULL_TREE;
@ -8266,11 +8266,33 @@ native_encode_initializer (tree init, unsigned char *ptr, int len,
if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
&& TYPE_DOMAIN (TREE_TYPE (field))
&& ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (field))))
return 0;
if (DECL_SIZE_UNIT (field) == NULL_TREE
|| !tree_fits_shwi_p (DECL_SIZE_UNIT (field)))
return 0;
fieldsize = tree_to_shwi (DECL_SIZE_UNIT (field));
{
if (mask || off != -1)
return 0;
if (val == NULL_TREE)
continue;
if (TREE_CODE (TREE_TYPE (val)) != ARRAY_TYPE)
return 0;
fieldsize = int_size_in_bytes (TREE_TYPE (val));
if (fieldsize < 0
|| (int) fieldsize != fieldsize
|| (pos + fieldsize) > INT_MAX)
return 0;
if (pos + fieldsize > total_bytes)
{
if (ptr != NULL && total_bytes - o < len)
memset (ptr + (total_bytes - o),
'\0', MIN (pos + fieldsize - o, len));
total_bytes = pos + fieldsize;
}
}
else
{
if (DECL_SIZE_UNIT (field) == NULL_TREE
|| !tree_fits_shwi_p (DECL_SIZE_UNIT (field)))
return 0;
fieldsize = tree_to_shwi (DECL_SIZE_UNIT (field));
}
if (fieldsize == 0)
continue;
@ -8439,12 +8461,31 @@ native_encode_initializer (tree init, unsigned char *ptr, int len,
|| (pos >= off
&& (pos + fieldsize <= (HOST_WIDE_INT) off + len)))
{
if (!native_encode_initializer (val, ptr ? ptr + pos - o
: NULL,
fieldsize,
off == -1 ? -1 : 0,
mask ? mask + pos : NULL))
int fldsize = fieldsize;
if (off == -1)
{
tree fld = DECL_CHAIN (field);
while (fld)
{
if (TREE_CODE (fld) == FIELD_DECL)
break;
fld = DECL_CHAIN (fld);
}
if (fld == NULL_TREE)
fldsize = len - pos;
}
r = native_encode_initializer (val, ptr ? ptr + pos - o
: NULL,
fldsize,
off == -1 ? -1 : 0,
mask ? mask + pos : NULL);
if (!r)
return 0;
if (off == -1
&& fldsize != fieldsize
&& r > fieldsize
&& pos + r > total_bytes)
total_bytes = pos + r;
}
else
{

View file

@ -0,0 +1,13 @@
/* PR middle-end/98366 */
/* { dg-require-effective-target int32 } */
typedef struct S { int a, b, c : 7, d : 8, e : 17; } S;
const S f[] = { {0, 3, 4, 2, 0} };
int
main ()
{
if (__builtin_memcmp (f, (S[]){{.b = 3, .c = 4, .d = 2, .e = 0}}, sizeof (S)))
__builtin_abort ();
return 0;
}