Hi,
I am testing this variant of the patch with comment and more explicit
merging along the inline path.

Honza

diff --git a/gcc/ipa-modref.cc b/gcc/ipa-modref.cc
index fc00acecfce..312beebf07c 100644
--- a/gcc/ipa-modref.cc
+++ b/gcc/ipa-modref.cc
@@ -5342,13 +5342,41 @@ ipa_merge_modref_summary_after_inlining (cgraph_edge 
*edge)
                                      : NULL;
   class modref_summary_lto *callee_info_lto
                 = summaries_lto ? summaries_lto->get (edge->callee) : NULL;
+
+  /* Compute effective ECF_CONST, ECF_PURE, ECF_NOVOPS,
+     ECF_LOOPING_CONST_OR_PURE and ignore_stores of the inlined function from
+     the point of view of caller of the function it is transitively inlined to.
+     If we have inline chaing A->B->C
+     then ECF_CONST, ECF_PURE_ECF, ECF_NOVOPS and ignore_stores is the 
strongest
+     flag seen on the inline path.
+
+     ECF_LOOPING_CONST_OR_PURE is bit special since, for example if C
+     is ECF_CONST | ECF_LOOPING_CONST_OR_PURE and B is ECF_PURE, then outcome
+     is ECF_CONST and !ECF_LOOPING_CONST_OR_PURE.
+
+     Flags are later used to avoid merging info about side-effects of C which
+     are invisible to to the caller of A.  For example, it is possible for 
const
+     function to have local array and call functions modifying it.  */
   int flags = flags_from_decl_or_type (edge->callee->decl);
-  /* Combine in outer flags.  */
-  cgraph_node *n;
-  for (n = edge->caller; n->inlined_to; n = n->callers->caller)
-    flags |= flags_from_decl_or_type (n->decl);
-  flags |= flags_from_decl_or_type (n->decl);
-  bool ignore_stores = ignore_stores_p (edge->caller->decl, flags);
+  bool ignore_stores = ignore_stores_p (edge->callee->decl, flags);
+  for (cgraph_node *n = edge->caller; n;
+       n = n->inlined_to ? n->callers->caller : NULL)
+    {
+      int f = flags_from_decl_or_type (n->decl);
+
+      ignore_stores |= ignore_stores_p (n->decl, f);
+      /* If we see first CONST/PURE flag in the chain, take its
+        ECF_LOOPING_CONST_OR_PURE  */
+      if (!(flags & (ECF_CONST | ECF_PURE)) && (f & (ECF_CONST | ECF_PURE)))
+        flags |= (f & ECF_LOOPING_CONST_OR_PURE);
+      /* If we already have ECF_CONST or ECF_PURE flag
+        just improve ECF_LOOPING_CONST_OR_PURE if possible.  */
+      if ((flags & (ECF_CONST | ECF_PURE))
+         && (flags & ECF_LOOPING_CONST_OR_PURE)
+         && (f & (ECF_CONST | ECF_PURE)) && !(f & ECF_LOOPING_CONST_OR_PURE))
+       flags &= ECF_LOOPING_CONST_OR_PURE;
+      flags |= f & (ECF_CONST | ECF_PURE | ECF_NOVOPS);
+    }
 
   if (!callee_info && to_info)
     {
diff --git a/gcc/testsuite/g++.dg/torture/pr120987-1.C 
b/gcc/testsuite/g++.dg/torture/pr120987-1.C
new file mode 100644
index 00000000000..4209679bc03
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/pr120987-1.C
@@ -0,0 +1,57 @@
+// { dg-do run { target c++11 } }
+// { dg-skip-if "requires hosted libstdc++ for string/memory" { ! hostedlib } }
+// PR tree-optimization/120987
+
+#include <memory>
+#include <string>
+#include <cstdlib>
+
+#define ERROR_STRING  "012345678901234567"
+
+struct gdb_exception
+{
+  gdb_exception (const char *s)
+    : message (std::make_shared<std::string> (s))
+  {}
+
+  explicit gdb_exception (gdb_exception &&other) noexcept
+    : message (std::move (other.message))
+  {
+    volatile int a = 1;
+    if (a != 1)
+      abort ();
+  }
+  
+
+  std::shared_ptr<std::string> message;
+};
+
+void __attribute__((noinline, noclone))
+throw_exception (gdb_exception &&exception)
+{
+  throw gdb_exception (std::move (exception));
+}
+
+static void __attribute__((noinline, noclone))
+parse_linespec (void)
+{
+  struct gdb_exception file_exception (ERROR_STRING);
+  throw_exception (std::move (file_exception));
+}
+
+int
+main (void)
+{
+  try
+    {
+      parse_linespec ();
+    }
+  catch (const gdb_exception &e)
+    {
+      if (*e.message != ERROR_STRING)
+        __builtin_abort();
+      return 0;
+    }
+
+  __builtin_abort();
+}

Reply via email to