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]

Deferred PATCH to tighten throw codegen


This patch improves codegen for throw-expressions in two ways:

  1) It constructs a thrown temporary directly into the exception object,
     rather than create it, copy it, and destroy it.
  2) It runs cleanups for other temporaries in the thrown expression 
     before the call to __cxa_throw, rather than during unwinding.

I'm not checking this in now, as it's just an optimization, and as such
doesn't seem appropriate for this point in development.  But I thought I
might as well send it out anyway.

2002-01-21  Jason Merrill  <jason@redhat.com>

	* tree.h (TARGET_EXPR_SLOT, TARGET_EXPR_INITIAL): New macros.
	(TARGET_EXPR_CLEANUP): New macro.

	* except.c (build_throw): Destroy temporaries from the thrown
	expression before calling __cxa_throw.  Construct a thrown 
	temporary directly into the exception object.
	(stabilize_throw_expr): New function.
	(wrap_cleanups_r): New function.
	* tree.c (stabilize_expr): New function.
	* init.c (build_init): New function.

*** cp-tree.h.~1~	Thu Jan 17 12:54:18 2002
--- cp-tree.h	Mon Jan 21 01:37:28 2002
*************** extern void init_init_processing		PARAMS
*** 3890,3895 ****
--- 3890,3896 ----
  extern void emit_base_init			PARAMS ((tree, tree));
  extern tree expand_member_init			PARAMS ((tree, tree, tree));
  extern tree build_aggr_init			PARAMS ((tree, tree, int));
+ extern tree build_init				PARAMS ((tree, tree, int));
  extern int is_aggr_type				PARAMS ((tree, int));
  extern tree get_aggr_from_typedef		PARAMS ((tree, int));
  extern tree get_type_value			PARAMS ((tree));
*************** extern tree cp_copy_res_decl_for_inlinin
*** 4277,4282 ****
--- 4278,4284 ----
  						   int*, void*));
  extern int cp_start_inlining			PARAMS ((tree));
  extern void cp_end_inlining			PARAMS ((tree));
+ extern tree stabilize_expr			PARAMS ((tree, tree *));
  
  /* in typeck.c */
  extern int string_conv_p			PARAMS ((tree, tree, int));
*** except.c.~1~	Tue Dec 18 15:18:18 2001
--- except.c	Mon Jan 21 13:22:01 2002
*************** Boston, MA 02111-1307, USA.  */
*** 35,40 ****
--- 35,41 ----
  #include "output.h"
  #include "except.h"
  #include "toplev.h"
+ #include "tree-inline.h"
  
  static void push_eh_cleanup PARAMS ((tree));
  static tree prepare_eh_type PARAMS ((tree));
*************** static void push_eh_cleanup PARAMS ((tre
*** 46,60 ****
  static bool decl_is_java_type PARAMS ((tree decl, int err));
  static void initialize_handler_parm PARAMS ((tree, tree));
  static tree do_allocate_exception PARAMS ((tree));
  static int complete_ptr_ref_or_void_ptr_p PARAMS ((tree, tree));
  static bool is_admissible_throw_operand PARAMS ((tree));
  static int can_convert_eh PARAMS ((tree, tree));
  static void check_handlers_1 PARAMS ((tree, tree));
  static tree cp_protect_cleanup_actions PARAMS ((void));
  
- #include "decl.h"
- #include "obstack.h"
- 
  /* Sets up all the global eh stuff that needs to be initialized at the
     start of compilation.  */
  
--- 47,60 ----
  static bool decl_is_java_type PARAMS ((tree decl, int err));
  static void initialize_handler_parm PARAMS ((tree, tree));
  static tree do_allocate_exception PARAMS ((tree));
+ static tree stabilize_throw_expr PARAMS ((tree, tree *));
+ static tree wrap_cleanups_r PARAMS ((tree *, int *, void *));
  static int complete_ptr_ref_or_void_ptr_p PARAMS ((tree, tree));
  static bool is_admissible_throw_operand PARAMS ((tree));
  static int can_convert_eh PARAMS ((tree, tree));
  static void check_handlers_1 PARAMS ((tree, tree));
  static tree cp_protect_cleanup_actions PARAMS ((void));
  
  /* Sets up all the global eh stuff that needs to be initialized at the
     start of compilation.  */
  
*************** do_allocate_exception (type)
*** 518,524 ****
  
  #if 0
  /* Call __cxa_free_exception from a cleanup.  This is never invoked
!    directly.  */
  
  static tree
  do_free_exception (ptr)
--- 518,524 ----
  
  #if 0
  /* Call __cxa_free_exception from a cleanup.  This is never invoked
!    directly, but see the comment for stabilize_throw_expr.  */
  
  static tree
  do_free_exception (ptr)
*************** do_free_exception (ptr)
*** 540,545 ****
--- 540,628 ----
  }
  #endif
  
+ /* Wrap all cleanups for TARGET_EXPRs in MUST_NOT_THROW_EXPR.
+    Called from build_throw via walk_tree_without_duplicates.  */
+ 
+ static tree
+ wrap_cleanups_r (tp, walk_subtrees, data)
+      tree *tp;
+      int *walk_subtrees ATTRIBUTE_UNUSED;
+      void *data ATTRIBUTE_UNUSED;
+ {
+   tree exp = *tp;
+   tree cleanup;
+ 
+   /* Don't walk into types.  */
+   if (TYPE_P (exp))
+     {
+       *walk_subtrees = 0;
+       return NULL_TREE;
+     }
+   if (TREE_CODE (exp) != TARGET_EXPR)
+     return NULL_TREE;
+ 
+   cleanup = TARGET_EXPR_CLEANUP (exp);
+   if (cleanup)
+     {
+       cleanup = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (cleanup), cleanup);
+       TARGET_EXPR_CLEANUP (exp) = cleanup;
+     }
+ 
+   /* Keep iterating.  */
+   return NULL_TREE;
+ }
+ 
+ /* Like stabilize_expr, but specifically for a thrown expression.  When
+    throwing a temporary class object, we want to construct it directly into
+    the thrown exception, so we look past the TARGET_EXPR and stabilize the
+    arguments of the call instead.
+ 
+    The case where EXP is a call to a function returning a class is a bit of
+    a grey area in the standard; it's unclear whether or not it should be
+    allowed to throw.  I'm going to say no, as that allows us to optimize
+    this case without worrying about deallocating the exception object if it
+    does.  The alternatives would be either not optimizing this case, or
+    wrapping the initialization in a TRY_CATCH_EXPR to call do_free_exception
+    rather than in a MUST_NOT_THROW_EXPR, for this case only.  */
+ 
+ static tree
+ stabilize_throw_expr (exp, initp)
+      tree exp;
+      tree *initp;
+ {
+   tree init_expr;
+ 
+   if (TREE_CODE (exp) == TARGET_EXPR
+       && TREE_CODE (TARGET_EXPR_INITIAL (exp)) == AGGR_INIT_EXPR
+       && flag_elide_constructors)
+     {
+       tree aggr_init = AGGR_INIT_EXPR_CHECK (TARGET_EXPR_INITIAL (exp));
+       tree args = TREE_OPERAND (aggr_init, 1);
+       tree newargs = NULL_TREE;
+       tree *p = &newargs;
+ 
+       init_expr = void_zero_node;
+       for (; args; args = TREE_CHAIN (args))
+ 	{
+ 	  tree arg_init_expr;
+ 	  tree newarg = stabilize_expr (TREE_VALUE (args), &arg_init_expr);
+ 
+ 	  if (arg_init_expr != void_zero_node)
+ 	    init_expr = build (COMPOUND_EXPR, void_type_node, arg_init_expr, init_expr);
+ 	  *p = tree_cons (NULL_TREE, newarg, NULL_TREE);
+ 	  p = &TREE_CHAIN (*p);
+ 	}
+       TREE_OPERAND (aggr_init, 1) = newargs;
+     }
+   else
+     {
+       exp = stabilize_expr (exp, &init_expr);
+     }
+ 
+   *initp = init_expr;
+   return exp;
+ }
+ 
  /* Build a throw expression.  */
  
  tree
*************** build_throw (exp)
*** 585,594 ****
      {
        tree throw_type;
        tree cleanup;
-       tree stmt_expr;
-       tree compound_stmt;
        tree object, ptr;
        tree tmp;
  
        fn = get_identifier ("__cxa_throw");
        if (IDENTIFIER_GLOBAL_VALUE (fn))
--- 668,676 ----
      {
        tree throw_type;
        tree cleanup;
        tree object, ptr;
        tree tmp;
+       tree temp_expr, allocate_expr;
  
        fn = get_identifier ("__cxa_throw");
        if (IDENTIFIER_GLOBAL_VALUE (fn))
*************** build_throw (exp)
*** 614,621 ****
  	  fn = push_throw_library_fn (fn, tmp);
  	}
  
-       begin_init_stmts (&stmt_expr, &compound_stmt);
- 
        /* throw expression */
        /* First, decay it.  */
        exp = decay_conversion (exp);
--- 696,701 ----
*************** build_throw (exp)
*** 633,669 ****
  	 the call to __cxa_allocate_exception first (which doesn't
  	 matter, since it can't throw).  */
  
!       my_friendly_assert (stmts_are_full_exprs_p () == 1, 19990926);
! 
!       /* Store the throw expression into a temp.  This can be less
! 	 efficient than storing it into the allocated space directly, but
! 	 if we allocated the space first we would have to deal with
! 	 cleaning it up if evaluating this expression throws.  */
!       if (TREE_SIDE_EFFECTS (exp))
! 	{
! 	  tmp = create_temporary_var (TREE_TYPE (exp));
! 	  DECL_INITIAL (tmp) = exp;
! 	  cp_finish_decl (tmp, exp, NULL_TREE, LOOKUP_ONLYCONVERTING);
! 	  exp = tmp;
! 	}
  
        /* Allocate the space for the exception.  */
!       ptr = create_temporary_var (ptr_type_node);
!       DECL_REGISTER (ptr) = 1;
!       cp_finish_decl (ptr, NULL_TREE, NULL_TREE, LOOKUP_ONLYCONVERTING);
!       tmp = do_allocate_exception (TREE_TYPE (exp));
!       tmp = build_modify_expr (ptr, INIT_EXPR, tmp);
!       finish_expr_stmt (tmp);
! 
        object = build1 (NOP_EXPR, build_pointer_type (TREE_TYPE (exp)), ptr);
        object = build_indirect_ref (object, NULL);
  
!       exp = build_modify_expr (object, INIT_EXPR, exp);
        if (exp == error_mark_node)
! 	error ("  in thrown expression");
  
        exp = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (exp), exp);
!       finish_expr_stmt (exp);
  
        throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object)));
  
--- 713,752 ----
  	 the call to __cxa_allocate_exception first (which doesn't
  	 matter, since it can't throw).  */
  
!       /* Pre-evaluate the thrown expression first, since if we allocated
! 	 the space first we would have to deal with cleaning it up if
! 	 evaluating this expression throws.  */
!       exp = stabilize_throw_expr (exp, &temp_expr);
  
        /* Allocate the space for the exception.  */
!       allocate_expr = do_allocate_exception (TREE_TYPE (exp));
!       allocate_expr = get_target_expr (allocate_expr);
!       ptr = TARGET_EXPR_SLOT (allocate_expr);
        object = build1 (NOP_EXPR, build_pointer_type (TREE_TYPE (exp)), ptr);
        object = build_indirect_ref (object, NULL);
  
!       /* And initialize the exception object.  */
!       exp = build_init (object, exp, LOOKUP_ONLYCONVERTING);
        if (exp == error_mark_node)
! 	{
! 	  error ("  in thrown expression");
! 	  return error_mark_node;
! 	}
  
        exp = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (exp), exp);
!       /* Prepend the allocation.  */
!       exp = build (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp);
!       if (temp_expr != void_zero_node)
! 	{
! 	  /* Prepend the calculation of the throw expression.  Also, force
! 	     any cleanups from the expression to be evaluated here so that
! 	     we don't have to do them during unwinding.  But first wrap
! 	     them in MUST_NOT_THROW_EXPR, since they are run after the
! 	     exception object is initialized.  */
! 	  walk_tree_without_duplicates (&temp_expr, wrap_cleanups_r, 0);
! 	  exp = build (COMPOUND_EXPR, TREE_TYPE (exp), temp_expr, exp);
! 	  exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
! 	}
  
        throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object)));
  
*************** build_throw (exp)
*** 686,698 ****
        tmp = tree_cons (NULL_TREE, cleanup, NULL_TREE);
        tmp = tree_cons (NULL_TREE, throw_type, tmp);
        tmp = tree_cons (NULL_TREE, ptr, tmp);
-       tmp = build_function_call (fn, tmp);
- 
        /* ??? Indicate that this function call throws throw_type.  */
  
!       finish_expr_stmt (tmp);
! 
!       exp = finish_init_stmts (stmt_expr, compound_stmt);
      }
    else
      {
--- 769,779 ----
        tmp = tree_cons (NULL_TREE, cleanup, NULL_TREE);
        tmp = tree_cons (NULL_TREE, throw_type, tmp);
        tmp = tree_cons (NULL_TREE, ptr, tmp);
        /* ??? Indicate that this function call throws throw_type.  */
+       tmp = build_function_call (fn, tmp);
  
!       /* Tack on the initialization stuff.  */
!       exp = build (COMPOUND_EXPR, TREE_TYPE (tmp), exp, tmp);
      }
    else
      {
*************** build_throw (exp)
*** 708,713 ****
--- 789,796 ----
  	    (fn, build_function_type (void_type_node, void_list_node));
  	}
  
+       /* ??? Indicate that this function call allows exceptions of the type
+ 	 of the enclosing catch block (if known).  */	 
        exp = build_function_call (fn, NULL_TREE);
      }
  
*** init.c.~1~	Wed Jan 16 13:09:36 2002
--- init.c	Mon Jan 21 01:35:30 2002
*************** build_aggr_init (exp, init, flags)
*** 1209,1214 ****
--- 1209,1234 ----
    return stmt_expr;
  }
  
+ /* Like build_aggr_init, but not just for aggregates.  */
+ 
+ tree
+ build_init (decl, init, flags)
+      tree decl, init;
+      int flags;
+ {
+   tree expr;
+ 
+   if (IS_AGGR_TYPE (TREE_TYPE (decl))
+       || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+     expr = build_aggr_init (decl, init, flags);
+   else
+     {
+       expr = build (INIT_EXPR, TREE_TYPE (decl), decl, init);
+       TREE_SIDE_EFFECTS (expr) = 1;
+     }
+   return expr;
+ }
+ 
  static void
  expand_default_init (binfo, true_exp, exp, init, flags)
       tree binfo;
*** tree.c.~1~	Tue Dec 18 15:18:19 2001
--- tree.c	Mon Jan 21 01:37:06 2002
*************** decl_linkage (decl)
*** 2460,2462 ****
--- 2460,2495 ----
    /* Everything else has internal linkage.  */
    return lk_internal;
  }
+ 
+ /* EXP is an expression that we want to pre-evaluate.  Returns via INITP an
+    expression to perform the pre-evaluation, and returns directly an
+    expression to use the precalculated result.  */
+ 
+ tree
+ stabilize_expr (exp, initp)
+      tree exp;
+      tree *initp;
+ {
+   tree init_expr;
+ 
+   if (!TREE_SIDE_EFFECTS (exp))
+     {
+       init_expr = void_zero_node;
+     }
+   else if (!real_lvalue_p (exp)
+ 	   || !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (exp)))
+     {
+       init_expr = get_target_expr (exp);
+       exp = TARGET_EXPR_SLOT (init_expr);
+     }
+   else
+     {
+       exp = build_unary_op (ADDR_EXPR, exp, 1);
+       init_expr = get_target_expr (exp);
+       exp = TARGET_EXPR_SLOT (init_expr);
+       exp = build_indirect_ref (exp, 0);
+     }
+ 
+   *initp = init_expr;
+   return exp;
+ }
*** tree.h.~1~	Wed Jan 16 19:16:36 2002
--- tree.h	Fri Jan 18 19:01:45 2002
*************** struct tree_vec
*** 857,862 ****
--- 857,867 ----
  #define EXPR_WFL_SET_LINECOL(NODE, LINE, COL) \
    (EXPR_WFL_LINECOL(NODE) = ((LINE) << 12) | ((COL) & 0xfff))
  
+ /* In a TARGET_EXPR node.  */
+ #define TARGET_EXPR_SLOT(NODE) TREE_OPERAND (TARGET_EXPR_CHECK (NODE), 0)
+ #define TARGET_EXPR_INITIAL(NODE) TREE_OPERAND (TARGET_EXPR_CHECK (NODE), 1)
+ #define TARGET_EXPR_CLEANUP(NODE) TREE_OPERAND (TARGET_EXPR_CHECK (NODE), 2)
+ 
  struct tree_exp
  {
    struct tree_common common;

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