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 0000000..1fd8dec
--- /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<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" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr30375.c b/gcc/testsuite/gcc.dg/tree-ssa/pr30375.c
index 0439b1c..4494a2b 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 0000000..282194c
--- /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 daaa99c..83ce29b 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;
