This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH: Improve exceptions/inlining compile-time performance


I have analyzed some of the reports regarding bad interactions between
inlining and exceptions with respect to compile-time performance.

Here is the basic problem.

We use the number of statements in a function as a measure of its
size.  However, a CLEANUP_STMT is actually expanded twice; once on the
ordinary code path and once in an exception handler for that region.
Now, if the CLEANUP_STMT contains a call to a function which itself
contains a CLEANUP_STMT, the nested CLEANUP_STMT will be expanded four
times.  

So, we get exponentially big functions that we think are only linearly
big.

One thing we could do is accurately count this by keeping track of the
number of CLEANUP_STMTs in a function, and multiplying by the nesting
depth appropriately.  One side-effect of that, however, is that
functions that will never throw exceptions will still not get inlined
because we will incorrectly measure their cost; we think they are big,
but then the optimizers throw out the copy of the CLEANUP_STMT for the
exception-handling path, so they're small after all.

That accurate counting is probably still a good idea, but I took a
different approach in this patch.  I noticed that many of the
functions in these input functions obviously raised no exceptions, and
then had the compiler simply not expand the CLEANUP_STMTs on the
exception-handling path in that circumstance.  By doing this first,
the thresholding described above will not adversely affect the
inlining of functions so often.

Here are some timing reuslts with the test case for PR 10316.

  Original: 27s
  Patched:  17s

Then, I went a step further and changed one line of the program
source:

    void destroy ();

to:

    void destroy () throw ();

With that change:

  Original: 23s
  Patched:   3s

By contrast, compiling with -fno-exceptions takes about 2.5s with both
the original and patched versions of the compiler.  So, we probably
still want some thresholding to bring the 17s down closer to the 2.5s.

The savings with the unpatched code come because the compiler is able
to throw out many of the exception handlers generated after generating
RTL for them; with the patched code the savings are much greater
because we are able to avoid generating so much RTL in the first
place.

So, one key aspect of this patch is that it gives users a way to
improve compile-times: use "throw ()" wherever possible. If the V3
library is not already using "throw()" wherever possible, it should
start doing so. :-)

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

I'm going to apply this to the 3.3 branch soon, as well; but I need to
test it there first.

--
Mark Mitchell
CodeSourcery, LLC
mark at codesourcery dot com

2003-04-21  Mark Mitchell  <mark at codesourcery dot com>

	* Makefile.in (calls.o): Depend on except.h.
	* calls.c: Include except.h.
	(emit_call_1): Call note_eh_region_may_contain_throw if
	appropriate.
	* except.c (eh_region): Add may_contain_throw.
	(expand_eh_region_end_cleanup): Do not include handler code when
	it cannot be reached.
	(note_eh_region_may_contain_throw): New function.
	* except.h (note_eh_region_may_contain_throw): New function.

2003-04-21  Mark Mitchell  <mark at codesourcery dot com>

	* call.c (build_over_call): Use build_cxx_call.
	(build_cxx_call): New method, split out of build_over_call.
	* cp-tree.h (language_function): Add can_throw.
	(build_cxx_call): Declare it.
	* decl.c (finish_function): If a function does not contain any
	calls to functions that can throw an exception, indicate that
	fact.
	* decl2.c (mark_used): Do not defer the instantiation of
	functions, if the current function does not throw.
	* optimize.c (maybe_clone_body): Copy TREE_NOTHROW to the clones.
	* pt.c (instantiate_decl): Make sure import_export_decl is called
	before emitting things.
	* rtti.c (throw_bad_cast): Use build_cxx_call.
	(build_dynamic_cast_1): Likewise.
	* typeck.c (build_function_call): Likewise.

2003-04-21  Mark Mitchell  <mark at codesourcery dot com>

	* g++.dg/template/recurse.C: Adjust location of error messages.

Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.1038
diff -c -5 -p -r1.1038 Makefile.in
*** Makefile.in	21 Apr 2003 19:48:05 -0000	1.1038
--- Makefile.in	21 Apr 2003 22:07:21 -0000
*************** builtins.o : builtins.c $(CONFIG_H) $(SY
*** 1530,1540 ****
     flags.h $(TARGET_H) function.h $(REGS_H) $(EXPR_H) $(OPTABS_H) insn-config.h \
     $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
     except.h $(TM_P_H) $(PREDICT_H) libfuncs.h real.h langhooks.h
  calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) flags.h \
     $(EXPR_H) langhooks.h $(TARGET_H) \
!    libfuncs.h $(REGS_H) toplev.h output.h function.h $(TIMEVAR_H) $(TM_P_H) cgraph.h
  expmed.o : expmed.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
     flags.h insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) real.h \
     toplev.h $(TM_P_H) langhooks.h
  explow.o : explow.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
     flags.h hard-reg-set.h insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) \
--- 1530,1540 ----
     flags.h $(TARGET_H) function.h $(REGS_H) $(EXPR_H) $(OPTABS_H) insn-config.h \
     $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
     except.h $(TM_P_H) $(PREDICT_H) libfuncs.h real.h langhooks.h
  calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) flags.h \
     $(EXPR_H) langhooks.h $(TARGET_H) \
!    libfuncs.h $(REGS_H) toplev.h output.h function.h $(TIMEVAR_H) $(TM_P_H) cgraph.h except.h
  expmed.o : expmed.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
     flags.h insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) real.h \
     toplev.h $(TM_P_H) langhooks.h
  explow.o : explow.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
     flags.h hard-reg-set.h insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) \
Index: calls.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/calls.c,v
retrieving revision 1.268
diff -c -5 -p -r1.268 calls.c
*** calls.c	19 Apr 2003 11:57:59 -0000	1.268
--- calls.c	21 Apr 2003 22:07:23 -0000
*************** Software Foundation, 59 Temple Place - S
*** 36,45 ****
--- 36,46 ----
  #include "timevar.h"
  #include "sbitmap.h"
  #include "langhooks.h"
  #include "target.h"
  #include "cgraph.h"
+ #include "except.h"
  
  /* Decide whether a function's arguments should be processed
     from first to last or from last to first.
  
     They should if the stack and args grow in opposite directions, but
*************** emit_call_1 (funexp, fndecl, funtype, st
*** 586,595 ****
--- 587,598 ----
    /* If this call can't throw, attach a REG_EH_REGION reg note to that
       effect.  */
    if (ecf_flags & ECF_NOTHROW)
      REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, const0_rtx,
  					       REG_NOTES (call_insn));
+   else
+     note_eh_region_may_contain_throw ();
  
    if (ecf_flags & ECF_NORETURN)
      REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_NORETURN, const0_rtx,
  					       REG_NOTES (call_insn));
    if (ecf_flags & ECF_ALWAYS_RETURN)
Index: except.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/except.c,v
retrieving revision 1.239
diff -c -5 -p -r1.239 except.c
*** except.c	15 Apr 2003 12:37:58 -0000	1.239
--- except.c	21 Apr 2003 22:07:24 -0000
*************** struct eh_region GTY(())
*** 207,216 ****
--- 207,219 ----
    rtx post_landing_pad;
  
    /* The RESX insn for handing off control to the next outermost handler,
       if appropriate.  */
    rtx resume;
+ 
+   /* True if something in this region may throw.  */
+   unsigned may_contain_throw : 1;
  };
  
  struct call_site_record GTY(())
  {
    rtx landing_pad;
*************** expand_eh_region_end_cleanup (handler)
*** 559,595 ****
    around_label = gen_label_rtx ();
    emit_jump (around_label);
  
    emit_label (region->label);
  
!   /* Give the language a chance to specify an action to be taken if an
!      exception is thrown that would propagate out of the HANDLER.  */
!   protect_cleanup_actions
!     = (lang_protect_cleanup_actions
!        ? (*lang_protect_cleanup_actions) ()
!        : NULL_TREE);
! 
!   if (protect_cleanup_actions)
!     expand_eh_region_start ();
! 
!   /* In case this cleanup involves an inline destructor with a try block in
!      it, we need to save the EH return data registers around it.  */
!   data_save[0] = gen_reg_rtx (ptr_mode);
!   emit_move_insn (data_save[0], get_exception_pointer (cfun));
!   data_save[1] = gen_reg_rtx (word_mode);
!   emit_move_insn (data_save[1], get_exception_filter (cfun));
! 
!   expand_expr (handler, const0_rtx, VOIDmode, 0);
  
!   emit_move_insn (cfun->eh->exc_ptr, data_save[0]);
!   emit_move_insn (cfun->eh->filter, data_save[1]);
  
!   if (protect_cleanup_actions)
!     expand_eh_region_end_must_not_throw (protect_cleanup_actions);
  
!   /* We need any stack adjustment complete before the around_label.  */
!   do_pending_stack_adjust ();
  
    /* We delay the generation of the _Unwind_Resume until we generate
       landing pads.  We emit a marker here so as to get good control
       flow data in the meantime.  */
    region->resume
--- 562,603 ----
    around_label = gen_label_rtx ();
    emit_jump (around_label);
  
    emit_label (region->label);
  
!   if (flag_non_call_exceptions 
!       || flag_forced_unwind_exceptions
!       || region->may_contain_throw)
!     {
!       /* Give the language a chance to specify an action to be taken if an
! 	 exception is thrown that would propagate out of the HANDLER.  */
!       protect_cleanup_actions
! 	= (lang_protect_cleanup_actions
! 	   ? (*lang_protect_cleanup_actions) ()
! 	   : NULL_TREE);
! 
!       if (protect_cleanup_actions)
! 	expand_eh_region_start ();
! 
!       /* In case this cleanup involves an inline destructor with a try block in
! 	 it, we need to save the EH return data registers around it.  */
!       data_save[0] = gen_reg_rtx (ptr_mode);
!       emit_move_insn (data_save[0], get_exception_pointer (cfun));
!       data_save[1] = gen_reg_rtx (word_mode);
!       emit_move_insn (data_save[1], get_exception_filter (cfun));
! 
!       expand_expr (handler, const0_rtx, VOIDmode, 0);
  
!       emit_move_insn (cfun->eh->exc_ptr, data_save[0]);
!       emit_move_insn (cfun->eh->filter, data_save[1]);
  
!       if (protect_cleanup_actions)
! 	expand_eh_region_end_must_not_throw (protect_cleanup_actions);
  
!       /* We need any stack adjustment complete before the around_label.  */
!       do_pending_stack_adjust ();
!     }
  
    /* We delay the generation of the _Unwind_Resume until we generate
       landing pads.  We emit a marker here so as to get good control
       flow data in the meantime.  */
    region->resume
*************** expand_eh_region_end_fixup (handler)
*** 819,828 ****
--- 827,852 ----
      return;
  
    fixup = expand_eh_region_end ();
    fixup->type = ERT_FIXUP;
    fixup->u.fixup.cleanup_exp = handler;
+ }
+ 
+ /* Note that the current EH region (if any) may contain a throw, or a
+    call to a function which itself may contain a throw.  */
+ 
+ void
+ note_eh_region_may_contain_throw ()
+ {
+   struct eh_region *region;
+ 
+   region = cfun->eh->cur_region;
+   while (region && !region->may_contain_throw)
+     {
+       region->may_contain_throw = 1;
+       region = region->outer;
+     }
  }
  
  /* Return an rtl expression for a pointer to the exception object
     within a handler.  */
  
Index: except.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/except.h,v
retrieving revision 1.66
diff -c -5 -p -r1.66 except.h
*** except.h	31 Mar 2003 05:14:53 -0000	1.66
--- except.h	21 Apr 2003 22:07:24 -0000
*************** extern void expand_eh_region_end_throw		
*** 76,85 ****
--- 76,89 ----
  /* End a fixup region.  Within this region the cleanups for the immediately
     enclosing region are _not_ run.  This is used for goto cleanup to avoid
     destroying an object twice.  */
  extern void expand_eh_region_end_fixup		PARAMS ((tree));
  
+ /* Note that the current EH region (if any) may contain a throw, or a
+    call to a function which itself may contain a throw.  */
+ extern void note_eh_region_may_contain_throw    PARAMS ((void));
+ 
  /* Invokes CALLBACK for every exception handler label.  Only used by old
     loop hackery; should not be used by new code.  */
  extern void for_each_eh_label			PARAMS ((void (*) (rtx)));
  
  /* Determine if the given INSN can throw an exception.  */
Index: cp/call.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/call.c,v
retrieving revision 1.376
diff -c -5 -p -r1.376 call.c
*** cp/call.c	12 Apr 2003 18:07:06 -0000	1.376
--- cp/call.c	21 Apr 2003 22:07:32 -0000
*************** build_over_call (struct z_candidate *can
*** 4705,4736 ****
    else if (DECL_INLINE (fn))
      fn = inline_conversion (fn);
    else
      fn = build_addr_func (fn);
  
    /* Recognize certain built-in functions so we can make tree-codes
       other than CALL_EXPR.  We do this when it enables fold-const.c
       to do something useful.  */
- 
    if (TREE_CODE (fn) == ADDR_EXPR
        && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
        && DECL_BUILT_IN (TREE_OPERAND (fn, 0)))
      {
        tree exp;
        exp = expand_tree_builtin (TREE_OPERAND (fn, 0), args, converted_args);
        if (exp)
  	return exp;
      }
  
!   /* Some built-in function calls will be evaluated at
!      compile-time in fold ().  */
!   fn = fold (build_call (fn, converted_args));
    if (VOID_TYPE_P (TREE_TYPE (fn)))
      return fn;
    fn = require_complete_type (fn);
    if (fn == error_mark_node)
      return error_mark_node;
    if (IS_AGGR_TYPE (TREE_TYPE (fn)))
      fn = build_cplus_new (TREE_TYPE (fn), fn);
    return convert_from_reference (fn);
  }
  
--- 4705,4758 ----
    else if (DECL_INLINE (fn))
      fn = inline_conversion (fn);
    else
      fn = build_addr_func (fn);
  
+   return build_cxx_call (fn, args, converted_args);
+ }
+ 
+ /* Build and return a call to FN, using the the CONVERTED_ARGS.  ARGS
+    gives the original form of the arguments.  This function performs
+    no overload resolution, conversion, or other high-level
+    operations.  */
+ 
+ tree
+ build_cxx_call(tree fn, tree args, tree converted_args)
+ {
+   tree fndecl;
+ 
    /* Recognize certain built-in functions so we can make tree-codes
       other than CALL_EXPR.  We do this when it enables fold-const.c
       to do something useful.  */
    if (TREE_CODE (fn) == ADDR_EXPR
        && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
        && DECL_BUILT_IN (TREE_OPERAND (fn, 0)))
      {
        tree exp;
        exp = expand_tree_builtin (TREE_OPERAND (fn, 0), args, converted_args);
        if (exp)
  	return exp;
      }
  
!   fn = build_call (fn, converted_args);
! 
!   /* If this call might throw an exception, note that fact.  */
!   fndecl = get_callee_fndecl (fn);
!   if ((!fndecl || !TREE_NOTHROW (fndecl)) && at_function_scope_p ())
!     cp_function_chain->can_throw = 1;
! 
!   /* Some built-in function calls will be evaluated at compile-time in
!      fold ().  */
!   fn = fold (fn);
! 
    if (VOID_TYPE_P (TREE_TYPE (fn)))
      return fn;
+ 
    fn = require_complete_type (fn);
    if (fn == error_mark_node)
      return error_mark_node;
+ 
    if (IS_AGGR_TYPE (TREE_TYPE (fn)))
      fn = build_cplus_new (TREE_TYPE (fn), fn);
    return convert_from_reference (fn);
  }
  
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.837
diff -c -5 -p -r1.837 cp-tree.h
*** cp/cp-tree.h	10 Apr 2003 08:07:11 -0000	1.837
--- cp/cp-tree.h	21 Apr 2003 22:07:33 -0000
*************** struct language_function GTY(())
*** 792,801 ****
--- 792,804 ----
    int returns_abnormally;
    int in_function_try_handler;
    int in_base_initializer;
    int x_expanding_p;
  
+   /* True if this function can throw an exception.  */
+   bool can_throw : 1;
+ 
    struct named_label_use_list *x_named_label_uses;
    struct named_label_list *x_named_labels;
    struct cp_binding_level *bindings;
    varray_type x_local_names;
  
*************** extern bool is_properly_derived_from (tr
*** 3520,3529 ****
--- 3523,3533 ----
  extern tree initialize_reference (tree, tree, tree);
  extern tree make_temporary_var_for_ref_to_temp (tree, tree);
  extern tree strip_top_quals (tree);
  extern tree perform_implicit_conversion (tree, tree);
  extern tree in_charge_arg_for_name (tree);
+ extern tree build_cxx_call (tree, tree, tree);
  
  /* in class.c */
  extern tree build_base_path			(enum tree_code, tree, tree, int);
  extern tree convert_to_base                     (tree, tree, bool);
  extern tree build_vtbl_ref			(tree, tree);
Index: cp/decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl.c,v
retrieving revision 1.1037
diff -c -5 -p -r1.1037 decl.c
*** cp/decl.c	17 Apr 2003 21:27:34 -0000	1.1037
--- cp/decl.c	21 Apr 2003 22:07:37 -0000
*************** finish_function (int flags)
*** 13959,13968 ****
--- 13959,13974 ----
      }
  
    /* If we're saving up tree structure, tie off the function now.  */
    finish_stmt_tree (&DECL_SAVED_TREE (fndecl));
  
+   /* If this function can't throw any exceptions, remember that.  */
+   if (!processing_template_decl
+       && !cp_function_chain->can_throw
+       && !flag_non_call_exceptions)
+     TREE_NOTHROW (fndecl) = 1;
+ 
    /* This must come after expand_function_end because cleanups might
       have declarations (from inline functions) that need to go into
       this function's blocks.  */
    
    /* If the current binding level isn't the outermost binding level
Index: cp/decl2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl2.c,v
retrieving revision 1.618
diff -c -5 -p -r1.618 decl2.c
*** cp/decl2.c	20 Apr 2003 11:57:38 -0000	1.618
--- cp/decl2.c	21 Apr 2003 22:07:39 -0000
*************** mark_used (tree decl)
*** 4579,4589 ****
       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))))
!     instantiate_decl (decl, /*defer_ok=*/1);
  }
  
  /* Called when a class-head is encountered.  TAG_KIND is the class-key
     for the class.  SCOPE, if non-NULL, is the type or namespace
     indicated in the nested-name-specifier for the declaration of the
--- 4579,4615 ----
       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
! 	 functions is expensive, and the inliner knows to
! 	 instantiate any functions it might need.
! 
! 	 However, if instantiating this function might help us mark
! 	 the current function TREE_NOTHROW, we go ahead and
! 	 instantiate it now.  */
!       defer = (!flag_exceptions
! 	       || TREE_CODE (decl) != FUNCTION_DECL
! 	       /* If the called function can't throw, we don't need to
! 		  generate its body to find that out.  */
! 	       || TREE_NOTHROW (decl)
! 	       || !cfun
! 	       /* If we already know the current function can't throw,
! 		  then we don't need to work hard to prove it.  */
! 	       || TREE_NOTHROW (current_function_decl)
! 	       /* If we already know that the current function *can*
! 		  throw, there's no point in gathering more
! 		  information.  */
! 	       || cp_function_chain->can_throw);
! 
!       instantiate_decl (decl, defer);
!     }
  }
  
  /* Called when a class-head is encountered.  TAG_KIND is the class-key
     for the class.  SCOPE, if non-NULL, is the type or namespace
     indicated in the nested-name-specifier for the declaration of the
Index: cp/optimize.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/optimize.c,v
retrieving revision 1.89
diff -c -5 -p -r1.89 optimize.c
*** cp/optimize.c	2 Mar 2003 21:18:16 -0000	1.89
--- cp/optimize.c	21 Apr 2003 22:07:39 -0000
*************** maybe_clone_body (tree fn)
*** 257,266 ****
--- 257,269 ----
        DECL_NUM_STMTS (clone) = DECL_NUM_STMTS (fn);
  
        /* Clean up.  */
        splay_tree_delete (decl_map);
  
+       /* The clone can throw iff the original function can throw.  */
+       cp_function_chain->can_throw = !TREE_NOTHROW (fn);
+ 
        /* Now, expand this function into RTL, if appropriate.  */
        finish_function (0);
        BLOCK_ABSTRACT_ORIGIN (DECL_INITIAL (clone)) = DECL_INITIAL (fn);
        expand_body (clone);
        pop_from_top_level ();
Index: cp/pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.681
diff -c -5 -p -r1.681 pt.c
*** cp/pt.c	17 Apr 2003 14:05:53 -0000	1.681
--- cp/pt.c	21 Apr 2003 22:07:42 -0000
*************** instantiate_decl (d, defer_ok)
*** 10946,10962 ****
        /* Clear out DECL_RTL; whatever was there before may not be right
  	 since we've reset the type of the declaration.  */
        SET_DECL_RTL (d, NULL_RTX);
  
        DECL_IN_AGGR_P (d) = 0;
!       if (DECL_INTERFACE_KNOWN (d))
! 	DECL_EXTERNAL (d) = ! DECL_NOT_REALLY_EXTERN (d);
!       else
! 	{
! 	  DECL_EXTERNAL (d) = 1;
! 	  DECL_NOT_REALLY_EXTERN (d) = 1;
! 	}
        cp_finish_decl (d, 
  		      (!DECL_INITIALIZED_IN_CLASS_P (d) 
  		       ? DECL_INITIAL (d) : NULL_TREE),
  		      NULL_TREE, 0);
      }
--- 10946,10957 ----
        /* Clear out DECL_RTL; whatever was there before may not be right
  	 since we've reset the type of the declaration.  */
        SET_DECL_RTL (d, NULL_RTX);
  
        DECL_IN_AGGR_P (d) = 0;
!       import_export_decl (d);
!       DECL_EXTERNAL (d) = ! DECL_NOT_REALLY_EXTERN (d);
        cp_finish_decl (d, 
  		      (!DECL_INITIALIZED_IN_CLASS_P (d) 
  		       ? DECL_INITIAL (d) : NULL_TREE),
  		      NULL_TREE, 0);
      }
*************** instantiate_decl (d, defer_ok)
*** 11005,11015 ****
        /* We don't need the local specializations any more.  */
        htab_delete (local_specializations);
        local_specializations = saved_local_specializations;
  
        /* Finish the function.  */
!       expand_body (finish_function (0));
      }
  
    /* We're not deferring instantiation any more.  */
    TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (d)) = 0;
  
--- 11000,11012 ----
        /* We don't need the local specializations any more.  */
        htab_delete (local_specializations);
        local_specializations = saved_local_specializations;
  
        /* Finish the function.  */
!       d = finish_function (0);
!       import_export_decl (d);
!       expand_body (d);
      }
  
    /* We're not deferring instantiation any more.  */
    TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (d)) = 0;
  
Index: cp/rtti.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/rtti.c,v
retrieving revision 1.159
diff -c -5 -p -r1.159 rtti.c
*** cp/rtti.c	13 Apr 2003 01:45:32 -0000	1.159
--- cp/rtti.c	21 Apr 2003 22:07:42 -0000
*************** throw_bad_cast (void)
*** 175,185 ****
      fn = IDENTIFIER_GLOBAL_VALUE (fn);
    else
      fn = push_throw_library_fn (fn, build_function_type (ptr_type_node,
  							 void_list_node));
    
!   return build_call (fn, NULL_TREE);
  }
  
  /* Return an expression for "__cxa_bad_typeid()".  The expression
     returned is an lvalue of type "const std::type_info".  */
  
--- 175,185 ----
      fn = IDENTIFIER_GLOBAL_VALUE (fn);
    else
      fn = push_throw_library_fn (fn, build_function_type (ptr_type_node,
  							 void_list_node));
    
!   return build_cxx_call (fn, NULL_TREE, NULL_TREE);
  }
  
  /* Return an expression for "__cxa_bad_typeid()".  The expression
     returned is an lvalue of type "const std::type_info".  */
  
*************** build_dynamic_cast_1 (tree type, tree ex
*** 658,668 ****
  	      tmp = build_function_type (ptr_type_node, tmp);
  	      dcast_fn = build_library_fn_ptr (name, tmp);
                pop_nested_namespace (ns);
                dynamic_cast_node = dcast_fn;
  	    }
!           result = build_call (dcast_fn, elems);
  
  	  if (tc == REFERENCE_TYPE)
  	    {
  	      tree bad = throw_bad_cast ();
  	      
--- 658,668 ----
  	      tmp = build_function_type (ptr_type_node, tmp);
  	      dcast_fn = build_library_fn_ptr (name, tmp);
                pop_nested_namespace (ns);
                dynamic_cast_node = dcast_fn;
  	    }
!           result = build_cxx_call (dcast_fn, elems, elems);
  
  	  if (tc == REFERENCE_TYPE)
  	    {
  	      tree bad = throw_bad_cast ();
  	      
Index: cp/typeck.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/typeck.c,v
retrieving revision 1.456
diff -c -5 -p -r1.456 typeck.c
*** cp/typeck.c	21 Apr 2003 11:16:57 -0000	1.456
--- cp/typeck.c	21 Apr 2003 22:07:44 -0000
*************** get_member_function_from_ptrfunc (instan
*** 2658,2668 ****
  tree
  build_function_call (function, params)
       tree function, params;
  {
    register tree fntype, fndecl;
-   register tree value_type;
    register tree coerced_params;
    tree result;
    tree name = NULL_TREE, assembler_name = NULL_TREE;
    int is_method;
    tree original = function;
--- 2658,2667 ----
*************** build_function_call (function, params)
*** 2753,2773 ****
  				    params, coerced_params);
        if (result)
  	return result;
      }
  
!   /* Some built-in function calls will be evaluated at
!      compile-time in fold ().  */
!   result = fold (build_call (function, coerced_params));
!   value_type = TREE_TYPE (result);
! 
!   if (TREE_CODE (value_type) == VOID_TYPE)
!     return result;
!   result = require_complete_type (result);
!   if (IS_AGGR_TYPE (value_type))
!     result = build_cplus_new (value_type, result);
!   return convert_from_reference (result);
  }
  
  /* Convert the actual parameter expressions in the list VALUES
     to the types in the list TYPELIST.
     If parmdecls is exhausted, or when an element has NULL as its type,
--- 2752,2762 ----
  				    params, coerced_params);
        if (result)
  	return result;
      }
  
!   return build_cxx_call (function, params, coerced_params);
  }
  
  /* Convert the actual parameter expressions in the list VALUES
     to the types in the list TYPELIST.
     If parmdecls is exhausted, or when an element has NULL as its type,
Index: testsuite/g++.dg/template/recurse.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.dg/template/recurse.C,v
retrieving revision 1.3
diff -c -5 -p -r1.3 recurse.C
*** testsuite/g++.dg/template/recurse.C	18 Mar 2002 07:39:24 -0000	1.3
--- testsuite/g++.dg/template/recurse.C	21 Apr 2003 22:07:48 -0000
***************
*** 4,14 ****
  template <int I> struct F
  {
    int operator()()
      {
        F<I+1> f;			// { dg-error "" "" }
!       return f()*I;
      }
  };
  
  template <> struct F<52>
  {
--- 4,14 ----
  template <int I> struct F
  {
    int operator()()
      {
        F<I+1> f;			// { dg-error "" "" }
!       return f()*I;             // { dg-error "" "" }
      }
  };
  
  template <> struct F<52>
  {


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]