aboutsummaryrefslogtreecommitdiff
path: root/gcc/gimplify.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/gimplify.c')
-rw-r--r--gcc/gimplify.c94
1 files changed, 63 insertions, 31 deletions
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 642cff9..6cf5d48 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -72,8 +72,6 @@ typedef struct gimple_temp_hash_elt
} 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);
@@ -1788,6 +1786,27 @@ gimplify_self_mod_expr (tree *expr_p, tree *pre_p, tree *post_p,
}
}
+/* 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
@@ -1806,6 +1825,9 @@ gimplify_arg (tree *expr_p, tree *pre_p)
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
@@ -2316,18 +2338,14 @@ gimplify_cond_expr (tree *expr_p, tree *pre_p, tree target)
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);
@@ -2352,16 +2370,13 @@ gimplify_modify_expr_to_memcpy (tree *expr_p, bool want_value)
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);
@@ -2771,24 +2786,13 @@ gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value)
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)
@@ -2805,6 +2809,23 @@ gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value)
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;
@@ -3784,6 +3805,17 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p,
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