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();
+}