On Fri, Dec 16, 2016 at 10:47:34AM +0100, Richard Biener wrote:
> On Thu, 15 Dec 2016, Florian Weimer wrote:
> 
> > * Jakub Jelinek:
> > 
> > > +      if (lookup_attribute ("used", attributes) == NULL)
> > > + attributes = tree_cons (get_identifier ("used"),  NULL, attributes);
> > 
> > Attribute “used” seems different to me from the rest.  Based on the
> > documentation, I wouldn't expect it to have any action-at-a-distance
> > effect, just that an out-of-line copy is always emitted.  It will
> > disable unused-function warnings, too, I think.
> 
> Yes, technically cloning on "known" callgraph paths is still possible
> with 'used' functions.

So, after looking at what "used" attribute does in the middle-end, it
prevents some IPA optimizations and disables optimizing such functions away.

Here is an updated patch that doesn't imply "used", but just some of its
properties, but still allow unused noipa functions to be optimized away
(if it has no callers, we don't really need it; but if it has callers,
then they shouldn't know what it is doing and the function should not know
what the callers are doing).

Additionally there is a testcase that verifies the various IPA optimizations
I could think of are not happening.  What isn't covered by a testcase is
not changing function signature (I've verified it on x86_64-linux -m32
by looking at assembly, but scanning assembly for register conventions
is fragile) and IPA-RA (again, dunno how to test it nicely).
Suggestions how to test that IPA-SRA and IPA-PTA aren't happening?
Anything else we need to cover?

2016-12-16  Jakub Jelinek  <ja...@redhat.com>

        * attribs.c (decl_attributes): Imply noinline, noclone and no_icf
        attributes for noipa attribute.  For naked attribute use
        lookup_attribute first before lookup_attribute_spec.
        * final.c (rest_of_handle_final): Disable IPA RA for functions with
        noipa attribute.
        * ipa-visibility.c (non_local_p): Fix comment typos.  Return true
        for functions with noipa attribute.
        (cgraph_externally_visible_p): Return true for functions with noipa
        attribute.
        * cgraph.c (cgraph_node::get_availability): Return AVAIL_INTERPOSABLE
        for functions with noipa attribute.
        * doc/extend.texi: Document noipa function attribute.
c-family/
        * c-attribs.c (c_common_attribute_table): Add noipa attribute.
        (handle_noipa_attribute): New function.
testsuite/
        * gcc.dg/attr-noipa.c: New test.

--- gcc/attribs.c.jj    2016-12-15 17:46:43.542154663 +0100
+++ gcc/attribs.c       2016-12-16 11:43:53.522570085 +0100
@@ -404,8 +404,8 @@ decl_attributes (tree *node, tree attrib
      those targets that support it.  */
   if (TREE_CODE (*node) == FUNCTION_DECL
       && attributes
-      && lookup_attribute_spec (get_identifier ("naked"))
-      && lookup_attribute ("naked", attributes) != NULL)
+      && lookup_attribute ("naked", attributes) != NULL
+      && lookup_attribute_spec (get_identifier ("naked")))
     {
       if (lookup_attribute ("noinline", attributes) == NULL)
        attributes = tree_cons (get_identifier ("noinline"), NULL, attributes);
@@ -414,6 +414,23 @@ decl_attributes (tree *node, tree attrib
        attributes = tree_cons (get_identifier ("noclone"),  NULL, attributes);
     }
 
+  /* A "noipa" function attribute implies "noinline", "noclone" and "no_icf"
+     for those targets that support it.  */
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      && attributes
+      && lookup_attribute ("noipa", attributes) != NULL
+      && lookup_attribute_spec (get_identifier ("noipa")))
+    {
+      if (lookup_attribute ("noinline", attributes) == NULL)
+       attributes = tree_cons (get_identifier ("noinline"), NULL, attributes);
+
+      if (lookup_attribute ("noclone", attributes) == NULL)
+       attributes = tree_cons (get_identifier ("noclone"),  NULL, attributes);
+
+      if (lookup_attribute ("no_icf", attributes) == NULL)
+       attributes = tree_cons (get_identifier ("no_icf"),  NULL, attributes);
+    }
+
   targetm.insert_attributes (*node, &attributes);
 
   for (a = attributes; a; a = TREE_CHAIN (a))
--- gcc/final.c.jj      2016-12-15 17:46:43.109160216 +0100
+++ gcc/final.c 2016-12-16 11:29:11.040280464 +0100
@@ -4473,7 +4473,8 @@ rest_of_handle_final (void)
   assemble_start_function (current_function_decl, fnname);
   final_start_function (get_insns (), asm_out_file, optimize);
   final (get_insns (), asm_out_file, optimize);
-  if (flag_ipa_ra)
+  if (flag_ipa_ra
+      && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
     collect_fn_hard_reg_usage ();
   final_end_function ();
 
--- gcc/ipa-visibility.c.jj     2016-10-31 13:28:06.000000000 +0100
+++ gcc/ipa-visibility.c        2016-12-16 13:41:06.547122864 +0100
@@ -90,11 +90,12 @@ static bool
 non_local_p (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
 {
   return !(node->only_called_directly_or_aliased_p ()
-          /* i386 would need update to output thunk with locak calling
-             ocnvetions.  */
+          /* i386 would need update to output thunk with local calling
+             conventions.  */
           && !node->thunk.thunk_p
           && node->definition
           && !DECL_EXTERNAL (node->decl)
+          && !lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl))
           && !node->externally_visible
           && !node->used_from_other_partition
           && !node->in_other_partition);
@@ -209,6 +210,8 @@ cgraph_externally_visible_p (struct cgra
   if (lookup_attribute ("externally_visible",
                        DECL_ATTRIBUTES (node->decl)))
     return true;
+  if (lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl)))
+    return true;
   if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
       && lookup_attribute ("dllexport",
                           DECL_ATTRIBUTES (node->decl)))
--- gcc/cgraph.c.jj     2016-12-15 17:46:43.227158703 +0100
+++ gcc/cgraph.c        2016-12-16 11:29:11.041280451 +0100
@@ -2255,7 +2255,8 @@ cgraph_node::get_availability (symtab_no
     avail = AVAIL_AVAILABLE;
   else if (transparent_alias)
     ultimate_alias_target (&avail, ref);
-  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl)))
+  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl))
+          || lookup_attribute ("noipa", DECL_ATTRIBUTES (decl)))
     avail = AVAIL_INTERPOSABLE;
   else if (!externally_visible)
     avail = AVAIL_AVAILABLE;
--- gcc/doc/extend.texi.jj      2016-12-15 17:47:09.436822537 +0100
+++ gcc/doc/extend.texi 2016-12-16 12:00:11.568583696 +0100
@@ -2955,6 +2955,19 @@ asm ("");
 (@pxref{Extended Asm}) in the called function, to serve as a special
 side-effect.
 
+@item noipa
+@cindex @code{noipa} function attribute
+Disable interprocedural optimizations between the function with this
+attribute and its callers, as if the body of the function is not available
+when optimizing callers and the callers are unavailable when optimizing
+the body.  This attribute implies @code{noinline}, @code{noclone} and
+@code{no_icf} attributes.    However, this attribute is not equivalent
+to a combination of other attributes, because its purpose is to suppress
+existing and future optimizations employing interprocedural analysis,
+including those that do not have an attribute suitable for disabling
+them individually.  This attribute is supported mainly for the purpose
+of testing the compiler.
+
 @item nonnull (@var{arg-index}, @dots{})
 @cindex @code{nonnull} function attribute
 @cindex functions with non-null pointer arguments
--- gcc/c-family/c-attribs.c.jj 2016-12-15 17:46:43.183159267 +0100
+++ gcc/c-family/c-attribs.c    2016-12-16 11:29:11.048280358 +0100
@@ -63,6 +63,7 @@ static tree handle_stack_protect_attribu
 static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noclone_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noicf_attribute (tree *, tree, tree, int, bool *);
+static tree handle_noipa_attribute (tree *, tree, tree, int, bool *);
 static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
 static tree handle_always_inline_attribute (tree *, tree, tree, int,
                                            bool *);
@@ -173,6 +174,8 @@ const struct attribute_spec c_common_att
                              handle_noclone_attribute, false },
   { "no_icf",                 0, 0, true,  false, false,
                              handle_noicf_attribute, false },
+  { "noipa",                 0, 0, true,  false, false,
+                             handle_noipa_attribute, false },
   { "leaf",                   0, 0, true,  false, false,
                              handle_leaf_attribute, false },
   { "always_inline",          0, 0, true,  false, false,
@@ -680,6 +683,21 @@ handle_noicf_attribute (tree *node, tree
 {
   if (TREE_CODE (*node) != FUNCTION_DECL)
     {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "noipa" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_noipa_attribute (tree *node, tree name, tree, int, bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
       warning (OPT_Wattributes, "%qE attribute ignored", name);
       *no_add_attrs = true;
     }
--- gcc/testsuite/gcc.dg/attr-noipa.c.jj        2016-12-16 12:55:55.464155338 
+0100
+++ gcc/testsuite/gcc.dg/attr-noipa.c   2016-12-16 14:15:49.000000000 +0100
@@ -0,0 +1,188 @@
+/* Test the noipa attribute.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+static inline int __attribute__((noipa))
+fn1 (void) /* { dg-warning "inline function \[^\n\]* given attribute noinline" 
"" } */
+{
+  return 1;
+}
+
+/* Verify the function is not inlined into its caller.  */
+
+static __attribute__((noipa)) int
+fn2 (int x, int y)
+{
+  return x + y;
+}
+
+int
+fn3 (int x)
+{
+  return fn2 (x, 0);
+}
+
+/* { dg-final { scan-tree-dump "= fn2 \\(" "optimized" } } */
+
+void fn4 (char *);
+
+/* Verify the function is not cloned.  */
+
+__attribute__((__noipa__)) static int
+fn5 (int x, int y)
+{
+  char *p = __builtin_alloca (x + y);
+  fn4 (p);
+  return x + y;
+}
+
+int
+fn6 (int x)
+{
+  return fn5 (x, 2);
+}
+
+/* { dg-final { scan-tree-dump "= fn5 \\(" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "fn5\\.constprop" "optimized" } } */
+
+/* Verify we still remove unused function calls, even if they have
+   noipa attribute.  */
+
+static void fn7 (void) __attribute__((noipa));
+static void
+fn7 (void)
+{
+}
+
+/* { dg-final { scan-tree-dump-not "fn7 \\(" "optimized" } } */
+
+/* Verify noipa functions are not ICF optimized.  */
+
+static __attribute__((noipa)) int
+fn8 (int x)
+{
+  return x + 12;
+}
+
+static __attribute__((noipa)) int
+fn9 (int x)
+{
+  return x + 12;
+}
+
+int
+fn10 (int x)
+{
+  return fn8 (x) + fn9 (x);
+}
+
+/* { dg-final { scan-tree-dump "fn8 \\(int" "optimized" } } */
+/* { dg-final { scan-tree-dump "fn9 \\(int" "optimized" } } */
+
+/* Verify IPA-VRP is not performed.  */
+
+void fn11 (void);
+
+static int __attribute__((noipa))
+fn12 (int x)
+{
+  if (x < 6 || x >= 29)
+    fn11 ();
+}
+
+void
+fn13 (int x)
+{
+  fn12 (6 + (x & 15));
+}
+
+/* { dg-final { scan-tree-dump "fn11 \\(\\)" "optimized" } } */
+
+void fn14 (void);
+
+__attribute__((noipa)) static int
+fn15 (int x)
+{
+  return x & 7;
+}
+
+int
+fn16 (int x)
+{
+  x = fn15 (x);
+  if (x < 0 || x >= 7)
+    fn14 ();
+}
+
+/* { dg-final { scan-tree-dump "fn14 \\(\\)" "optimized" } } */
+
+/* Verify IPA BIT CP is not performed.  */
+
+void fn17 (void);
+
+__attribute__((noipa)) static int
+fn18 (int x)
+{
+  if (x & 8)
+    fn17 ();
+}
+
+void
+fn19 (void)
+{
+  fn18 (1);
+  fn18 (2);
+  fn18 (4);
+  fn18 (16);
+  fn18 (32);
+  fn18 (64);
+}
+
+/* { dg-final { scan-tree-dump "fn17 \\(\\)" "optimized" } } */
+
+/* Ensure pure/const discovery is not performed.  */
+
+int var1;
+void fn20 (void);
+
+__attribute__((noipa)) static int
+fn21 (int x, int y)
+{
+  return x * y;
+}
+
+int
+fn22 (void)
+{
+  var1 = 7;
+  asm volatile ("" : "+g" (var1) : : "memory");
+  int a = var1;
+  int b = fn21 (a, a);
+  if (a != var1)
+    fn20 ();
+  return b;
+}
+
+/* { dg-final { scan-tree-dump "fn20 \\(\\)" "optimized" } } */
+
+/* Verify IPA alignment propagation is not performed.  */
+
+static __attribute__ ((aligned(16))) char var2[32];
+void fn23 (void);
+
+__attribute__((noipa)) static void
+fn24 (char *p)
+{
+  if ((((__UINTPTR_TYPE__) p) & 15) != 0)
+    fn23 ();
+  asm ("");
+}
+
+void
+fn25 (void)
+{
+  fn24 (var2);
+  fn24 (var2 + 16);
+}
+
+/* { dg-final { scan-tree-dump "fn20 \\(\\)" "optimized" } } */


        Jakub

Reply via email to