attribute((leafify)) for 3.4

Richard Guenther rguenth@tat.physik.uni-tuebingen.de
Fri Oct 31 15:53:00 GMT 2003


On Fri, 31 Oct 2003, Jan Hubicka wrote:

> > On Thu, 30 Oct 2003, Richard Guenther wrote:
> > I missed to mark the cgraph edges created after inlining, with this chunk
> > added I get leafify behavior as wanted.  But I still see these artificial
> > libstdc++ functions not inlined - they seem to be triggered by temporary
> > objects created inside the loop and never optimized away (as 3.3 did).
> > I'll try to dig further and come up with a testcase.
> >
> > (Final) patch is attached. It breaks debugging (-g3) with the following
> > ICE:
> >
> > /home/rguenth/ix86/pooma/tat-serial-gcc34/pooma/linux/src/Evaluator/InlineEvaluator.h:102:
> > internal compiler error: in gen_subprogram_die, at dwarf2out.c:10607
> > Please submit a full bug report,
> > with preprocessed source if appropriate.
> > See <URL:http://gcc.gnu.org/bugs.html> for instructions.
> > make: *** [tramp3d.o] Error 1
> >
> > It seems that unnecessary (inlined) function bodies are emit and
> > debugging is confused by this. But I really dont know how to prevent
> > this. Linking is fine. Any idea?
>
> Dwarf2out emits abstract debug infromation for functions that are
> inlined.
> This crash usually happens when function is inlined but the debug
> information is missing.  You probably need to fix:
>
> /* Return true when the DECL can possibly be inlined.  */
> bool
> cgraph_function_possibly_inlined_p (tree decl)
> {
>   if (!cgraph_global_info_ready)
>     return (DECL_INLINE (decl) && !flag_no_inline);
>   return cgraph_node (decl)->global.inlined;
> }
>
> To return true for functions inlined by leafifying.

Ah, ok. I missed this, fixed by setting e->callee->global.inlined to 1 in
cgraph_decide_inlining_leafify().

> > +static void
> > +cgraph_decide_inlining_leafify(struct cgraph_node *node)
> > +{
> > +  struct cgraph_edge *e;
> > +  if (node->aux)
> > +    return;
> > +  for (e = node->callees; e; e = e->next_callee)
> > +    {
> > +      e->maybe_inline_call = 1;
> > +      cgraph_decide_inlining_leafify(e->callee);
> > +    }
>
> In general we are trying to avoid recursion over loop depth.  You will
> also need to behave somewhat sanely in the case of recursion (either not
> inline the reucursive call or error out.

It's tail recursion, so it shouldn't be bad.  Also I moved node->aux
initialization up to avoid going endlessly through callgraph loops.
Recursive inlining seems to be prevented somehow at least for not-direct
recursing, for direct recursing I added a check in the mark routine. So
cgraph_decide_inlining_leafify() looks now like

static void
cgraph_decide_inlining_leafify(struct cgraph_node *node)
{
  struct cgraph_edge *e;
  if (node->aux)
    return;
  node->aux = node;
  for (e = node->callees; e; e = e->next_callee)
    {
      if (e->callee == node)
        continue;
      e->maybe_inline_call = 1;
      e->callee->global.inlined = 1;
      cgraph_decide_inlining_leafify(e->callee);
    }
}

> In general the cgraph changes looks acceptable for me for 3.4 given that
> I will reorganize this somewhat in 3.5 anyway.
> However you will need to wait for global write maintainer to agree on the
> rest of patch and concept of leafify argument and decision whether such
> a change is acceptable in stage 3.

I won't submit this officially for 3.4, as I dont have a copyright
assignment yet and I suspect this would last too long to be accepted late
in stage 3 anyways. Also the concept of leafify needs some arguments.
Apart from this I think it is trivial for you to implement this
functionality after cleanup in 3.5, so I wont need to fiddle around with
copyright assignments.

> Please also use -c3p for diffing so the functio names appear in the
> diff.  It would make it more readable.  Also there seem to be some
> formating problems (perhaps just tabs replaced by spaces) in your patch.

Ah, never saw -p, this one is really useful.

Updated patch attached for those who are interested.

Thanks,

Richard.

--
Richard Guenther <richard dot guenther at uni-tuebingen dot de>
WWW: http://www.tat.physik.uni-tuebingen.de/~rguenth/
-------------- next part --------------
Index: c-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.c,v
retrieving revision 1.465
diff -u -c -3 -p -r1.465 c-common.c
*** c-common.c	20 Oct 2003 22:03:32 -0000	1.465
--- c-common.c	31 Oct 2003 12:42:53 -0000
*************** static tree handle_noreturn_attribute (t
*** 762,767 ****
--- 762,768 ----
  static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
  static tree handle_always_inline_attribute (tree *, tree, tree, int,
  					    bool *);
+ static tree handle_leafify_attribute (tree *, tree, tree, int, bool *);
  static tree handle_used_attribute (tree *, tree, tree, int, bool *);
  static tree handle_unused_attribute (tree *, tree, tree, int, bool *);
  static tree handle_const_attribute (tree *, tree, tree, int, bool *);
*************** const struct attribute_spec c_common_att
*** 824,829 ****
--- 825,832 ----
  			      handle_noinline_attribute },
    { "always_inline",          0, 0, true,  false, false,
  			      handle_always_inline_attribute },
+   { "leafify",                0, 0, true,  false, false,
+ 			      handle_leafify_attribute },
    { "used",                   0, 0, true,  false, false,
  			      handle_used_attribute },
    { "unused",                 0, 0, false, false, false,
*************** handle_always_inline_attribute (tree *no
*** 4448,4453 ****
--- 4451,4478 ----
  				tree args ATTRIBUTE_UNUSED,
  				int flags ATTRIBUTE_UNUSED,
  				bool *no_add_attrs)
+ {
+   if (TREE_CODE (*node) == FUNCTION_DECL)
+     {
+       /* Do nothing else, just set the attribute.  We'll get at
+ 	 it later with lookup_attribute.  */
+     }
+   else
+     {
+       warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+       *no_add_attrs = true;
+     }
+ 
+   return NULL_TREE;
+ }
+ 
+ /* Handle a "leafify" attribute; arguments as in
+    struct attribute_spec.handler.  */
+ 
+ static tree
+ handle_leafify_attribute (tree *node, tree name,
+ 			  tree args ATTRIBUTE_UNUSED,
+ 			  int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
  {
    if (TREE_CODE (*node) == FUNCTION_DECL)
      {
Index: cgraph.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cgraph.c,v
retrieving revision 1.34
diff -u -c -3 -p -r1.34 cgraph.c
*** cgraph.c	22 Oct 2003 19:18:03 -0000	1.34
--- cgraph.c	31 Oct 2003 12:42:53 -0000
*************** create_edge (struct cgraph_node *caller,
*** 157,162 ****
--- 157,163 ----
    struct cgraph_edge *edge2;
  
    edge->inline_call = false;
+   edge->maybe_inline_call = false;
    /* At the moment we don't associate calls with specific CALL_EXPRs
       as we probably ought to, so we must preserve inline_call flags to
       be the same in all copies of the same edge.  */
*************** create_edge (struct cgraph_node *caller,
*** 165,170 ****
--- 166,172 ----
        if (edge2->callee == callee)
  	{
  	  edge->inline_call = edge2->inline_call;
+ 	  edge->maybe_inline_call = edge2->maybe_inline_call;
  	  break;
  	}
  
Index: cgraph.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cgraph.h,v
retrieving revision 1.23
diff -u -c -3 -p -r1.23 cgraph.h
*** cgraph.h	22 Oct 2003 11:58:09 -0000	1.23
--- cgraph.h	31 Oct 2003 12:42:53 -0000
*************** struct cgraph_edge GTY(())
*** 120,125 ****
--- 120,126 ----
    struct cgraph_edge *next_caller;
    struct cgraph_edge *next_callee;
    bool inline_call;
+   bool maybe_inline_call;
  };
  
  /* The cgraph_varpool data strutcture.
Index: cgraphunit.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cgraphunit.c,v
retrieving revision 1.37
diff -u -c -3 -p -r1.37 cgraphunit.c
*** cgraphunit.c	22 Oct 2003 11:58:09 -0000	1.37
--- cgraphunit.c	31 Oct 2003 12:42:53 -0000
*************** static int nfunctions_inlined;
*** 57,62 ****
--- 57,65 ----
  static int initial_insns;
  static int overall_insns;
  
+ /* Flag if we expand a leafify function.  */
+ static int leafify;
+ 
  /* Records tree nodes seen in cgraph_create_edges.  Simply using
     walk_tree_without_duplicates doesn't guarantee each node is visited
     once because it gets a new htab upon each recursive call from
*************** record_call_1 (tree *tp, int *walk_subtr
*** 257,265 ****
  	tree decl = get_callee_fndecl (*tp);
  	if (decl && TREE_CODE (decl) == FUNCTION_DECL)
  	  {
  	    if (DECL_BUILT_IN (decl))
  	      return NULL;
! 	    cgraph_record_call (data, decl);
  
  	    /* When we see a function call, we don't want to look at the
  	       function reference in the ADDR_EXPR that is hanging from
--- 260,272 ----
  	tree decl = get_callee_fndecl (*tp);
  	if (decl && TREE_CODE (decl) == FUNCTION_DECL)
  	  {
+             struct cgraph_edge *e;
+ 
  	    if (DECL_BUILT_IN (decl))
  	      return NULL;
! 	    e = cgraph_record_call (data, decl);
!             if (leafify)
!               e->maybe_inline_call = 1;
  
  	    /* When we see a function call, we don't want to look at the
  	       function reference in the ADDR_EXPR that is hanging from
*************** cgraph_optimize_function (struct cgraph_
*** 471,477 ****
    /* optimize_inline_calls avoids inlining of current_function_decl.  */
    current_function_decl = decl;
    if (flag_inline_trees)
!     optimize_inline_calls (decl);
    if (node->nested)
      {
        for (node = node->nested; node; node = node->next_nested)
--- 478,487 ----
    /* optimize_inline_calls avoids inlining of current_function_decl.  */
    current_function_decl = decl;
    if (flag_inline_trees)
!     {
!       leafify = (lookup_attribute ("leafify", DECL_ATTRIBUTES (node->decl)) != NULL);
!       optimize_inline_calls (decl);
!     }
    if (node->nested)
      {
        for (node = node->nested; node; node = node->next_nested)
*************** cgraph_expand_function (struct cgraph_no
*** 507,513 ****
    else
      {
        for (e = node->callers; e; e = e->next_caller)
! 	if (e->inline_call)
  	  break;
        if (!e)
  	DECL_SAVED_TREE (decl) = NULL;
--- 517,523 ----
    else
      {
        for (e = node->callers; e; e = e->next_caller)
! 	if (e->inline_call || e->maybe_inline_call)
  	  break;
        if (!e)
  	DECL_SAVED_TREE (decl) = NULL;
*************** cgraph_decide_inlining_of_small_function
*** 1070,1075 ****
--- 1080,1106 ----
    free (heap_node);
  }
  
+ /* Go down the call tree marking nodes disregarding inlining limits.  Called
+  * for function decls with attribute((leafify)) set.  Avoid recursion and
+  * redundant work by using node->aux field.  */
+ 
+ static void
+ cgraph_decide_inlining_leafify(struct cgraph_node *node)
+ {
+   struct cgraph_edge *e;
+   if (node->aux)
+     return;
+   node->aux = node;
+   for (e = node->callees; e; e = e->next_callee)
+     {
+       if (e->callee == node)
+         continue;
+       e->maybe_inline_call = 1;
+       e->callee->global.inlined = 1;
+       cgraph_decide_inlining_leafify(e->callee);
+     }
+ }
+ 
  /* Decide on the inlining.  We do so in the topological order to avoid
     expenses on updating datastructures.  */
  
*************** cgraph_decide_inlining (void)
*** 1216,1221 ****
--- 1247,1274 ----
  	}
      }
  
+   /* For all nodes with their function decl having attribute((leafify)) set
+    * remove inline mark and set maybe_inline_call downward all callees.
+    * Use node->aux to avoid duplicate walking of callees.  */
+ 
+   for (node = cgraph_nodes; node; node = node->next)
+     node->aux = 0;
+ 
+   for (i = nnodes - 1; i >= 0; i--)
+     {
+       struct cgraph_edge *e;
+ 
+       node = order[i];
+ 
+       if (lookup_attribute ("leafify", DECL_ATTRIBUTES (node->decl)) == NULL)
+         continue;
+ 
+       for (e = node->callers; e; e = e->next_caller)
+         e->inline_call = 0;
+ 
+       cgraph_decide_inlining_leafify(node);
+     }
+ 
    if (cgraph_dump_file)
      fprintf (cgraph_dump_file,
  	     "\nInlined %i calls, eliminated %i functions, "
*************** cgraph_inline_p (tree caller_decl, tree 
*** 1291,1297 ****
  
    for (e = caller->callees; e; e = e->next_callee)
      if (e->callee == callee)
!       return e->inline_call;
    /* We do not record builtins in the callgraph.  Perhaps it would make more
       sense to do so and then prune out those not overwritten by explicit
       function body.  */
--- 1344,1350 ----
  
    for (e = caller->callees; e; e = e->next_callee)
      if (e->callee == callee)
!       return e->inline_call || (e->maybe_inline_call && leafify);
    /* We do not record builtins in the callgraph.  Perhaps it would make more
       sense to do so and then prune out those not overwritten by explicit
       function body.  */
Index: doc/extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.169
diff -u -c -3 -p -r1.169 extend.texi
*** doc/extend.texi	26 Oct 2003 19:03:42 -0000	1.169
--- doc/extend.texi	31 Oct 2003 12:42:57 -0000
*************** The keyword @code{__attribute__} allows 
*** 1976,1982 ****
  attributes when making a declaration.  This keyword is followed by an
  attribute specification inside double parentheses.  The following
  attributes are currently defined for functions on all targets:
! @code{noreturn}, @code{noinline}, @code{always_inline},
  @code{pure}, @code{const}, @code{nothrow},
  @code{format}, @code{format_arg}, @code{no_instrument_function},
  @code{section}, @code{constructor}, @code{destructor}, @code{used},
--- 1976,1982 ----
  attributes when making a declaration.  This keyword is followed by an
  attribute specification inside double parentheses.  The following
  attributes are currently defined for functions on all targets:
! @code{noreturn}, @code{noinline}, @code{always_inline}, @code{leafify},
  @code{pure}, @code{const}, @code{nothrow},
  @code{format}, @code{format_arg}, @code{no_instrument_function},
  @code{section}, @code{constructor}, @code{destructor}, @code{used},
*************** inlining.
*** 2052,2057 ****
--- 2052,2066 ----
  Generally, functions are not inlined unless optimization is specified.
  For functions declared inline, this attribute inlines the function even
  if no optimization level was specified.
+ 
+ @cindex @code{leafify} function attribute
+ @item leafify
+ Generally, inlining into a function is limited. For a function marked with
+ this attribute, every call inside this function will be inlined, if possible.
+ This attribute implies that the function itself is not considered for automatic
+ inlining, but into functions marked @code{leafify}. You need to mark the function
+ @code{noinline} if you want to prevent the latter. The @code{leafify} attribute
+ only works in unit-at-a-time mode.
  
  @cindex @code{pure} function attribute
  @item pure


More information about the Gcc mailing list