+2004-07-16 Richard Henderson <rth@redhat.com>
+
+ * tree-def (WITH_SIZE_EXPR): New.
+ * explow.c (expr_size, int_expr_size): Handle WITH_SIZE_EXPR.
+ * expr.c (expand_expr_real_1): Likewise.
+ * gimplify.c (maybe_with_size_expr): New.
+ (gimplify_arg, gimplify_modify_expr): Use it.
+ (gimplify_modify_expr_to_memcpy): Take size parameter.
+ (gimplify_modify_expr_to_memset): Likewise.
+ (gimplify_expr): Handle WITH_SIZE_EXPR.
+ * tree-alias-common.c (find_func_aliases): Likewise.
+ * tree-eh.c (tree_could_trap_p): Likewise.
+ (tree_could_throw_p): Likewise.
+ * tree-gimple.c (is_gimple_lvalue): Likewise.
+ (get_call_expr_in): Likewise.
+ * tree-inline.c (estimate_num_insns_1): Likewise.
+ (expand_calls_inline): Likewise.
+ * tree-nested.c (convert_call_expr): Likewise.
+ * tree-pretty-print.c (dump_generic_node): Likewise.
+ * tree-sra.c (sra_walk_expr): Likewise.
+ * tree-ssa-alias.c (add_pointed_to_expr): Likewise.
+ * tree-ssa-ccp.c (get_rhs, set_rhs): Likewise.
+ * tree-ssa-operands.c (get_expr_operands): Likewise.
+ * tree-tailcall.c (find_tail_calls): Likewise.
+
+ * calls.c (expand_call): Reset old_stack_allocated after
+ calling emit_stack_restore.
+
2004-07-16 Richard Henderson <rth@redhat.com>
* langhooks-def.h (LANG_HOOKS_TREE_INLINING_COPY_RES_DECL_FOR_INLINING,
emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
stack_pointer_delta = old_stack_pointer_delta;
pending_stack_adjust = old_pending_adj;
+ old_stack_allocated = stack_pointer_delta - pending_stack_adjust;
stack_arg_under_construction = old_stack_arg_under_construction;
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
stack_usage_map = initial_stack_usage_map;
rtx
expr_size (tree exp)
{
- tree size = SUBSTITUTE_PLACEHOLDER_IN_EXPR (lang_hooks.expr_size (exp), exp);
+ tree size;
+
+ if (TREE_CODE (exp) == WITH_SIZE_EXPR)
+ size = TREE_OPERAND (exp, 1);
+ else
+ size = SUBSTITUTE_PLACEHOLDER_IN_EXPR (lang_hooks.expr_size (exp), exp);
return expand_expr (size, NULL_RTX, TYPE_MODE (sizetype), 0);
}
HOST_WIDE_INT
int_expr_size (tree exp)
{
- tree t = lang_hooks.expr_size (exp);
-
- if (t == 0
- || TREE_CODE (t) != INTEGER_CST
- || TREE_OVERFLOW (t)
- || TREE_INT_CST_HIGH (t) != 0
- /* If the result would appear negative, it's too big to represent. */
- || (HOST_WIDE_INT) TREE_INT_CST_LOW (t) < 0)
+ tree size;
+
+ if (TREE_CODE (exp) == WITH_SIZE_EXPR)
+ size = TREE_OPERAND (exp, 1);
+ else
+ size = lang_hooks.expr_size (exp);
+
+ if (size == 0 || !host_integerp (size, 0))
return -1;
- return TREE_INT_CST_LOW (t);
+ return tree_low_cst (size, 0);
}
\f
/* Return a copy of X in which all memory references
expand_asm_expr (exp);
return const0_rtx;
+ case WITH_SIZE_EXPR:
+ /* WITH_SIZE_EXPR expands to its first argument. The caller should
+ have pulled out the size to use in whatever context it needed. */
+ return expand_expr_real (TREE_OPERAND (exp, 0), original_target, tmode,
+ modifier, alt_rtl);
+
default:
return lang_hooks.expand_expr (exp, original_target, tmode,
modifier, alt_rtl);
} elt_t;
/* Forward declarations. */
-static enum gimplify_status gimplify_modify_expr_rhs (tree *, tree *, tree *,
- tree *, tree *, bool);
static enum gimplify_status gimplify_compound_expr (tree *, tree *, bool);
}
}
+/* If *EXPR_P has a variable sized type, wrap it in a WITH_SIZE_EXPR. */
+
+static void
+maybe_with_size_expr (tree *expr_p)
+{
+ tree expr, type, size;
+
+ expr = *expr_p;
+ type = TREE_TYPE (expr);
+ if (type == error_mark_node)
+ return;
+
+ size = TYPE_SIZE_UNIT (type);
+ if (size && TREE_CODE (size) != INTEGER_CST)
+ {
+ size = unshare_expr (size);
+ size = SUBSTITUTE_PLACEHOLDER_IN_EXPR (size, expr);
+ *expr_p = build2 (WITH_SIZE_EXPR, type, expr, size);
+ }
+}
+
/* Subroutine of gimplify_call_expr: Gimplify a single argument. */
static enum gimplify_status
else
test = is_gimple_lvalue, fb = fb_either;
+ /* If this is a variable sized type, we must remember the size. */
+ maybe_with_size_expr (expr_p);
+
/* There is a sequence point before a function call. Side effects in
the argument list must occur before the actual call. So, when
gimplifying arguments, force gimplify_expr to use an internal
a call to __builtin_memcpy. */
static enum gimplify_status
-gimplify_modify_expr_to_memcpy (tree *expr_p, bool want_value)
+gimplify_modify_expr_to_memcpy (tree *expr_p, tree size, bool want_value)
{
tree args, t, to, to_ptr, from;
to = TREE_OPERAND (*expr_p, 0);
from = TREE_OPERAND (*expr_p, 1);
- t = TYPE_SIZE_UNIT (TREE_TYPE (from));
- t = unshare_expr (t);
- t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, to);
- t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, from);
- args = tree_cons (NULL, t, NULL);
+ args = tree_cons (NULL, size, NULL);
t = build_fold_addr_expr (from);
args = tree_cons (NULL, t, args);
a CONSTRUCTOR with an empty element list. */
static enum gimplify_status
-gimplify_modify_expr_to_memset (tree *expr_p, bool want_value)
+gimplify_modify_expr_to_memset (tree *expr_p, tree size, bool want_value)
{
tree args, t, to, to_ptr;
to = TREE_OPERAND (*expr_p, 0);
- t = TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (*expr_p, 1)));
- t = unshare_expr (t);
- t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, to);
- args = tree_cons (NULL, t, NULL);
+ args = tree_cons (NULL, size, NULL);
args = tree_cons (NULL, integer_zero_node, args);
if (ret != GS_UNHANDLED)
return ret;
- /* If the value being copied is of variable width, expose the length
- if the copy by converting the whole thing to a memcpy/memset.
- Note that we need to do this before gimplifying any of the operands
- so that we can resolve any PLACEHOLDER_EXPRs in the size.
- Also note that the RTL expander uses the size of the expression to
- be copied, not of the destination, so that is what we must here.
- The types on both sides of the MODIFY_EXPR should be the same,
- but they aren't always and there are problems with class-wide types
- in Ada where it's hard to make it "correct". */
- if (TREE_CODE (TREE_TYPE (*from_p)) != ERROR_MARK
- && TYPE_SIZE_UNIT (TREE_TYPE (*from_p))
- && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (*from_p))) != INTEGER_CST)
- {
- if (TREE_CODE (*from_p) == CONSTRUCTOR)
- return gimplify_modify_expr_to_memset (expr_p, want_value);
- else
- return gimplify_modify_expr_to_memcpy (expr_p, want_value);
- }
+ /* If the value being copied is of variable width, compute the length
+ of the copy into a WITH_SIZE_EXPR. Note that we need to do this
+ before gimplifying any of the operands so that we can resolve any
+ PLACEHOLDER_EXPRs in the size. Also note that the RTL expander uses
+ the size of the expression to be copied, not of the destination, so
+ that is what we must here. */
+ maybe_with_size_expr (from_p);
ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
if (ret == GS_ERROR)
if (ret != GS_UNHANDLED)
return ret;
+ /* If we've got a variable sized assignment between two lvalues (i.e. does
+ not involve a call), then we can make things a bit more straightforward
+ by converting the assignment to memcpy or memset. */
+ if (TREE_CODE (*from_p) == WITH_SIZE_EXPR)
+ {
+ tree from = TREE_OPERAND (*from_p, 0);
+ tree size = TREE_OPERAND (*from_p, 1);
+
+ if (TREE_CODE (from) == CONSTRUCTOR)
+ return gimplify_modify_expr_to_memset (expr_p, size, want_value);
+ if (is_gimple_addr_expr_arg (from))
+ {
+ *from_p = from;
+ return gimplify_modify_expr_to_memcpy (expr_p, size, want_value);
+ }
+ }
+
/* If the destination is already simple, nothing else needed. */
if (is_gimple_tmp_var (*to_p) || !want_value)
ret = GS_ALL_DONE;
ret = gimplify_statement_list (expr_p);
break;
+ case WITH_SIZE_EXPR:
+ {
+ enum gimplify_status r0, r1;
+ r0 = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
+ post_p == &internal_post ? NULL : post_p,
+ gimple_test_f, fallback);
+ r1 = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
+ is_gimple_val, fb_rvalue);
+ }
+ break;
+
case VAR_DECL:
/* ??? If this is a local variable, and it has not been seen in any
outer BIND_EXPR, then it's probably the result of a duplicate
+2004-07-16 Richard Henderson <rth@redhat.com>
+
+ * gcc.c-torture/compile/20020210-1.c: Remove XFAIL.
+
2004-07-16 Tobias Schlueter <tobias.schlueter@physik.uni-muenchen.de>
* lib/fortran-torture.exp (fortran-torture): Don't test compile
/* PR c/5615 */
-/* { dg-xfail-if "regression/16417" { "*-*-*" } { "-O1" "-O2" "-O3 -fomit-frame-pointer" "-O3 -g" "-Os" } { "" } } */
void f(int a, struct {int b[a];} c) {}
{
op0 = TREE_OPERAND (stp, 0);
op1 = TREE_OPERAND (stp, 1);
+ if (TREE_CODE (op1) == WITH_SIZE_EXPR)
+ op1 = TREE_OPERAND (op1, 0);
}
+
/* lhsAV should always have an alias variable */
lhsAV = get_alias_var (op0);
if (!lhsAV)
honor_trapv = true;
}
+ restart:
switch (code)
{
case COMPONENT_REF:
case REALPART_EXPR:
case IMAGPART_EXPR:
case BIT_FIELD_REF:
- t = TREE_OPERAND (expr, 0);
- return tree_could_trap_p (t);
+ case WITH_SIZE_EXPR:
+ expr = TREE_OPERAND (expr, 0);
+ code = TREE_CODE (expr);
+ goto restart;
case ARRAY_RANGE_REF:
/* Let us be conservative here for now. We might be checking bounds of
t = TREE_OPERAND (t, 1);
}
+ if (TREE_CODE (t) == WITH_SIZE_EXPR)
+ t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == CALL_EXPR)
return (call_expr_flags (t) & ECF_NOTHROW) == 0;
if (flag_non_call_exceptions)
addr-expr-arg: ID
| compref
+ with-size-arg: addr-expr-arg
+ | indirectref
+ | call-stmt
+
+ indirectref : INDIRECT_REF
+ op0 -> val
+
lhs : addr-expr-arg
- | '*' val
| bitfieldref
+ | indirectref
+ | WITH_SIZE_EXPR
+ op0 -> with-size-arg
+ op1 -> val
min-lval : ID
- | '*' val
+ | indirectref
bitfieldref : BIT_FIELD_REF
op0 -> inner-compref
op0 -> inner-compref
condition : val
- | val RELOP val
+ | RELOP
+ op0 -> val
+ op1 -> val
val : ID
| CONST
rhs : lhs
| CONST
- | '&' addr-expr-arg
- | call_expr
- | UNOP val
- | val BINOP val
- | val RELOP val
+ | call-stmt
+ | ADDR_EXPR
+ op0 -> addr-expr-arg
+ | UNOP
+ op0 -> val
+ | BINOP
+ op0 -> val
+ op1 -> val
+ | RELOP
+ op0 -> val
+ op1 -> val
*/
static inline bool is_gimple_id (tree);
{
return (is_gimple_addr_expr_arg (t)
|| TREE_CODE (t) == INDIRECT_REF
+ || TREE_CODE (t) == WITH_SIZE_EXPR
/* These are complex lvalues, but don't have addresses, so they
go here. */
|| TREE_CODE (t) == BIT_FIELD_REF);
{
if (TREE_CODE (t) == MODIFY_EXPR)
t = TREE_OPERAND (t, 1);
+ if (TREE_CODE (t) == WITH_SIZE_EXPR)
+ t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == CALL_EXPR)
return t;
return NULL_TREE;
case EXIT_EXPR:
case LOOP_EXPR:
case PHI_NODE:
+ case WITH_SIZE_EXPR:
break;
/* We don't account constants for now. Assume that the cost is amortized
case MODIFY_EXPR:
stmt_p = &TREE_OPERAND (stmt, 1);
stmt = *stmt_p;
+ if (TREE_CODE (stmt) == WITH_SIZE_EXPR)
+ {
+ stmt_p = &TREE_OPERAND (stmt, 0);
+ stmt = *stmt_p;
+ }
if (TREE_CODE (stmt) != CALL_EXPR)
break;
case RETURN_EXPR:
case MODIFY_EXPR:
- /* Only return and modify may contain calls. */
+ case WITH_SIZE_EXPR:
+ /* Only return modify and with_size_expr may contain calls. */
*walk_subtrees = 1;
break;
pp_decimal_int (buffer, SSA_NAME_VERSION (node));
break;
+ case WITH_SIZE_EXPR:
+ pp_string (buffer, "WITH_SIZE_EXPR <");
+ dump_generic_node (buffer, TREE_OPERAND (node, 0), spc, flags, false);
+ pp_string (buffer, ", ");
+ dump_generic_node (buffer, TREE_OPERAND (node, 1), spc, flags, false);
+ pp_string (buffer, ">");
+ break;
+
case VALUE_HANDLE:
pp_printf (buffer, "VH.%d", VALUE_HANDLE_ID (node));
break;
type other than the one we've scalarized. */
goto use_all;
+ case WITH_SIZE_EXPR:
+ /* This is a transparent wrapper. The entire inner expression really
+ is being used. */
+ goto use_all;
+
use_all:
expr_p = &TREE_OPERAND (inner, 0);
inner = expr = *expr_p;
{
struct ptr_info_def *pi;
+ if (TREE_CODE (value) == WITH_SIZE_EXPR)
+ value = TREE_OPERAND (value, 0);
+
#if defined ENABLE_CHECKING
/* Pointer variables should have been handled by merge_pointed_to_info. */
if (TREE_CODE (value) == SSA_NAME
{
case RETURN_EXPR:
stmt = TREE_OPERAND (stmt, 0);
- if (stmt)
- return get_rhs (stmt);
- else
- return NULL;
+ if (!stmt || TREE_CODE (stmt) != MODIFY_EXPR)
+ return stmt;
+ /* FALLTHRU */
case MODIFY_EXPR:
- return TREE_OPERAND (stmt, 1);
+ stmt = TREE_OPERAND (stmt, 1);
+ if (TREE_CODE (stmt) == WITH_SIZE_EXPR)
+ return TREE_OPERAND (stmt, 0);
+ else
+ return stmt;
case COND_EXPR:
return COND_EXPR_COND (stmt);
/* FALLTHRU */
case MODIFY_EXPR:
+ op = TREE_OPERAND (stmt, 1);
+ if (TREE_CODE (op) == WITH_SIZE_EXPR)
+ stmt = op;
TREE_OPERAND (stmt, 1) = expr;
break;
get_expr_operands (stmt, &TREE_OPERAND (expr, 2), opf_none, prev_vops);
return;
+ case WITH_SIZE_EXPR:
+ /* WITH_SIZE_EXPR is a pass-through reference to it's first argument,
+ and an rvalue reference to its second argument. */
+ get_expr_operands (stmt, &TREE_OPERAND (expr, 1), opf_none, prev_vops);
+ get_expr_operands (stmt, &TREE_OPERAND (expr, 0), flags, prev_vops);
+ return;
+
case CALL_EXPR:
get_call_expr_operands (stmt, expr, prev_vops);
return;
case MODIFY_EXPR:
- get_expr_operands (stmt, &TREE_OPERAND (expr, 1), opf_none, prev_vops);
+ {
+ int subflags;
+ tree op;
+
+ get_expr_operands (stmt, &TREE_OPERAND (expr, 1), opf_none, prev_vops);
+
+ op = TREE_OPERAND (expr, 0);
+ if (TREE_CODE (op) == WITH_SIZE_EXPR)
+ op = TREE_OPERAND (expr, 0);
+ if (TREE_CODE (op) == ARRAY_REF
+ || TREE_CODE (op) == COMPONENT_REF
+ || TREE_CODE (op) == REALPART_EXPR
+ || TREE_CODE (op) == IMAGPART_EXPR)
+ subflags = opf_is_def;
+ else
+ subflags = opf_is_def | opf_kill_def;
- if (TREE_CODE (TREE_OPERAND (expr, 0)) == ARRAY_REF
- || TREE_CODE (TREE_OPERAND (expr, 0)) == COMPONENT_REF
- || TREE_CODE (TREE_OPERAND (expr, 0)) == REALPART_EXPR
- || TREE_CODE (TREE_OPERAND (expr, 0)) == IMAGPART_EXPR)
- get_expr_operands (stmt, &TREE_OPERAND (expr, 0), opf_is_def,
- prev_vops);
- else
- get_expr_operands (stmt, &TREE_OPERAND (expr, 0),
- opf_is_def | opf_kill_def, prev_vops);
- return;
+ get_expr_operands (stmt, &TREE_OPERAND (expr, 0), subflags, prev_vops);
+ return;
+ }
case VA_ARG_EXPR:
/* Mark VA_ARG_EXPR nodes as making volatile references. FIXME,
{
ass_var = TREE_OPERAND (stmt, 0);
call = TREE_OPERAND (stmt, 1);
+ if (TREE_CODE (call) == WITH_SIZE_EXPR)
+ call = TREE_OPERAND (call, 0);
}
else
{
baseclass of itself or another class. */
DEFTREECODE (TREE_BINFO, "tree_binfo", 'x', 0)
+/* Records the size for an expression of variable size type. This is
+ for use in contexts in which we are accessing the entire object,
+ such as for a function call, or block copy.
+ Operand 0 is the real expression.
+ Operand 1 is the size of the type in the expression. */
+DEFTREECODE (WITH_SIZE_EXPR, "with_size_expr", 'e', 2)
+
/*
Local variables:
mode:c