This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH: build_new_1 overhaul
- To: gcc-patches at gcc dot gnu dot org
- Subject: C++ PATCH: build_new_1 overhaul
- From: Jason Merrill <jason at redhat dot com>
- Date: 15 Dec 2000 16:33:08 +0000
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;
}