re PR tree-optimization/93210 (Sub-optimal code optimization on struct/combound constexpr (gcc vs. clang))

PR tree-optimization/93210
	* fold-const.h (native_encode_initializer,
	can_native_interpret_type_p): Declare.
	* fold-const.c (native_encode_string): Fix up handling with off != -1,
	simplify.
	(native_encode_initializer): New function, moved from dwarf2out.c.
	Adjust to native_encode_expr compatible arguments, including dry-run
	and partial extraction modes.  Don't handle STRING_CST.
	(can_native_interpret_type_p): No longer static.
	* gimple-fold.c (fold_ctor_reference): For native_encode_expr, verify
	offset / BITS_PER_UNIT fits into int and don't call it if
	can_native_interpret_type_p fails.  If suboff is NULL and for
	CONSTRUCTOR fold_{,non}array_ctor_reference returns NULL, retry with
	native_encode_initializer.
	(fold_const_aggregate_ref_1): Formatting fix.
	* dwarf2out.c (native_encode_initializer): Moved to fold-const.c.
	(tree_add_const_value_attribute): Adjust caller.

	* gcc.dg/pr93210.c: New test.
	* g++.dg/opt/pr93210.C: New test.

From-SVN: r280141
This commit is contained in:
Jakub Jelinek 2020-01-10 22:18:22 +01:00 committed by Jakub Jelinek
parent 974bb8a4dc
commit ea69031c5f
8 changed files with 373 additions and 158 deletions

View file

@ -1,5 +1,23 @@
2020-01-10 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/93210
* fold-const.h (native_encode_initializer,
can_native_interpret_type_p): Declare.
* fold-const.c (native_encode_string): Fix up handling with off != -1,
simplify.
(native_encode_initializer): New function, moved from dwarf2out.c.
Adjust to native_encode_expr compatible arguments, including dry-run
and partial extraction modes. Don't handle STRING_CST.
(can_native_interpret_type_p): No longer static.
* gimple-fold.c (fold_ctor_reference): For native_encode_expr, verify
offset / BITS_PER_UNIT fits into int and don't call it if
can_native_interpret_type_p fails. If suboff is NULL and for
CONSTRUCTOR fold_{,non}array_ctor_reference returns NULL, retry with
native_encode_initializer.
(fold_const_aggregate_ref_1): Formatting fix.
* dwarf2out.c (native_encode_initializer): Moved to fold-const.c.
(tree_add_const_value_attribute): Adjust caller.
PR tree-optimization/90838
* tree-ssa-forwprop.c (simplify_count_trailing_zeroes): Use
SCALAR_INT_TYPE_MODE instead of TYPE_MODE as operand of

View file

@ -20258,150 +20258,6 @@ add_location_or_const_value_attribute (dw_die_ref die, tree decl, bool cache_p)
return tree_add_const_value_attribute_for_decl (die, decl);
}
/* Helper function for tree_add_const_value_attribute. Natively encode
initializer INIT into an array. Return true if successful. */
static bool
native_encode_initializer (tree init, unsigned char *array, int size)
{
tree type;
if (init == NULL_TREE)
return false;
STRIP_NOPS (init);
switch (TREE_CODE (init))
{
case STRING_CST:
type = TREE_TYPE (init);
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree enttype = TREE_TYPE (type);
scalar_int_mode mode;
if (!is_int_mode (TYPE_MODE (enttype), &mode)
|| GET_MODE_SIZE (mode) != 1)
return false;
if (int_size_in_bytes (type) != size)
return false;
if (size > TREE_STRING_LENGTH (init))
{
memcpy (array, TREE_STRING_POINTER (init),
TREE_STRING_LENGTH (init));
memset (array + TREE_STRING_LENGTH (init),
'\0', size - TREE_STRING_LENGTH (init));
}
else
memcpy (array, TREE_STRING_POINTER (init), size);
return true;
}
return false;
case CONSTRUCTOR:
type = TREE_TYPE (init);
if (int_size_in_bytes (type) != size)
return false;
if (TREE_CODE (type) == ARRAY_TYPE)
{
HOST_WIDE_INT min_index;
unsigned HOST_WIDE_INT cnt;
int curpos = 0, fieldsize;
constructor_elt *ce;
if (TYPE_DOMAIN (type) == NULL_TREE
|| !tree_fits_shwi_p (TYPE_MIN_VALUE (TYPE_DOMAIN (type))))
return false;
fieldsize = int_size_in_bytes (TREE_TYPE (type));
if (fieldsize <= 0)
return false;
min_index = tree_to_shwi (TYPE_MIN_VALUE (TYPE_DOMAIN (type)));
memset (array, '\0', size);
FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce)
{
tree val = ce->value;
tree index = ce->index;
int pos = curpos;
if (index && TREE_CODE (index) == RANGE_EXPR)
pos = (tree_to_shwi (TREE_OPERAND (index, 0)) - min_index)
* fieldsize;
else if (index)
pos = (tree_to_shwi (index) - min_index) * fieldsize;
if (val)
{
STRIP_NOPS (val);
if (!native_encode_initializer (val, array + pos, fieldsize))
return false;
}
curpos = pos + fieldsize;
if (index && TREE_CODE (index) == RANGE_EXPR)
{
int count = tree_to_shwi (TREE_OPERAND (index, 1))
- tree_to_shwi (TREE_OPERAND (index, 0));
while (count-- > 0)
{
if (val)
memcpy (array + curpos, array + pos, fieldsize);
curpos += fieldsize;
}
}
gcc_assert (curpos <= size);
}
return true;
}
else if (TREE_CODE (type) == RECORD_TYPE
|| TREE_CODE (type) == UNION_TYPE)
{
tree field = NULL_TREE;
unsigned HOST_WIDE_INT cnt;
constructor_elt *ce;
if (int_size_in_bytes (type) != size)
return false;
if (TREE_CODE (type) == RECORD_TYPE)
field = TYPE_FIELDS (type);
FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce)
{
tree val = ce->value;
int pos, fieldsize;
if (ce->index != 0)
field = ce->index;
if (val)
STRIP_NOPS (val);
if (field == NULL_TREE || DECL_BIT_FIELD (field))
return false;
if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
&& TYPE_DOMAIN (TREE_TYPE (field))
&& ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (field))))
return false;
else if (DECL_SIZE_UNIT (field) == NULL_TREE
|| !tree_fits_shwi_p (DECL_SIZE_UNIT (field)))
return false;
fieldsize = tree_to_shwi (DECL_SIZE_UNIT (field));
pos = int_byte_position (field);
gcc_assert (pos + fieldsize <= size);
if (val && fieldsize != 0
&& !native_encode_initializer (val, array + pos, fieldsize))
return false;
}
return true;
}
return false;
case VIEW_CONVERT_EXPR:
case NON_LVALUE_EXPR:
return native_encode_initializer (TREE_OPERAND (init, 0), array, size);
default:
return native_encode_expr (init, array, size) == size;
}
}
/* Attach a DW_AT_const_value attribute to DIE. The value of the
attribute is the const value T. */
@ -20446,7 +20302,7 @@ tree_add_const_value_attribute (dw_die_ref die, tree t)
{
unsigned char *array = ggc_cleared_vec_alloc<unsigned char> (size);
if (native_encode_initializer (init, array, size))
if (native_encode_initializer (init, array, size) == size)
{
add_AT_vec (die, DW_AT_const_value, size, 1, array);
return true;

View file

@ -7837,9 +7837,10 @@ native_encode_string (const_tree expr, unsigned char *ptr, int len, int off)
return 0;
if (off == -1)
off = 0;
len = MIN (total_bytes - off, len);
if (ptr == NULL)
/* Dry run. */;
else if (TREE_STRING_LENGTH (expr) - off < MIN (total_bytes, len))
else
{
int written = 0;
if (off < TREE_STRING_LENGTH (expr))
@ -7847,12 +7848,9 @@ native_encode_string (const_tree expr, unsigned char *ptr, int len, int off)
written = MIN (len, TREE_STRING_LENGTH (expr) - off);
memcpy (ptr, TREE_STRING_POINTER (expr) + off, written);
}
memset (ptr + written, 0,
MIN (total_bytes - written, len - written));
memset (ptr + written, 0, len - written);
}
else
memcpy (ptr, TREE_STRING_POINTER (expr) + off, MIN (total_bytes, len));
return MIN (total_bytes - off, len);
return len;
}
@ -7895,6 +7893,213 @@ native_encode_expr (const_tree expr, unsigned char *ptr, int len, int off)
}
}
/* Similar to native_encode_expr, but also handle CONSTRUCTORs, VCEs,
NON_LVALUE_EXPRs and nops. */
int
native_encode_initializer (tree init, unsigned char *ptr, int len,
int off)
{
/* We don't support starting at negative offset and -1 is special. */
if (off < -1 || init == NULL_TREE)
return 0;
STRIP_NOPS (init);
switch (TREE_CODE (init))
{
case VIEW_CONVERT_EXPR:
case NON_LVALUE_EXPR:
return native_encode_initializer (TREE_OPERAND (init, 0), ptr, len, off);
default:
return native_encode_expr (init, ptr, len, off);
case CONSTRUCTOR:
tree type = TREE_TYPE (init);
HOST_WIDE_INT total_bytes = int_size_in_bytes (type);
if (total_bytes < 0)
return 0;
if ((off == -1 && total_bytes > len) || off >= total_bytes)
return 0;
int o = off == -1 ? 0 : off;
if (TREE_CODE (type) == ARRAY_TYPE)
{
HOST_WIDE_INT min_index;
unsigned HOST_WIDE_INT cnt;
HOST_WIDE_INT curpos = 0, fieldsize;
constructor_elt *ce;
if (TYPE_DOMAIN (type) == NULL_TREE
|| !tree_fits_shwi_p (TYPE_MIN_VALUE (TYPE_DOMAIN (type))))
return 0;
fieldsize = int_size_in_bytes (TREE_TYPE (type));
if (fieldsize <= 0)
return 0;
min_index = tree_to_shwi (TYPE_MIN_VALUE (TYPE_DOMAIN (type)));
if (ptr != NULL)
memset (ptr, '\0', MIN (total_bytes - off, len));
FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce)
{
tree val = ce->value;
tree index = ce->index;
HOST_WIDE_INT pos = curpos, count = 0;
bool full = false;
if (index && TREE_CODE (index) == RANGE_EXPR)
{
if (!tree_fits_shwi_p (TREE_OPERAND (index, 0))
|| !tree_fits_shwi_p (TREE_OPERAND (index, 1)))
return 0;
pos = (tree_to_shwi (TREE_OPERAND (index, 0)) - min_index)
* fieldsize;
count = (tree_to_shwi (TREE_OPERAND (index, 1))
- tree_to_shwi (TREE_OPERAND (index, 0)));
}
else if (index)
{
if (!tree_fits_shwi_p (index))
return 0;
pos = (tree_to_shwi (index) - min_index) * fieldsize;
}
curpos = pos;
if (val)
do
{
if (off == -1
|| (curpos >= off
&& (curpos + fieldsize
<= (HOST_WIDE_INT) off + len)))
{
if (full)
{
if (ptr)
memcpy (ptr + (curpos - o), ptr + (pos - o),
fieldsize);
}
else if (!native_encode_initializer (val,
ptr
? ptr + curpos - o
: NULL,
fieldsize,
off == -1 ? -1
: 0))
return 0;
else
{
full = true;
pos = curpos;
}
}
else if (curpos + fieldsize > off
&& curpos < (HOST_WIDE_INT) off + len)
{
/* Partial overlap. */
unsigned char *p = NULL;
int no = 0;
int l;
if (curpos >= off)
{
if (ptr)
p = ptr + curpos - off;
l = MIN ((HOST_WIDE_INT) off + len - curpos,
fieldsize);
}
else
{
p = ptr;
no = off - curpos;
l = len;
}
if (!native_encode_initializer (val, p, l, no))
return 0;
}
curpos += fieldsize;
}
while (count-- != 0);
}
return MIN (total_bytes - off, len);
}
else if (TREE_CODE (type) == RECORD_TYPE
|| TREE_CODE (type) == UNION_TYPE)
{
unsigned HOST_WIDE_INT cnt;
constructor_elt *ce;
if (ptr != NULL)
memset (ptr, '\0', MIN (total_bytes - off, len));
FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce)
{
tree field = ce->index;
tree val = ce->value;
HOST_WIDE_INT pos, fieldsize;
if (field == NULL_TREE)
return 0;
pos = int_byte_position (field);
if (off != -1 && (HOST_WIDE_INT) off + len <= pos)
continue;
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 (fieldsize == 0)
continue;
if (off != -1 && pos + fieldsize <= off)
continue;
if (DECL_BIT_FIELD (field))
return 0;
if (val == NULL_TREE)
continue;
if (off == -1
|| (pos >= off
&& (pos + fieldsize <= (HOST_WIDE_INT) off + len)))
{
if (!native_encode_initializer (val, ptr ? ptr + pos - o
: NULL,
fieldsize,
off == -1 ? -1 : 0))
return 0;
}
else
{
/* Partial overlap. */
unsigned char *p = NULL;
int no = 0;
int l;
if (pos >= off)
{
if (ptr)
p = ptr + pos - off;
l = MIN ((HOST_WIDE_INT) off + len - pos,
fieldsize);
}
else
{
p = ptr;
no = off - pos;
l = len;
}
if (!native_encode_initializer (val, p, l, no))
return 0;
}
}
return MIN (total_bytes - off, len);
}
return 0;
}
}
/* Subroutine of native_interpret_expr. Interpret the contents of
the buffer PTR of length LEN as an INTEGER_CST of type TYPE.
@ -8129,7 +8334,7 @@ native_interpret_expr (tree type, const unsigned char *ptr, int len)
/* Returns true if we can interpret the contents of a native encoding
as TYPE. */
static bool
bool
can_native_interpret_type_p (tree type)
{
switch (TREE_CODE (type))

View file

@ -26,7 +26,10 @@ extern int folding_initializer;
/* Convert between trees and native memory representation. */
extern int native_encode_expr (const_tree, unsigned char *, int, int off = -1);
extern int native_encode_initializer (tree, unsigned char *, int,
int off = -1);
extern tree native_interpret_expr (tree, const unsigned char *, int);
extern bool can_native_interpret_type_p (tree);
/* Fold constants as much as possible in an expression.
Returns the simplified expression.

View file

@ -6919,8 +6919,10 @@ fold_ctor_reference (tree type, tree ctor, const poly_uint64 &poly_offset,
if (CONSTANT_CLASS_P (ctor)
&& BITS_PER_UNIT == 8
&& offset % BITS_PER_UNIT == 0
&& offset / BITS_PER_UNIT <= INT_MAX
&& size % BITS_PER_UNIT == 0
&& size <= MAX_BITSIZE_MODE_ANY_MODE)
&& size <= MAX_BITSIZE_MODE_ANY_MODE
&& can_native_interpret_type_p (type))
{
unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
int len = native_encode_expr (ctor, buf, size / BITS_PER_UNIT,
@ -6934,13 +6936,35 @@ fold_ctor_reference (tree type, tree ctor, const poly_uint64 &poly_offset,
if (!suboff)
suboff = &dummy;
tree ret;
if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (ctor)) == VECTOR_TYPE)
return fold_array_ctor_reference (type, ctor, offset, size,
from_decl, suboff);
ret = fold_array_ctor_reference (type, ctor, offset, size,
from_decl, suboff);
else
ret = fold_nonarray_ctor_reference (type, ctor, offset, size,
from_decl, suboff);
return fold_nonarray_ctor_reference (type, ctor, offset, size,
from_decl, suboff);
/* Fall back to native_encode_initializer. Needs to be done
only in the outermost fold_ctor_reference call (because it itself
recurses into CONSTRUCTORs) and doesn't update suboff. */
if (ret == NULL_TREE
&& suboff == &dummy
&& BITS_PER_UNIT == 8
&& offset % BITS_PER_UNIT == 0
&& offset / BITS_PER_UNIT <= INT_MAX
&& size % BITS_PER_UNIT == 0
&& size <= MAX_BITSIZE_MODE_ANY_MODE
&& can_native_interpret_type_p (type))
{
unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
int len = native_encode_initializer (ctor, buf, size / BITS_PER_UNIT,
offset / BITS_PER_UNIT);
if (len > 0)
return native_interpret_expr (type, buf, len);
}
return ret;
}
return NULL_TREE;
@ -7049,7 +7073,7 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
tree c = fold_const_aggregate_ref_1 (TREE_OPERAND (t, 0), valueize);
if (c && TREE_CODE (c) == COMPLEX_CST)
return fold_build1_loc (EXPR_LOCATION (t),
TREE_CODE (t), TREE_TYPE (t), c);
TREE_CODE (t), TREE_TYPE (t), c);
break;
}

View file

@ -1,3 +1,9 @@
2020-01-10 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/93210
* gcc.dg/pr93210.c: New test.
* g++.dg/opt/pr93210.C: New test.
2020-01-10 Vladimir Makarov <vmakarov@redhat.com>
PR inline-asm/93027

View file

@ -0,0 +1,37 @@
// PR tree-optimization/93210
// { dg-do compile { target c++11 } }
// { dg-options "-O2 -fdump-tree-optimized" }
// { dg-final { scan-tree-dump-not "static_member\.d" "optimized" } }
union U { struct { unsigned int a, b; } c; unsigned long long d; };
inline
bool operator == (U const &x, U const &y) noexcept
{
return x.d == y.d;
};
struct S
{
static constexpr U static_member = { { 13, 42 } };
bool foo (U const &y) const noexcept;
bool bar (U const &y) const noexcept;
};
#if __cpp_inline_variables < 201606L
constexpr U S::static_member;
#endif
#if __SIZEOF_INT__ * 2 == __SIZEOF_LONG_LONG__
bool
S::foo (U const &y) const noexcept
{
return static_member == y;
}
bool
S::bar (U const &y) const noexcept
{
return U (static_member) == y;
}
#endif

View file

@ -0,0 +1,66 @@
/* PR tree-optimization/93210 */
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-optimized" } */
/* { dg-final { scan-tree-dump-times "return \[0-9]\[0-9a-fA-FxX]*;" 31 "optimized" } } */
#ifdef __SIZEOF_INT128__
typedef unsigned __int128 L;
#else
typedef unsigned long long L;
#endif
struct S { signed char a, b; unsigned char c; };
struct T { signed char d; struct S e[25]; signed char f; };
union U { struct T g; L h[10]; };
const union U u = { { 1, { { 2, 3, 4 }, { 5, 6, 7 }, { 8, 9, 10 },
{ 12, 13, 14 }, { 15, 16, 17 }, { 18, 19, 20 },
{ 22, 23, 24 }, { 25, 26, 27 }, { 28, 29, 30 },
{ 32, 33, 34 }, { 35, 36, 37 }, { 38, 39, 40 },
{ 42, 43, 44 }, { 45, 46, 47 }, { 48, 49, 50 },
{ 52, 53, 54 }, { 55, 56, 57 }, { 58, 59, 60 },
{ 62, 63, 64 }, { 65, 66, 67 }, { 68, 69, 70 },
{ 72, 73, 74 }, { 75, 76, 77 }, { 78, 79, 80 },
{ 82, 83, 84 } }, 85 } };
const union U v = { { 1, { { 2, 3, 4 }, [1 ... 23] = { 5, 6, 7 },
{ 8, 9, 10 } }, 86 } };
struct A { char a[5]; char b[16]; char c[7]; };
union V { struct A d; unsigned int e[10]; };
const union V w = { { "abcde", "ijkl", "mnopqr" } };
#define N(n) __attribute__((noipa)) L foo##n (void) { return u.h[n]; }
#define M N(0) N(1) N(2) N(3) N(4) N(5) N(6) N(7) N(8) N(9)
M
#undef N
#define N(n) __attribute__((noipa)) L bar##n (void) { return v.h[n]; }
M
#undef N
#define N(n) __attribute__((noipa)) L baz##n (void) { return w.e[n]; }
M
typedef L (*F) (void);
F arr[30] = {
#undef N
#define N(n) foo##n,
M
#undef N
#define N(n) bar##n,
M
#undef N
#define N(n) baz##n,
M
};
int
main ()
{
const union U *p = &u;
const union U *q = &v;
const union V *r = &w;
__asm ("" : "+g" (p));
__asm ("" : "+g" (q));
__asm ("" : "+g" (r));
for (int i = 0; i < 10; i++)
if (arr[i] () != p->h[i]
|| arr[i + 10] () != q->h[i]
|| arr[i + 20] () != r->e[i])
__builtin_abort ();
return 0;
}