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:
parent
d155c6fef0
commit
9e59e99a60
@ -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.
|
||||
|
@ -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.
|
||||
|
101
gcc/testsuite/g++.dg/tree-ssa/ssa-dse-1.C
Normal file
101
gcc/testsuite/g++.dg/tree-ssa/ssa-dse-1.C
Normal 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" } } */
|
||||
|
@ -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" } } */
|
||||
|
||||
|
62
gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-24.c
Normal file
62
gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-24.c
Normal 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" } } */
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user