C++ PATCH: PR 11131

Mark Mitchell mark@codesourcery.com
Tue Jun 10 07:40:00 GMT 2003


This patch fixes PR c++/11131.

The key problem is that when we instantiate a class we also
create delcarations of the member functions in that class.  If the
main declaration for one of the members is updated later (by, for
example, being declared inline), the inline directive doesn't get
propagated to the declaration of the instantiated version.  It's hard
to fix that, so instead, I've arranged to make sure we look back at
the main template.

Tested on i686-pc-linux-gnu, applied on the mainline.

--
Mark Mitchell
CodeSourcery, LLC
mark@codesourcery.com

2003-06-10  Mark Mitchell  <mark@codesourcery.com>

	PR c++/11131
	* cp-tree.h (template_for_substitution): Declare.
	* decl2.c (mark_used): Use it when figuring out whether or not a
	function is inline.
	* pt.c (template_for_substitution): Give it external linkage.
	* tree.c (cp_cannot_inline_tree_fn): Instantiate as early as
	possible.

2003-06-10  Mark Mitchell  <mark@codesourcery.com>

	PR c++/11131
	* g++.dg/opt/template1.C: New test.

Index: tree-inline.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-inline.c,v
retrieving revision 1.60
diff -c -5 -p -r1.60 tree-inline.c
*** tree-inline.c	9 Jun 2003 12:57:10 -0000	1.60
--- tree-inline.c	10 Jun 2003 07:06:23 -0000
*************** inlinable_function_p (fn, id, nolimit)
*** 977,988 ****
    /* If we've already decided this function shouldn't be inlined,
       there's no need to check again.  */
    if (DECL_UNINLINABLE (fn))
      return 0;
  
!   /* Assume it is not inlinable.  */
!   inlinable = 0;
         
    /* We may be here either because fn is declared inline or because
       we use -finline-functions.  For the second case, we are more
       restrictive.  */
    if (DID_INLINE_FUNC (fn))
--- 977,990 ----
    /* If we've already decided this function shouldn't be inlined,
       there's no need to check again.  */
    if (DECL_UNINLINABLE (fn))
      return 0;
  
!   /* See if there is any language-specific reason it cannot be
!      inlined.  (It is important that this hook be called early because
!      in C++ it may result in template instantiation.)  */
!   inlinable = !(*lang_hooks.tree_inlining.cannot_inline_tree_fn) (&fn);
         
    /* We may be here either because fn is declared inline or because
       we use -finline-functions.  For the second case, we are more
       restrictive.  */
    if (DID_INLINE_FUNC (fn))
*************** inlinable_function_p (fn, id, nolimit)
*** 991,1042 ****
    /* The number of instructions (estimated) of current function.  */
    currfn_insns = DECL_NUM_STMTS (fn) * INSNS_PER_STMT;
  
    /* If we're not inlining things, then nothing is inlinable.  */
    if (! flag_inline_trees)
!     ;
    /* If we're not inlining all functions and the function was not
       declared `inline', we don't inline it.  Don't think of
       disregarding DECL_INLINE when flag_inline_trees == 2; it's the
       front-end that must set DECL_INLINE in this case, because
       dwarf2out loses if a function is inlined that doesn't have
       DECL_INLINE set.  */
    else if (! DECL_INLINE (fn) && !nolimit)
!     ;
  #ifdef INLINER_FOR_JAVA
    /* Synchronized methods can't be inlined.  This is a bug.  */
    else if (METHOD_SYNCHRONIZED (fn))
!     ;
  #endif /* INLINER_FOR_JAVA */
    /* We can't inline functions that are too big.  Only allow a single
       function to be of MAX_INLINE_INSNS_SINGLE size.  Make special
       allowance for extern inline functions, though.  */
    else if (!nolimit
  	   && ! (*lang_hooks.tree_inlining.disregard_inline_limits) (fn)
  	   && currfn_insns > max_inline_insns_single)
!     ;
    /* We can't inline functions that call __builtin_longjmp at all.
       The non-local goto machenery really requires the destination
       be in a different function.  If we allow the function calling
       __builtin_longjmp to be inlined into the function calling
       __builtin_setjmp, Things will Go Awry.  */
    /* ??? Need front end help to identify "regular" non-local goto.  */
    else if (find_builtin_longjmp_call (DECL_SAVED_TREE (fn)))
!     ;
    /* Refuse to inline alloca call unless user explicitly forced so as this may
       change program's memory overhead drastically when the function using alloca
       is called in loop.  In GCC present in SPEC2000 inlining into schedule_block
       cause it to require 2GB of ram instead of 256MB.  */
    else if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) == NULL
  	   && find_alloca_call (DECL_SAVED_TREE (fn)))
!     ;
!   /* All is well.  We can inline this function.  Traditionally, GCC
!      has refused to inline functions using alloca, or functions whose
!      values are returned in a PARALLEL, and a few other such obscure
!      conditions.  We are not equally constrained at the tree level.  */
!   else
!     inlinable = 1;
  
    /* Squirrel away the result so that we don't have to check again.  */
    DECL_UNINLINABLE (fn) = ! inlinable;
  
    /* In case we don't disregard the inlining limits and we basically
--- 993,1038 ----
    /* The number of instructions (estimated) of current function.  */
    currfn_insns = DECL_NUM_STMTS (fn) * INSNS_PER_STMT;
  
    /* If we're not inlining things, then nothing is inlinable.  */
    if (! flag_inline_trees)
!     inlinable = 0;
    /* If we're not inlining all functions and the function was not
       declared `inline', we don't inline it.  Don't think of
       disregarding DECL_INLINE when flag_inline_trees == 2; it's the
       front-end that must set DECL_INLINE in this case, because
       dwarf2out loses if a function is inlined that doesn't have
       DECL_INLINE set.  */
    else if (! DECL_INLINE (fn) && !nolimit)
!     inlinable = 0;
  #ifdef INLINER_FOR_JAVA
    /* Synchronized methods can't be inlined.  This is a bug.  */
    else if (METHOD_SYNCHRONIZED (fn))
!     inlinable = 0;
  #endif /* INLINER_FOR_JAVA */
    /* We can't inline functions that are too big.  Only allow a single
       function to be of MAX_INLINE_INSNS_SINGLE size.  Make special
       allowance for extern inline functions, though.  */
    else if (!nolimit
  	   && ! (*lang_hooks.tree_inlining.disregard_inline_limits) (fn)
  	   && currfn_insns > max_inline_insns_single)
!     inlinable = 0;
    /* We can't inline functions that call __builtin_longjmp at all.
       The non-local goto machenery really requires the destination
       be in a different function.  If we allow the function calling
       __builtin_longjmp to be inlined into the function calling
       __builtin_setjmp, Things will Go Awry.  */
    /* ??? Need front end help to identify "regular" non-local goto.  */
    else if (find_builtin_longjmp_call (DECL_SAVED_TREE (fn)))
!     inlinable = 0;
    /* Refuse to inline alloca call unless user explicitly forced so as this may
       change program's memory overhead drastically when the function using alloca
       is called in loop.  In GCC present in SPEC2000 inlining into schedule_block
       cause it to require 2GB of ram instead of 256MB.  */
    else if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) == NULL
  	   && find_alloca_call (DECL_SAVED_TREE (fn)))
!     inlinable = 0;
  
    /* Squirrel away the result so that we don't have to check again.  */
    DECL_UNINLINABLE (fn) = ! inlinable;
  
    /* In case we don't disregard the inlining limits and we basically
*************** inlinable_function_p (fn, id, nolimit)
*** 1062,1074 ****
  			- (sum_insns - MAX_INLINE_INSNS) / MAX_INLINE_SLOPE;
  	  if (currfn_insns > max_curr)
  	    inlinable = 0;
  	}
      }
- 
-   if (inlinable && (*lang_hooks.tree_inlining.cannot_inline_tree_fn) (&fn))
-     inlinable = 0;
  
    /* If we don't have the function body available, we can't inline
       it.  */
    if (! DECL_SAVED_TREE (fn))
      inlinable = 0;
--- 1058,1067 ----
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.849
diff -c -5 -p -r1.849 cp-tree.h
*** cp/cp-tree.h	7 Jun 2003 11:10:43 -0000	1.849
--- cp/cp-tree.h	10 Jun 2003 07:06:56 -0000
*************** extern bool dependent_template_arg_p    
*** 3976,3985 ****
--- 3976,3986 ----
  extern bool dependent_template_p                (tree);
  extern bool type_dependent_expression_p         (tree);
  extern bool value_dependent_expression_p        (tree);
  extern tree resolve_typename_type               (tree, bool);
  extern tree resolve_typename_type_in_current_instantiation (tree);
+ extern tree template_for_substitution           (tree);
  
  /* in repo.c */
  extern void repo_template_used (tree);
  extern void repo_template_instantiated (tree, bool);
  extern void init_repo (const char *);
Index: cp/decl2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl2.c,v
retrieving revision 1.627
diff -c -5 -p -r1.627 decl2.c
*** cp/decl2.c	21 May 2003 23:27:50 -0000	1.627
--- cp/decl2.c	10 Jun 2003 07:06:58 -0000
*************** mark_used (tree decl)
*** 4588,4598 ****
       instantiation. We check that DECL is not an explicit
       instantiation because that is not checked in instantiate_decl.  */
    if ((DECL_NON_THUNK_FUNCTION_P (decl) || TREE_CODE (decl) == VAR_DECL)
        && DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)
        && (!DECL_EXPLICIT_INSTANTIATION (decl)
! 	  || (TREE_CODE (decl) == FUNCTION_DECL && DECL_INLINE (decl))))
      {
        bool defer;
  
        /* Normally, we put off instantiating functions in order to
  	 improve compile times.  Maintaining a stack of active
--- 4588,4600 ----
       instantiation. We check that DECL is not an explicit
       instantiation because that is not checked in instantiate_decl.  */
    if ((DECL_NON_THUNK_FUNCTION_P (decl) || TREE_CODE (decl) == VAR_DECL)
        && DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)
        && (!DECL_EXPLICIT_INSTANTIATION (decl)
! 	  || (TREE_CODE (decl) == FUNCTION_DECL 
! 	      && DECL_INLINE (DECL_TEMPLATE_RESULT 
! 			      (template_for_substitution (decl))))))
      {
        bool defer;
  
        /* Normally, we put off instantiating functions in order to
  	 improve compile times.  Maintaining a stack of active
Index: cp/pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.697
diff -c -5 -p -r1.697 pt.c
*** cp/pt.c	3 Jun 2003 13:01:43 -0000	1.697
--- cp/pt.c	10 Jun 2003 07:07:01 -0000
*************** static void tsubst_default_arguments PAR
*** 169,179 ****
  static tree for_each_template_parm_r PARAMS ((tree *, int *, void *));
  static tree copy_default_args_to_explicit_spec_1 PARAMS ((tree, tree));
  static void copy_default_args_to_explicit_spec PARAMS ((tree));
  static int invalid_nontype_parm_type_p PARAMS ((tree, tsubst_flags_t));
  static int eq_local_specializations (const void *, const void *);
- static tree template_for_substitution (tree);
  static bool dependent_type_p_r (tree);
  static bool dependent_template_id_p (tree, tree);
  static tree tsubst (tree, tree, tsubst_flags_t, tree);
  static tree tsubst_expr	(tree, tree, tsubst_flags_t, tree);
  static tree tsubst_copy	(tree, tree, tsubst_flags_t, tree);
--- 169,178 ----
*************** regenerate_decl_from_template (decl, tmp
*** 10734,10744 ****
  }
  
  /* Return the TEMPLATE_DECL into which DECL_TI_ARGS(DECL) should be
     substituted to get DECL.  */
  
! static tree
  template_for_substitution (tree decl)
  {
    tree tmpl = DECL_TI_TEMPLATE (decl);
  
    /* Set TMPL to the template whose DECL_TEMPLATE_RESULT is the pattern
--- 10733,10743 ----
  }
  
  /* Return the TEMPLATE_DECL into which DECL_TI_ARGS(DECL) should be
     substituted to get DECL.  */
  
! tree
  template_for_substitution (tree decl)
  {
    tree tmpl = DECL_TI_TEMPLATE (decl);
  
    /* Set TMPL to the template whose DECL_TEMPLATE_RESULT is the pattern
Index: cp/tree.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/tree.c,v
retrieving revision 1.326
diff -c -5 -p -r1.326 tree.c
*** cp/tree.c	17 May 2003 22:21:35 -0000	1.326
--- cp/tree.c	10 Jun 2003 07:07:02 -0000
*************** int
*** 2202,2224 ****
  cp_cannot_inline_tree_fn (fnp)
       tree *fnp;
  {
    tree fn = *fnp;
  
-   if (flag_really_no_inline
-       && lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) == NULL)
-     return 1;
- 
    /* We can inline a template instantiation only if it's fully
       instantiated.  */
    if (DECL_TEMPLATE_INFO (fn)
        && TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (fn)))
      {
        fn = *fnp = instantiate_decl (fn, /*defer_ok=*/0);
        if (TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (fn)))
  	return 1;
      }
  
    /* Don't auto-inline anything that might not be bound within
       this unit of translation.  */
    if (!DECL_DECLARED_INLINE_P (fn) && !(*targetm.binds_local_p) (fn))
      {
--- 2202,2227 ----
  cp_cannot_inline_tree_fn (fnp)
       tree *fnp;
  {
    tree fn = *fnp;
  
    /* We can inline a template instantiation only if it's fully
       instantiated.  */
    if (DECL_TEMPLATE_INFO (fn)
        && TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (fn)))
      {
        fn = *fnp = instantiate_decl (fn, /*defer_ok=*/0);
        if (TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (fn)))
  	return 1;
      }
+ 
+   if (!DECL_INLINE (fn))
+     return 1;
+ 
+   if (flag_really_no_inline
+       && lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) == NULL)
+     return 1;
  
    /* Don't auto-inline anything that might not be bound within
       this unit of translation.  */
    if (!DECL_DECLARED_INLINE_P (fn) && !(*targetm.binds_local_p) (fn))
      {
Index: testsuite/g++.dg/opt/template1.C
===================================================================
RCS file: testsuite/g++.dg/opt/template1.C
diff -N testsuite/g++.dg/opt/template1.C
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- testsuite/g++.dg/opt/template1.C	10 Jun 2003 07:07:07 -0000
***************
*** 0 ****
--- 1,19 ----
+ // { dg-options "-O2" }
+ // { dg-final { scan-assembler-not "foo1" } }
+ 
+ template <int>
+ struct A {
+     void foo1 () throw ();
+     void foo2 ();
+ 
+     void UNRELATED ();
+ };
+ 
+ template <> void A<0>::UNRELATED ();
+ 
+ template <int dim> inline void A<dim>::foo1 () throw () {}
+ template <int dim> inline void A<dim>::foo2 ()          {}
+ 
+ void bar (A<0> &a) {
+   a.foo1 ();
+ }



More information about the Gcc-patches mailing list