Hi,
this patch adds fnspecs for cxa_* functions in except.cc.  Main goal is to make
modref to see proper side-effects of functions which may throw. So in general
we get
 - cxa_allocate_exception
    which gets the same annotations as malloc (since it is kind of same thing)
 - cxa_free_exception
    which gets the same annotations as free
 - cxa_throw which is marked as const except for first parameter which is 
believed
    that it makes it escape (which is necessary) and modify (which is not 
necessary
    but it would matter inly if we would like to do constant propagation across
    EH edges.

Honza
 
gcc/cp/ChangeLog:

2022-06-23  Jan Hubicka  <hubi...@ucw.cz>

        * except.cc (declare_library_fn_1): Add fnspec parameter.
        (declare_library_fn): Add fnspec parameter.
        (do_allocate_exception): Declare fnspecs.
        (do_free_exception): Declare fnspecs.
        (build_throw): Declare fnspecs.

gcc/testsuite/ChangeLog:

2022-06-23  Jan Hubicka  <hubi...@ucw.cz>

        * g++.dg/opt/eh6.C: New test.
        * g++.dg/tree-ssa/kill.C: New test.

diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc
index da0a65c613d..bb9a5aee6da 100644
--- a/gcc/cp/except.cc
+++ b/gcc/cp/except.cc
@@ -133,13 +133,14 @@ build_exc_ptr (void)
 }
 
 /* Declare an exception ABI entry point called NAME.
-   ECF are the library flags, RTYPE the return type and ARGS[NARGS]
+   ECF are the library flags, FNSPEC is the attr "fn spec" string (or NULL),
+   RTYPE the return type and ARGS[NARGS]
    the parameter types.  We return the DECL -- which might be one
    found via the symbol table pushing, if the user already declared
    it.  If we pushed a new decl, the user will see it.  */
 
 static tree
-declare_library_fn_1 (const char *name, int ecf,
+declare_library_fn_1 (const char *name, int ecf, const char *fnspec,
                      tree rtype, int nargs, tree args[])
 {
   tree ident = get_identifier (name);
@@ -150,6 +151,14 @@ declare_library_fn_1 (const char *name, int ecf,
   for (unsigned ix = nargs; ix--;)
     arg_list = tree_cons (NULL_TREE, args[ix], arg_list);
   tree fntype = build_function_type (rtype, arg_list);
+  if (fnspec)
+    {
+      tree attr_args = build_tree_list (NULL_TREE,
+                                       build_string (strlen (fnspec), fnspec));
+      tree attrs = tree_cons (get_identifier ("fn spec"),
+                             attr_args, TYPE_ATTRIBUTES (fntype));
+      fntype = build_type_attribute_variant (fntype, attrs);
+    }
   tree res = push_library_fn (ident, fntype, except, ecf);
 
   return res;
@@ -157,7 +166,8 @@ declare_library_fn_1 (const char *name, int ecf,
 
 /* Find or declare a function NAME, returning RTYPE, taking a single
    parameter PTYPE, with an empty exception specification. ECF are the
-   library fn flags.  If TM_ECF is non-zero, also find or create a
+   library fn flags.  FNSPEC is the attr "fn spec" string (or NULL).
+   If TM_ECF is non-zero, also find or create a
    transaction variant and record it as a replacement, when flag_tm is
    in effect.
 
@@ -167,9 +177,10 @@ declare_library_fn_1 (const char *name, int ecf,
 
 static tree
 declare_library_fn (const char *name, tree rtype, tree ptype,
-                   int ecf, int tm_ecf)
+                   int ecf, int tm_ecf, const char *fnspec = NULL)
 {
-  tree res = declare_library_fn_1 (name, ecf, rtype, ptype ? 1 : 0, &ptype);
+  tree res = declare_library_fn_1 (name, ecf, fnspec,
+                                  rtype, ptype ? 1 : 0, &ptype);
   if (res == error_mark_node)
     return res;
 
@@ -177,7 +188,7 @@ declare_library_fn (const char *name, tree rtype, tree 
ptype,
     {
       char *tm_name = concat ("_ITM_", name + 2, NULL_TREE);
 
-      tree tm_fn = declare_library_fn_1 (tm_name, ecf | tm_ecf, rtype,
+      tree tm_fn = declare_library_fn_1 (tm_name, ecf | tm_ecf, fnspec, rtype,
                                         ptype ? 1 : 0, &ptype);
       free (tm_name);
       if (tm_fn != error_mark_node)
@@ -547,7 +558,8 @@ do_allocate_exception (tree type)
     allocate_exception_fn
       = declare_library_fn ("__cxa_allocate_exception",
                            ptr_type_node, size_type_node,
-                           ECF_NOTHROW | ECF_MALLOC | ECF_COLD, ECF_TM_PURE);
+                           ECF_NOTHROW | ECF_MALLOC | ECF_COLD, ECF_TM_PURE,
+                           "mc");
 
   return cp_build_function_call_nary (allocate_exception_fn,
                                      tf_warning_or_error,
@@ -565,7 +577,8 @@ do_free_exception (tree ptr)
     free_exception_fn
       = declare_library_fn ("__cxa_free_exception",
                            void_type_node, ptr_type_node,
-                           ECF_NOTHROW | ECF_LEAF, ECF_TM_PURE);
+                           ECF_NOTHROW | ECF_LEAF, ECF_TM_PURE,
+                           ".co ");
 
   return cp_build_function_call_nary (free_exception_fn,
                                      tf_warning_or_error, ptr, NULL_TREE);
@@ -653,11 +666,13 @@ build_throw (location_t loc, tree exp)
 
          throw_fn = declare_library_fn_1 ("__cxa_throw",
                                           ECF_NORETURN | ECF_COLD,
+                                          ".c. X X ",
                                           void_type_node, 3, args);
          if (flag_tm && throw_fn != error_mark_node)
            {
              tree itm_fn = declare_library_fn_1 ("_ITM_cxa_throw",
                                                  ECF_NORETURN | ECF_COLD,
+                                                 ".c. X X ",
                                                  void_type_node, 3, args);
              if (itm_fn != error_mark_node)
                {
@@ -804,6 +819,7 @@ build_throw (location_t loc, tree exp)
        {
          rethrow_fn = declare_library_fn_1 ("__cxa_rethrow",
                                             ECF_NORETURN | ECF_COLD,
+                                            ".c",
                                             void_type_node, 0, NULL);
          if (flag_tm && rethrow_fn != error_mark_node)
            apply_tm_attr (rethrow_fn, get_identifier ("transaction_pure"));
diff --git a/gcc/testsuite/g++.dg/opt/eh6.C b/gcc/testsuite/g++.dg/opt/eh6.C
new file mode 100644
index 00000000000..fa891fb2559
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/eh6.C
@@ -0,0 +1,34 @@
+// PR middle-end/106057
+// { dg-do run }
+// { dg-options "-O2" }
+struct a {int a; int b;} a;
+int b;
+
+__attribute__((noinline))
+struct a maybethrow(int b)
+{
+        if (!b)
+                throw(0);
+        return {0,0};
+}
+
+void
+test(int b)
+{
+        a={1,1};
+        a=maybethrow(b);
+        a={0,0};
+}
+int
+main()
+{
+        try {
+                test(b);
+        }
+        catch(int) {
+                if (!a.a)
+                        __builtin_abort ();
+        }
+        return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/tree-ssa/kill.C 
b/gcc/testsuite/g++.dg/tree-ssa/kill.C
new file mode 100644
index 00000000000..bee58cd91fe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/kill.C
@@ -0,0 +1,20 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-modref1" } */
+__attribute__ ((noinline))
+void test(int a)
+{
+       if (a)
+               throw (1);
+}
+int mem;
+void link_error ();
+int
+main()
+{
+       mem = 0;
+       test (0);
+       if (mem)
+               link_error ();
+       return 0;
+}
+// { dg-final { scan-tree-dump-not "modref done with result: tracked." 
"modref1" } }

Reply via email to