From 9e59e99a6016420f36eb3a2a8a7912c58ef99a17 Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Fri, 13 Jan 2017 08:46:22 -0700 Subject: [PATCH] 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 --- gcc/ChangeLog | 12 ++- gcc/testsuite/ChangeLog | 12 ++- gcc/testsuite/g++.dg/tree-ssa/ssa-dse-1.C | 101 +++++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/pr30375.c | 3 +- gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-24.c | 62 +++++++++++++ gcc/tree-ssa-dse.c | 64 +++++++++++++ 6 files changed, 247 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/tree-ssa/ssa-dse-1.C create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-24.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3cb1a600c42..221aff93d49 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,8 +1,14 @@ 2017-01-13 Jeff Law - 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. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index edee5a4a633..bb9b824b187 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,8 +1,14 @@ 2017-01-13 Jeff Law - 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. diff --git a/gcc/testsuite/g++.dg/tree-ssa/ssa-dse-1.C b/gcc/testsuite/g++.dg/tree-ssa/ssa-dse-1.C new file mode 100644 index 00000000000..1fd8dec99e9 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/ssa-dse-1.C @@ -0,0 +1,101 @@ +/* { dg-do compile } */ +/* { dg-options "-std=c++14 -O -fdump-tree-dse1-details" } */ + +using uint = unsigned int; + +template +struct FixBuf +{ + C buf[S] = {}; +}; + +template +struct OutBuf +{ + C* cur; + C* end; + C* beg; + + template + constexpr + OutBuf(FixBuf& 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(r + '0'); + return *this; + } +}; + +template +struct BufOrSize +{ + template + static constexpr auto Select(FixBuf& fb, OutBuf&) + { + return fb; + } +}; + +template<> +struct BufOrSize +{ + template + static constexpr auto Select(FixBuf&, OutBuf& ob) + { + return ob.cur - ob.beg; + } +}; + +// if BOS=1, it will return the size of the generated data, else the data itself +template +constexpr +auto fixbuf() +{ + FixBuf fb; + OutBuf ob{fb}; + for (uint i = 0; i <= N; ++i) { + ob << i << static_cast(i == N ? 0 : ' '); + } + return BufOrSize::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 \\*\\)& \\+ \[0-9\]+B\\\] = {}" 1 "dse1" } } */ + diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr30375.c b/gcc/testsuite/gcc.dg/tree-ssa/pr30375.c index 0439b1c9ce2..4494a2b0bd6 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr30375.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr30375.c @@ -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" } } */ + diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-24.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-24.c new file mode 100644 index 00000000000..282194c1e32 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-24.c @@ -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" } } */ diff --git a/gcc/tree-ssa-dse.c b/gcc/tree-ssa-dse.c index daaa99c4dd8..83ce29b8670 100644 --- a/gcc/tree-ssa-dse.c +++ b/gcc/tree-ssa-dse.c @@ -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; -- 2.30.2