re PR middle-end/61912 (Missed (partial) dead store elimination for structures on GIMPLE)

PR tree-optimization/61912
	PR tree-optimization/77485
	* tree-ssa-dse.c: Include expr.h.
	(maybe_trim_constructor_store): New function.
	(maybe_trim_partially_dead_store): Call maybe_trim_constructor_store.

	PR tree-optimization/61912
	PR tree-optimization/77485
	* g++.dg/tree-ssa/ssa-dse-1.C: New test.
	* gcc.dg/tree-ssa/pr30375: Adjust expected output.
	* gcc.dg/tree-ssa/ssa-dse-24.c: New test.

From-SVN: r244443
This commit is contained in:
Jeff Law 2017-01-13 08:46:22 -07:00 committed by Jeff Law
parent d155c6fef0
commit 9e59e99a60
6 changed files with 247 additions and 7 deletions

View File

@ -1,8 +1,14 @@
2017-01-13 Jeff Law <law@redhat.com>
PR tree-optimization/33562
PR tree-optimization/61912
PR tree-optimization/77485
PR tree-optimization/61912
PR tree-optimization/77485
* tree-ssa-dse.c: Include expr.h.
(maybe_trim_constructor_store): New function.
(maybe_trim_partially_dead_store): Call maybe_trim_constructor_store.
PR tree-optimization/33562
PR tree-optimization/61912
PR tree-optimization/77485
* doc/invoke.texi: Document new dse-max-object-size param.
* params.def (PARM_DSE_MAX_OBJECT_SIZE): New PARAM.
* tree-ssa-dse.c: Include params.h.

View File

@ -1,8 +1,14 @@
2017-01-13 Jeff Law <law@redhat.com>
PR tree-optimization/33562
PR tree-optimization/61912
PR tree-optimization/77485
PR tree-optimization/61912
PR tree-optimization/77485
* g++.dg/tree-ssa/ssa-dse-1.C: New test.
* gcc.dg/tree-ssa/pr30375: Adjust expected output.
* gcc.dg/tree-ssa/ssa-dse-24.c: New test.
PR tree-optimization/33562
PR tree-optimization/61912
PR tree-optimization/77485
* gcc.dg/tree-ssa/complex-4.c: Remove xfail.
* gcc.dg/tree-ssa/complex-5.c: Likewise.
* gcc.dg/tree-ssa/ssa-dse-9.c: Likewise.

View File

@ -0,0 +1,101 @@
/* { dg-do compile } */
/* { dg-options "-std=c++14 -O -fdump-tree-dse1-details" } */
using uint = unsigned int;
template<typename C, uint S>
struct FixBuf
{
C buf[S] = {};
};
template<typename C>
struct OutBuf
{
C* cur;
C* end;
C* beg;
template<uint S>
constexpr
OutBuf(FixBuf<C, S>& b) : cur{b.buf}, end{b.buf + S}, beg{b.buf} { }
OutBuf(C* b, C* e) : cur{b}, end{e} { }
OutBuf(C* b, uint s) : cur{b}, end{b + s} { }
constexpr
OutBuf& operator<<(C v)
{
if (cur < end) {
*cur = v;
}
++cur;
return *this;
}
constexpr
OutBuf& operator<<(uint v)
{
uint q = v / 10U;
uint r = v % 10U;
if (q) {
*this << q;
}
*this << static_cast<C>(r + '0');
return *this;
}
};
template<bool BOS>
struct BufOrSize
{
template<typename C, uint S>
static constexpr auto Select(FixBuf<C, S>& fb, OutBuf<C>&)
{
return fb;
}
};
template<>
struct BufOrSize<true>
{
template<typename C, uint S>
static constexpr auto Select(FixBuf<C, S>&, OutBuf<C>& ob)
{
return ob.cur - ob.beg;
}
};
// if BOS=1, it will return the size of the generated data, else the data itself
template<uint N, uint S, bool BOS = 0>
constexpr
auto fixbuf()
{
FixBuf<char, S> fb;
OutBuf<char> ob{fb};
for (uint i = 0; i <= N; ++i) {
ob << i << static_cast<char>(i == N ? 0 : ' ');
}
return BufOrSize<BOS>::Select(fb, ob);
}
auto foo()
{
constexpr auto x = fixbuf<13, 200>();
return x;
}
auto foo_sized()
{
constexpr auto s = fixbuf<13, 0, 1>();
constexpr auto x = fixbuf<13, s>();
return x;
}
int main()
{
}
/* { dg-final { scan-tree-dump-times "MEM\\\[\\(struct FixBuf \\*\\)&<retval> \\+ \[0-9\]+B\\\] = {}" 1 "dse1" } } */

View File

@ -22,4 +22,5 @@ void test_signed_msg_encoding(void)
f();
}
/* { dg-final { scan-tree-dump-times "signInfo = {}" 1 "dse1" } } */
/* { dg-final { scan-tree-dump-times "MEM\\\[\\(struct _s \\*\\)&signInfo \\+ \[0-9\]+B\\\] = {}" 1 "dse1" } } */

View File

@ -0,0 +1,62 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-dse1" } */
typedef unsigned int wchar_t;
struct printf_info
{
int prec;
int width;
wchar_t spec;
unsigned int is_long_double:1;
unsigned int is_short:1;
unsigned int is_long:1;
unsigned int alt:1;
unsigned int space:1;
unsigned int left:1;
unsigned int showsign:1;
unsigned int group:1;
unsigned int extra:1;
unsigned int is_char:1;
unsigned int wide:1;
unsigned int i18n:1;
unsigned int __pad:4;
unsigned short int user;
wchar_t pad;
} info;
void bar (struct printf_info *);
void foo(int prec,
int width,
wchar_t spec,
unsigned int is_long_double,
unsigned int is_short,
unsigned int is_long,
unsigned int alt,
unsigned int space,
unsigned int left,
unsigned int showsign,
unsigned int group,
wchar_t pad)
{
struct printf_info info = {
.prec = prec,
.width = width,
.spec = spec,
.is_long_double = is_long_double,
.is_short = is_short,
.is_long = is_long,
.alt = alt,
.space = space,
.left = left,
.showsign = showsign,
.group = group,
.pad = pad,
.extra = 0,
.wide = sizeof (char) != 1 };
bar (&info);
}
/* { dg-final { scan-tree-dump-times "MEM\\\[\\(struct printf_info \\*\\)&info \\+ \[0-9\]+B\\\] = {}" 1 "dse1" } } */

View File

@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see
#include "domwalk.h"
#include "tree-cfgcleanup.h"
#include "params.h"
#include "alias.h"
/* This file implements dead store elimination.
@ -271,6 +272,66 @@ maybe_trim_complex_store (ao_ref *ref, sbitmap live, gimple *stmt)
are live. We do not try to optimize those cases. */
}
/* STMT initializes an object using a CONSTRUCTOR where one or more of the
bytes written are dead stores. ORIG is the bitmap of bytes stored by
STMT. LIVE is the bitmap of stores that are actually live.
Attempt to rewrite STMT so that only the real or imaginary part of
the object is actually stored.
The most common case for getting here is a CONSTRUCTOR with no elements
being used to zero initialize an object. We do not try to handle other
cases as those would force us to fully cover the object with the
CONSTRUCTOR node except for the components that are dead. */
static void
maybe_trim_constructor_store (ao_ref *ref, sbitmap live, gimple *stmt)
{
tree ctor = gimple_assign_rhs1 (stmt);
/* This is the only case we currently handle. It actually seems to
catch most cases of actual interest. */
gcc_assert (CONSTRUCTOR_NELTS (ctor) == 0);
int head_trim = 0;
int tail_trim = 0;
compute_trims (ref, live, &head_trim, &tail_trim);
/* Now we want to replace the constructor initializer
with memset (object + head_trim, 0, size - head_trim - tail_trim). */
if (head_trim || tail_trim)
{
/* We want &lhs for the MEM_REF expression. */
tree lhs_addr = build_fold_addr_expr (gimple_assign_lhs (stmt));
if (! is_gimple_min_invariant (lhs_addr))
return;
/* The number of bytes for the new constructor. */
int count = (ref->size / BITS_PER_UNIT) - head_trim - tail_trim;
/* And the new type for the CONSTRUCTOR. Essentially it's just
a char array large enough to cover the non-trimmed parts of
the original CONSTRUCTOR. Note we want explicit bounds here
so that we know how many bytes to clear when expanding the
CONSTRUCTOR. */
tree type = build_array_type_nelts (char_type_node, count);
/* Build a suitable alias type rather than using alias set zero
to avoid pessimizing. */
tree alias_type = reference_alias_ptr_type (gimple_assign_lhs (stmt));
/* Build a MEM_REF representing the whole accessed area, starting
at the first byte not trimmed. */
tree exp = fold_build2 (MEM_REF, type, lhs_addr,
build_int_cst (alias_type, head_trim));
/* Now update STMT with a new RHS and LHS. */
gimple_assign_set_lhs (stmt, exp);
gimple_assign_set_rhs1 (stmt, build_constructor (type, NULL));
}
}
/* STMT is a memory write where one or more bytes written are dead
stores. ORIG is the bitmap of bytes stored by STMT. LIVE is the
bitmap of stores that are actually live.
@ -287,6 +348,9 @@ maybe_trim_partially_dead_store (ao_ref *ref, sbitmap live, gimple *stmt)
{
switch (gimple_assign_rhs_code (stmt))
{
case CONSTRUCTOR:
maybe_trim_constructor_store (ref, live, stmt);
break;
case COMPLEX_CST:
maybe_trim_complex_store (ref, live, stmt);
break;