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]

C++ PATCH: build_new_1 overhaul


While working on the last patch and another one, I finally mustered the
activation energy to clean up build_new_1, primarily to remove the calls to
SAVE_EXPR that have caused such headaches over the years.  Now we do all
operations on alloc_node rather than try to pass the value from one stage
to the next.  Much simpler.

Tested i686-pc-linux-gnu.  No new testcase.

2000-12-15  Jason Merrill  <jason@redhat.com>

	* init.c (build_new_1): Reorganize.  Now with 100% fewer SAVE_EXPRs!

*** init.c.~1~	Fri Dec 15 14:29:08 2000
--- init.c	Fri Dec 15 16:25:25 2000
*************** build_new_1 (exp)
*** 2236,2244 ****
       tree exp;
  {
    tree placement, init;
!   tree type, true_type, size, rval;
    tree nelts = NULL_TREE;
!   tree alloc_expr, alloc_node = NULL_TREE;
    int has_array = 0;
    enum tree_code code;
    int use_cookie, nothrow, check_new;
--- 2236,2245 ----
       tree exp;
  {
    tree placement, init;
!   tree type, true_type, size, rval, t;
    tree nelts = NULL_TREE;
!   tree alloc_call, alloc_expr, alloc_node;
!   tree cookie_expr, init_expr;
    int has_array = 0;
    enum tree_code code;
    int use_cookie, nothrow, check_new;
*************** build_new_1 (exp)
*** 2283,2293 ****
    if (!complete_type_or_else (true_type, exp))
      return error_mark_node;
  
    if (has_array)
!     size = fold (cp_build_binary_op (MULT_EXPR, size_in_bytes (true_type),
! 				     nelts));
!   else
!     size = size_in_bytes (type);
  
    if (TREE_CODE (true_type) == VOID_TYPE)
      {
--- 2281,2289 ----
    if (!complete_type_or_else (true_type, exp))
      return error_mark_node;
  
+   size = size_in_bytes (true_type);
    if (has_array)
!     size = fold (cp_build_binary_op (MULT_EXPR, size, nelts));
  
    if (TREE_CODE (true_type) == VOID_TYPE)
      {
*************** build_new_1 (exp)
*** 2302,2309 ****
       new.  */
    if (!globally_qualified_p
        && IS_AGGR_TYPE (true_type)
!       && ((!has_array && TYPE_HAS_NEW_OPERATOR (true_type))
! 	  || (has_array && TYPE_HAS_ARRAY_NEW_OPERATOR (true_type))))
      use_global_new = 0;
    else
      use_global_new = 1;
--- 2298,2306 ----
       new.  */
    if (!globally_qualified_p
        && IS_AGGR_TYPE (true_type)
!       && (has_array
! 	  ? TYPE_HAS_ARRAY_NEW_OPERATOR (true_type)
! 	  : TYPE_HAS_NEW_OPERATOR (true_type)))
      use_global_new = 0;
    else
      use_global_new = 1;
*************** build_new_1 (exp)
*** 2333,2341 ****
        size = size_binop (PLUS_EXPR, size, cookie_size);
      }
  
-   if (has_array && init && pedantic)
-     cp_pedwarn ("initialization in array new");
- 
    /* Allocate the object.  */
    
    if (! placement && TYPE_FOR_JAVA (true_type))
--- 2330,2335 ----
*************** build_new_1 (exp)
*** 2349,2359 ****
        if (alloc_decl == NULL_TREE)
  	fatal("call to Java constructor, while `%s' undefined", alloc_name);
        class_addr = build1 (ADDR_EXPR, jclass_node, class_decl);
!       rval = build_function_call (alloc_decl,
! 				  tree_cons (NULL_TREE, class_addr,
! 					     build_tree_list (NULL_TREE,
! 							      class_size)));
!       rval = cp_convert (build_pointer_type (true_type), rval);
      }
    else
      {
--- 2343,2352 ----
        if (alloc_decl == NULL_TREE)
  	fatal("call to Java constructor, while `%s' undefined", alloc_name);
        class_addr = build1 (ADDR_EXPR, jclass_node, class_decl);
!       alloc_call = (build_function_call
! 		    (alloc_decl,
! 		     tree_cons (NULL_TREE, class_addr,
! 				build_tree_list (NULL_TREE, class_size))));
      }
    else
      {
*************** build_new_1 (exp)
*** 2364,2382 ****
        fnname = ansi_opname (code);
  
        if (use_global_new)
! 	rval = (build_new_function_call 
! 		(lookup_function_nonclass (fnname, args),
! 		 args));
        else
! 	rval = build_method_call (build_dummy_object (true_type),
! 				  fnname, args, NULL_TREE,
! 				  LOOKUP_NORMAL);
!       rval = cp_convert (build_pointer_type (true_type), rval);
      }
  
!   if (rval == error_mark_node)
      return error_mark_node;
  
    /*        unless an allocation function is declared with an empty  excep-
       tion-specification  (_except.spec_),  throw(), it indicates failure to
       allocate storage by throwing a bad_alloc exception  (clause  _except_,
--- 2357,2377 ----
        fnname = ansi_opname (code);
  
        if (use_global_new)
! 	alloc_call = (build_new_function_call 
! 		      (lookup_function_nonclass (fnname, args),
! 		       args));
        else
! 	alloc_call = build_method_call (build_dummy_object (true_type),
! 					fnname, args, NULL_TREE,
! 					LOOKUP_NORMAL);
      }
  
!   if (alloc_call == error_mark_node)
      return error_mark_node;
  
+   if (alloc_call == NULL_TREE)
+     abort ();
+ 
    /*        unless an allocation function is declared with an empty  excep-
       tion-specification  (_except.spec_),  throw(), it indicates failure to
       allocate storage by throwing a bad_alloc exception  (clause  _except_,
*************** build_new_1 (exp)
*** 2387,2460 ****
  
       So check for a null exception spec on the op new we just called.  */
  
!   nothrow = 0;
!   if (rval)
!     {
!       /* The CALL_EXPR.  */
!       tree t = TREE_OPERAND (rval, 0);
!       /* The function.  */
!       t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
!       nothrow = TYPE_NOTHROW_P (TREE_TYPE (t));
!     }
    check_new = (flag_check_new || nothrow) && ! use_java_new;
  
!   if ((check_new || flag_exceptions) && rval)
!     {
!       alloc_expr = get_target_expr (rval);
!       alloc_node = rval = TREE_OPERAND (alloc_expr, 0);
!     }
!   else
!     alloc_expr = NULL_TREE;
  
!   /* if rval is NULL_TREE I don't have to allocate it, but are we
!      totally sure we have some extra bytes in that case for the
!      cookies? And how does that interact with the code below? (mrs) */
!   /* Finish up some magic for new'ed arrays */
!   if (use_cookie && rval != NULL_TREE)
      {
!       tree cookie, exp1;
!       rval = convert (string_type_node, rval); /* for ptr arithmetic */
!       rval = save_expr (cp_build_binary_op (PLUS_EXPR, rval, cookie_size));
        /* Store the number of bytes allocated so that we can know how
  	 many elements to destroy later.  */
        if (flag_new_abi)
  	{
  	  /* Under the new ABI, we use the last sizeof (size_t) bytes
  	     to store the number of elements.  */
! 	  cookie = build_indirect_ref (build (MINUS_EXPR,
! 					      build_pointer_type (sizetype),
! 					      rval,
! 					      size_in_bytes (sizetype)),
! 				       NULL_PTR);
! 	  exp1 = build (MODIFY_EXPR, void_type_node, cookie, nelts);
  	}
        else
  	{
! 	  cookie 
! 	    = build_indirect_ref (build (MINUS_EXPR,
! 					 build_pointer_type (BI_header_type),
! 					 rval, cookie_size), NULL_PTR);
! 	  exp1 = build (MODIFY_EXPR, void_type_node,
! 			build_component_ref (cookie, nelts_identifier,
! 					     NULL_TREE, 0),
! 			nelts);
  	}
! 
!       /* Build `(cookie = nelts, rval)' and use that as the complete
! 	 expression.  */
!       rval = cp_convert (build_pointer_type (true_type), rval);
!       rval = build_compound_expr
! 	(tree_cons (NULL_TREE, exp1,
! 		    build_tree_list (NULL_TREE, rval)));
      }
  
!   if (rval == error_mark_node)
!     return error_mark_node;
! 
!   /* Don't call any constructors or do any initialization.  */
!   if (init == void_type_node)
!     goto done;
! 
    if (TYPE_NEEDS_CONSTRUCTING (type) || init)
      {
        if (! TYPE_NEEDS_CONSTRUCTING (type)
--- 2382,2437 ----
  
       So check for a null exception spec on the op new we just called.  */
  
!   /* The ADDR_EXPR.  */
!   t = TREE_OPERAND (alloc_call, 0);
!   /* The function.  */
!   t = TREE_OPERAND (t, 0);
!   nothrow = TYPE_NOTHROW_P (TREE_TYPE (t));
    check_new = (flag_check_new || nothrow) && ! use_java_new;
  
!   alloc_expr = alloc_call;
  
!   if (use_cookie)
!     /* Adjust so we're pointing to the start of the object.  */
!     alloc_expr = build (PLUS_EXPR, TREE_TYPE (alloc_expr),
! 			alloc_expr, cookie_size);
!   alloc_expr = convert (build_pointer_type (type), alloc_expr);
! 
!   /* Now save the allocation expression so we only evaluate it once.  */
!   alloc_expr = get_target_expr (alloc_expr);
!   alloc_node = TREE_OPERAND (alloc_expr, 0);
! 
!   /* Now initialize the cookie.  */
!   if (use_cookie)
      {
!       tree cookie;
! 
        /* Store the number of bytes allocated so that we can know how
  	 many elements to destroy later.  */
        if (flag_new_abi)
  	{
  	  /* Under the new ABI, we use the last sizeof (size_t) bytes
  	     to store the number of elements.  */
! 	  cookie = build (MINUS_EXPR, build_pointer_type (sizetype),
! 			  alloc_node, size_in_bytes (sizetype));
! 	  cookie = build_indirect_ref (cookie, NULL_PTR);
  	}
        else
  	{
! 	  cookie = build (MINUS_EXPR, build_pointer_type (BI_header_type),
! 			  alloc_node, cookie_size);
! 	  cookie = build_indirect_ref (cookie, NULL_PTR);
! 	  cookie = build_component_ref (cookie, nelts_identifier,
! 					NULL_TREE, 0);
  	}
!       cookie_expr = build (MODIFY_EXPR, void_type_node, cookie, nelts);
!       TREE_SIDE_EFFECTS (cookie_expr) = 1;
      }
+   else
+     cookie_expr = NULL_TREE;
  
!   /* Now initialize the allocated object.  */
!   init_expr = NULL_TREE;
    if (TYPE_NEEDS_CONSTRUCTING (type) || init)
      {
        if (! TYPE_NEEDS_CONSTRUCTING (type)
*************** build_new_1 (exp)
*** 2465,2475 ****
  	  tree deref;
  	  tree deref_type;
  
! 	  /* At present RVAL is a temporary variable, created to hold
! 	     the value from the call to `operator new'.  We transform
! 	     it to (*RVAL = INIT, RVAL).  */
! 	  rval = save_expr (rval);
! 	  deref = build_indirect_ref (rval, NULL_PTR);
  
  	  /* Even for something like `new const int (10)' we must
  	     allow the expression to be non-const while we do the
--- 2442,2448 ----
  	  tree deref;
  	  tree deref_type;
  
! 	  deref = build_indirect_ref (alloc_node, NULL_PTR);
  
  	  /* Even for something like `new const int (10)' we must
  	     allow the expression to be non-const while we do the
*************** build_new_1 (exp)
*** 2483,2536 ****
  	  TREE_READONLY (deref) = 0;
  
  	  if (TREE_CHAIN (init) != NULL_TREE)
! 	    pedwarn ("initializer list being treated as compound expression");
  	  else if (TREE_CODE (init) == CONSTRUCTOR)
  	    {
! 	      pedwarn ("initializer list appears where operand should be used");
  	      init = TREE_OPERAND (init, 1);
  	    }
  	  init = build_compound_expr (init);
  
  	  init = convert_for_initialization (deref, type, init, LOOKUP_NORMAL,
  					     "new", NULL_TREE, 0);
! 	  rval = build (COMPOUND_EXPR, TREE_TYPE (rval),
! 			build_modify_expr (deref, NOP_EXPR, init),
! 			rval);
! 	  TREE_NO_UNUSED_WARNING (rval) = 1;
! 	  TREE_SIDE_EFFECTS (rval) = 1;
  	}
        else if (! has_array)
  	{
- 	  tree newrval;
  	  /* Constructors are never virtual. If it has an initialization, we
  	     need to complain if we aren't allowed to use the ctor that took
  	     that argument.  */
  	  int flags = LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_COMPLAIN;
  
! 	  rval = save_expr (rval);
! 	  newrval = rval;
! 
! 	  if (newrval && TREE_CODE (TREE_TYPE (newrval)) == POINTER_TYPE)
! 	    newrval = build_indirect_ref (newrval, NULL_PTR);
  
! 	  newrval = build_method_call (newrval, 
! 				       complete_ctor_identifier,
! 				       init, TYPE_BINFO (true_type), flags);
! 
! 	  if (newrval == NULL_TREE || newrval == error_mark_node)
! 	    return error_mark_node;
! 
! 	  newrval = build (COMPOUND_EXPR, TREE_TYPE (rval), newrval, rval);
! 	  rval = newrval;
! 	  TREE_HAS_CONSTRUCTOR (rval) = 1;
  	}
        else
! 	rval = (build_vec_init
! 		(NULL_TREE, 
! 		 save_expr (rval),
! 		 cp_build_binary_op (MINUS_EXPR, nelts, integer_one_node),
! 		 init,
! 		 /*from_array=*/0));
  
        /* If any part of the object initialization terminates by throwing an
  	 exception and a suitable deallocation function can be found, the
--- 2456,2503 ----
  	  TREE_READONLY (deref) = 0;
  
  	  if (TREE_CHAIN (init) != NULL_TREE)
! 	    pedwarn
! 	      ("initializer list being treated as compound expression");
  	  else if (TREE_CODE (init) == CONSTRUCTOR)
  	    {
! 	      pedwarn
! 		("initializer list appears where operand should be used");
  	      init = TREE_OPERAND (init, 1);
  	    }
  	  init = build_compound_expr (init);
  
  	  init = convert_for_initialization (deref, type, init, LOOKUP_NORMAL,
  					     "new", NULL_TREE, 0);
! 	  init_expr = build_modify_expr (deref, NOP_EXPR, init);
  	}
        else if (! has_array)
  	{
  	  /* Constructors are never virtual. If it has an initialization, we
  	     need to complain if we aren't allowed to use the ctor that took
  	     that argument.  */
  	  int flags = LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_COMPLAIN;
  
! 	  init_expr = build_indirect_ref (alloc_node, NULL_PTR);
  
! 	  init_expr = build_method_call (init_expr, 
! 					 complete_ctor_identifier,
! 					 init, TYPE_BINFO (true_type), flags);
  	}
        else
! 	{
! 	  if (init && pedantic)
! 	    cp_pedwarn ("initialization in array new");
! 
! 	  init_expr = convert (build_pointer_type (true_type), alloc_node);
! 	  init_expr = (build_vec_init
! 		       (NULL_TREE, init_expr,
! 			cp_build_binary_op (MINUS_EXPR, nelts,
! 					    integer_one_node),
! 			init, /*from_array=*/0));
! 	}
! 
!       if (init_expr == error_mark_node)
! 	return error_mark_node;
  
        /* If any part of the object initialization terminates by throwing an
  	 exception and a suitable deallocation function can be found, the
*************** build_new_1 (exp)
*** 2540,2549 ****
  	 unambiguous matching deallocation function can be found,
  	 propagating the exception does not cause the object's memory to be
  	 freed.  */
!       if (flag_exceptions && alloc_expr && ! use_java_new)
  	{
  	  enum tree_code dcode = has_array ? VEC_DELETE_EXPR : DELETE_EXPR;
! 	  tree cleanup, fn = NULL_TREE;
  	  int flags = (LOOKUP_NORMAL 
  		       | (globally_qualified_p * LOOKUP_GLOBAL));
  
--- 2507,2516 ----
  	 unambiguous matching deallocation function can be found,
  	 propagating the exception does not cause the object's memory to be
  	 freed.  */
!       if (flag_exceptions && ! use_java_new)
  	{
  	  enum tree_code dcode = has_array ? VEC_DELETE_EXPR : DELETE_EXPR;
! 	  tree cleanup;
  	  int flags = (LOOKUP_NORMAL 
  		       | (globally_qualified_p * LOOKUP_GLOBAL));
  
*************** build_new_1 (exp)
*** 2552,2572 ****
               functions that we use for finding allocation functions.  */
  	  flags |= LOOKUP_SPECULATIVELY;
  
! 	  /* We expect alloc_expr to look like a TARGET_EXPR around
! 	     a NOP_EXPR around the CALL_EXPR we want.  */
! 	  fn = TREE_OPERAND (alloc_expr, 1);
! 	  fn = TREE_OPERAND (fn, 0);
! 
! 	  cleanup = build_op_delete_call (dcode, alloc_node, size, flags, fn);
  
  	  /* Ack!  First we allocate the memory.  Then we set our sentry
  	     variable to true, and expand a cleanup that deletes the memory
! 	     if sentry is true.  Then we run the constructor and store the
! 	     returned pointer in buf.  Then we clear sentry and return buf.  */
  
  	  if (cleanup)
  	    {
! 	      tree end, sentry, begin, buf, t = TREE_TYPE (rval);
  
  	      begin = get_target_expr (boolean_true_node);
  	      sentry = TREE_OPERAND (begin, 0);
--- 2519,2549 ----
               functions that we use for finding allocation functions.  */
  	  flags |= LOOKUP_SPECULATIVELY;
  
! 	  cleanup = build_op_delete_call (dcode, alloc_node, size, flags,
! 					  alloc_call);
  
  	  /* Ack!  First we allocate the memory.  Then we set our sentry
  	     variable to true, and expand a cleanup that deletes the memory
! 	     if sentry is true.  Then we run the constructor, and finally
! 	     clear the sentry.
! 
! 	     It would be nice to be able to handle this without the sentry
! 	     variable, perhaps with a TRY_CATCH_EXPR, but this doesn't
! 	     work.  We allocate the space first, so if there are any
! 	     temporaries with cleanups in the constructor args we need this
! 	     EH region to extend until end of full-expression to preserve
! 	     nesting.
! 
! 	     If the backend had some mechanism so that we could force the
! 	     allocation to be expanded after all the other args to the
! 	     constructor, that would fix the nesting problem and we could
! 	     do away with this complexity.  But that would complicate other
! 	     things; in particular, it would make it difficult to bail out
! 	     if the allocation function returns null.  */
  
  	  if (cleanup)
  	    {
! 	      tree end, sentry, begin;
  
  	      begin = get_target_expr (boolean_true_node);
  	      sentry = TREE_OPERAND (begin, 0);
*************** build_new_1 (exp)
*** 2575,2620 ****
  		= build (COND_EXPR, void_type_node, sentry,
  			 cleanup, void_zero_node);
  
- 	      rval = get_target_expr (rval);
- 
  	      end = build (MODIFY_EXPR, TREE_TYPE (sentry),
  			   sentry, boolean_false_node);
  
! 	      buf = TREE_OPERAND (rval, 0);
! 
! 	      rval = build (COMPOUND_EXPR, t, begin,
! 			    build (COMPOUND_EXPR, t, rval,
! 				   build (COMPOUND_EXPR, t, end, buf)));
  	    }
  	}
      }
    else if (CP_TYPE_CONST_P (true_type))
      cp_error ("uninitialized const in `new' of `%#T'", true_type);
  
!  done:
  
!   if (alloc_expr && rval == alloc_node)
!     {
!       rval = TREE_OPERAND (alloc_expr, 1);
!       alloc_expr = NULL_TREE;
!     }
  
!   if (check_new && alloc_expr)
      {
-       /* Did we modify the storage?  */
        tree ifexp = cp_build_binary_op (NE_EXPR, alloc_node,
  				       integer_zero_node);
        rval = build_conditional_expr (ifexp, rval, alloc_node);
      }
  
!   if (alloc_expr)
!     rval = build (COMPOUND_EXPR, TREE_TYPE (rval), alloc_expr, rval);
! 
!   if (rval && TREE_TYPE (rval) != build_pointer_type (type))
!     {
!       /* The type of new int [3][3] is not int *, but int [3] * */
!       rval = build_c_cast (build_pointer_type (type), rval);
!     }
  
    return rval;
  }
--- 2552,2592 ----
  		= build (COND_EXPR, void_type_node, sentry,
  			 cleanup, void_zero_node);
  
  	      end = build (MODIFY_EXPR, TREE_TYPE (sentry),
  			   sentry, boolean_false_node);
  
! 	      init_expr
! 		= build (COMPOUND_EXPR, void_type_node, begin,
! 			 build (COMPOUND_EXPR, void_type_node, init_expr,
! 				end));
  	    }
  	}
      }
    else if (CP_TYPE_CONST_P (true_type))
      cp_error ("uninitialized const in `new' of `%#T'", true_type);
  
!   /* Now build up the return value in reverse order.  */
  
!   rval = alloc_node;
! 
!   if (init_expr)
!     rval = build (COMPOUND_EXPR, TREE_TYPE (rval), init_expr, rval);
!   if (cookie_expr)
!     rval = build (COMPOUND_EXPR, TREE_TYPE (rval), cookie_expr, rval);
  
!   if (rval == alloc_node)
!     /* If we didn't modify anything, strip the TARGET_EXPR and return the
!        (adjusted) call.  */
!     return TREE_OPERAND (alloc_expr, 1);
! 
!   if (check_new)
      {
        tree ifexp = cp_build_binary_op (NE_EXPR, alloc_node,
  				       integer_zero_node);
        rval = build_conditional_expr (ifexp, rval, alloc_node);
      }
  
!   rval = build (COMPOUND_EXPR, TREE_TYPE (rval), alloc_expr, rval);
  
    return rval;
  }

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