aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-ccp.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-ccp.c')
-rw-r--r--gcc/tree-ssa-ccp.c345
1 files changed, 130 insertions, 215 deletions
diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c
index fc0c4ca..0bbc0f0 100644
--- a/gcc/tree-ssa-ccp.c
+++ b/gcc/tree-ssa-ccp.c
@@ -1616,7 +1616,7 @@ struct gimple_opt_pass pass_ccp =
};
-/* A subroutine of fold_stmt_r. Attempts to fold *(A+O) to A[X].
+/* A subroutine of fold_stmt. Attempts to fold *(A+O) to A[X].
BASE is an array type. OFFSET is a byte displacement. ORIG_TYPE
is the desired result type. */
@@ -2001,7 +2001,7 @@ maybe_fold_offset_to_address (tree addr, tree offset, tree orig_type)
return NULL_TREE;
}
-/* A subroutine of fold_stmt_r. Attempt to simplify *(BASE+OFFSET).
+/* A subroutine of fold_stmt. Attempt to simplify *(BASE+OFFSET).
Return the simplified expression, or NULL if nothing could be done. */
static tree
@@ -2220,180 +2220,64 @@ maybe_fold_stmt_addition (tree res_type, tree op0, tree op1)
return t;
}
-/* For passing state through walk_tree into fold_stmt_r and its
- children. */
-
-struct fold_stmt_r_data
-{
- gimple stmt;
- bool *changed_p;
- bool *inside_addr_expr_p;
-};
-
-/* Subroutine of fold_stmt called via walk_tree. We perform several
- simplifications of EXPR_P, mostly having to do with pointer arithmetic. */
+/* Subroutine of fold_stmt. We perform several simplifications of the
+ memory reference tree EXPR and make sure to re-gimplify them properly
+ after propagation of constant addresses. IS_LHS is true if the
+ reference is supposed to be an lvalue. */
static tree
-fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data)
+maybe_fold_reference (tree expr, bool is_lhs)
{
- struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
- struct fold_stmt_r_data *fold_stmt_r_data;
- bool *inside_addr_expr_p;
- bool *changed_p;
- tree expr = *expr_p, t;
- bool volatile_p = TREE_THIS_VOLATILE (expr);
+ tree *t = &expr;
- fold_stmt_r_data = (struct fold_stmt_r_data *) wi->info;
- inside_addr_expr_p = fold_stmt_r_data->inside_addr_expr_p;
- changed_p = fold_stmt_r_data->changed_p;
+ if (TREE_CODE (expr) == ARRAY_REF
+ && !is_lhs)
+ {
+ tree tem = fold_read_from_constant_string (expr);
+ if (tem)
+ return tem;
+ }
- /* ??? It'd be nice if walk_tree had a pre-order option. */
- switch (TREE_CODE (expr))
+ /* ??? We might want to open-code the relevant remaining cases
+ to avoid using the generic fold. */
+ if (handled_component_p (*t)
+ && CONSTANT_CLASS_P (TREE_OPERAND (*t, 0)))
{
- case INDIRECT_REF:
- t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
- if (t)
- return t;
- *walk_subtrees = 0;
+ tree tem = fold (*t);
+ if (tem != *t)
+ return tem;
+ }
+
+ while (handled_component_p (*t))
+ t = &TREE_OPERAND (*t, 0);
- t = maybe_fold_stmt_indirect (expr, TREE_OPERAND (expr, 0),
- integer_zero_node);
+ if (TREE_CODE (*t) == INDIRECT_REF)
+ {
+ tree tem = maybe_fold_stmt_indirect (*t, TREE_OPERAND (*t, 0),
+ integer_zero_node);
/* Avoid folding *"abc" = 5 into 'a' = 5. */
- if (wi->is_lhs && t && TREE_CODE (t) == INTEGER_CST)
- t = NULL_TREE;
- if (!t
- && TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR)
+ if (is_lhs && tem && CONSTANT_CLASS_P (tem))
+ tem = NULL_TREE;
+ if (!tem
+ && TREE_CODE (TREE_OPERAND (*t, 0)) == ADDR_EXPR)
/* If we had a good reason for propagating the address here,
make sure we end up with valid gimple. See PR34989. */
- t = TREE_OPERAND (TREE_OPERAND (expr, 0), 0);
- break;
-
- case NOP_EXPR:
- t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
- if (t)
- return t;
- *walk_subtrees = 0;
-
- if (POINTER_TYPE_P (TREE_TYPE (expr))
- && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (expr)))
- && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0)))
- && (t = maybe_fold_offset_to_address (TREE_OPERAND (expr, 0),
- integer_zero_node,
- TREE_TYPE (TREE_TYPE (expr)))))
- return t;
- break;
-
- /* ??? Could handle more ARRAY_REFs here, as a variant of INDIRECT_REF.
- We'd only want to bother decomposing an existing ARRAY_REF if
- the base array is found to have another offset contained within.
- Otherwise we'd be wasting time. */
- case ARRAY_REF:
- /* If we are not processing expressions found within an
- ADDR_EXPR, then we can fold constant array references.
- Don't fold on LHS either, to avoid folding "abc"[0] = 5
- into 'a' = 5. */
- if (!*inside_addr_expr_p && !wi->is_lhs)
- t = fold_read_from_constant_string (expr);
- else
- t = NULL;
- break;
-
- case ADDR_EXPR:
- *inside_addr_expr_p = true;
- t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
- *inside_addr_expr_p = false;
- if (t)
- return t;
- *walk_subtrees = 0;
-
- /* Make sure the value is properly considered constant, and so gets
- propagated as expected. */
- if (*changed_p)
- recompute_tree_invariant_for_addr_expr (expr);
- return NULL_TREE;
-
- case COMPONENT_REF:
- t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
- if (t)
- return t;
- *walk_subtrees = 0;
-
- /* Make sure the FIELD_DECL is actually a field in the type on the lhs.
- We've already checked that the records are compatible, so we should
- come up with a set of compatible fields. */
- {
- tree expr_record = TREE_TYPE (TREE_OPERAND (expr, 0));
- tree expr_field = TREE_OPERAND (expr, 1);
-
- if (DECL_FIELD_CONTEXT (expr_field) != TYPE_MAIN_VARIANT (expr_record))
- {
- expr_field = find_compatible_field (expr_record, expr_field);
- TREE_OPERAND (expr, 1) = expr_field;
- }
- }
- break;
-
- case TARGET_MEM_REF:
- t = maybe_fold_tmr (expr);
- break;
-
- case POINTER_PLUS_EXPR:
- t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
- if (t)
- return t;
- t = walk_tree (&TREE_OPERAND (expr, 1), fold_stmt_r, data, NULL);
- if (t)
- return t;
- *walk_subtrees = 0;
-
- t = maybe_fold_stmt_addition (TREE_TYPE (expr),
- TREE_OPERAND (expr, 0),
- TREE_OPERAND (expr, 1));
- break;
-
- case COND_EXPR:
- if (COMPARISON_CLASS_P (TREE_OPERAND (expr, 0)))
- {
- tree op0 = TREE_OPERAND (expr, 0);
- tree tem;
- bool set;
-
- fold_defer_overflow_warnings ();
- tem = fold_binary (TREE_CODE (op0), TREE_TYPE (op0),
- TREE_OPERAND (op0, 0),
- TREE_OPERAND (op0, 1));
- /* This is actually a conditional expression, not a GIMPLE
- conditional statement, however, the valid_gimple_rhs_p
- test still applies. */
- set = tem && is_gimple_condexpr (tem) && valid_gimple_rhs_p (tem);
- fold_undefer_overflow_warnings (set, fold_stmt_r_data->stmt, 0);
- if (set)
- {
- COND_EXPR_COND (expr) = tem;
- t = expr;
- break;
- }
- }
- return NULL_TREE;
-
- default:
- return NULL_TREE;
- }
+ tem = TREE_OPERAND (TREE_OPERAND (*t, 0), 0);
- if (t)
- {
- /* Preserve volatileness of the original expression.
- We can end up with a plain decl here which is shared
- and we shouldn't mess with its flags. */
- if (!SSA_VAR_P (t))
- TREE_THIS_VOLATILE (t) = volatile_p;
- *expr_p = t;
- *changed_p = true;
+ if (tem)
+ {
+ *t = tem;
+ tem = maybe_fold_reference (expr, is_lhs);
+ if (tem)
+ return tem;
+ return expr;
+ }
}
return NULL_TREE;
}
+
/* Return the string length, maximum string length or maximum value of
ARG in LENGTH.
If ARG is an SSA name variable, follow its use-def chains. If LENGTH
@@ -2713,23 +2597,61 @@ fold_gimple_assign (gimple_stmt_iterator *si)
gimple stmt = gsi_stmt (*si);
enum tree_code subcode = gimple_assign_rhs_code (stmt);
- tree result = NULL;
+ tree result = NULL_TREE;
switch (get_gimple_rhs_class (subcode))
{
case GIMPLE_SINGLE_RHS:
{
tree rhs = gimple_assign_rhs1 (stmt);
-
+
/* Try to fold a conditional expression. */
if (TREE_CODE (rhs) == COND_EXPR)
{
- tree temp = fold (COND_EXPR_COND (rhs));
- if (temp != COND_EXPR_COND (rhs))
- result = fold_build3 (COND_EXPR, TREE_TYPE (rhs), temp,
- COND_EXPR_THEN (rhs), COND_EXPR_ELSE (rhs));
+ tree op0 = COND_EXPR_COND (rhs);
+ tree tem;
+ bool set = false;
+
+ if (COMPARISON_CLASS_P (op0))
+ {
+ fold_defer_overflow_warnings ();
+ tem = fold_binary (TREE_CODE (op0), TREE_TYPE (op0),
+ TREE_OPERAND (op0, 0),
+ TREE_OPERAND (op0, 1));
+ /* This is actually a conditional expression, not a GIMPLE
+ conditional statement, however, the valid_gimple_rhs_p
+ test still applies. */
+ set = (tem && is_gimple_condexpr (tem)
+ && valid_gimple_rhs_p (tem));
+ fold_undefer_overflow_warnings (set, stmt, 0);
+ }
+ else if (is_gimple_min_invariant (op0))
+ {
+ tem = op0;
+ set = true;
+ }
+ else
+ return NULL_TREE;
+
+ if (set)
+ result = fold_build3 (COND_EXPR, TREE_TYPE (rhs), tem,
+ COND_EXPR_THEN (rhs), COND_EXPR_ELSE (rhs));
}
+ else if (TREE_CODE (rhs) == TARGET_MEM_REF)
+ return maybe_fold_tmr (rhs);
+
+ else if (REFERENCE_CLASS_P (rhs))
+ return maybe_fold_reference (rhs, false);
+
+ else if (TREE_CODE (rhs) == ADDR_EXPR)
+ {
+ tree tem = maybe_fold_reference (TREE_OPERAND (rhs, 0), true);
+ if (tem)
+ result = fold_convert (TREE_TYPE (rhs),
+ build_fold_addr_expr (tem));
+ }
+
/* If we couldn't fold the RHS, hand over to the generic
fold routines. */
if (result == NULL_TREE)
@@ -2742,11 +2664,8 @@ fold_gimple_assign (gimple_stmt_iterator *si)
if (result != rhs && valid_gimple_rhs_p (result))
return result;
- else
- /* It is possible that fold_stmt_r simplified the RHS.
- Make sure that the subcode of this statement still
- reflects the principal operator of the rhs operand. */
- return rhs;
+
+ return NULL_TREE;
}
break;
@@ -2919,32 +2838,17 @@ fold_gimple_call (gimple_stmt_iterator *gsi)
/* Fold the statement pointed to by GSI. In some cases, this function may
replace the whole statement with a new one. Returns true iff folding
- makes any changes. */
+ makes any changes.
+ The statement pointed to by GSI should be in valid gimple form but may
+ be in unfolded state as resulting from for example constant propagation
+ which can produce *&x = 0. */
bool
fold_stmt (gimple_stmt_iterator *gsi)
{
- tree res;
- struct fold_stmt_r_data fold_stmt_r_data;
- struct walk_stmt_info wi;
-
bool changed = false;
- bool inside_addr_expr = false;
-
gimple stmt = gsi_stmt (*gsi);
- fold_stmt_r_data.stmt = stmt;
- fold_stmt_r_data.changed_p = &changed;
- fold_stmt_r_data.inside_addr_expr_p = &inside_addr_expr;
-
- memset (&wi, 0, sizeof (wi));
- wi.info = &fold_stmt_r_data;
-
- /* Fold the individual operands.
- For example, fold instances of *&VAR into VAR, etc. */
- res = walk_gimple_op (stmt, fold_stmt_r, &wi);
- gcc_assert (!res);
-
/* Fold the main computation performed by the statement. */
switch (gimple_code (stmt))
{
@@ -2956,7 +2860,6 @@ fold_stmt (gimple_stmt_iterator *gsi)
gimple_assign_set_rhs_from_tree (gsi, new_rhs);
changed = true;
}
- stmt = gsi_stmt (*gsi);
break;
}
case GIMPLE_COND:
@@ -2972,42 +2875,39 @@ fold_stmt (gimple_stmt_iterator *gsi)
break;
}
+ stmt = gsi_stmt (*gsi);
+
+ /* Fold *& on the lhs. */
+ if (gimple_has_lhs (stmt))
+ {
+ tree lhs = gimple_get_lhs (stmt);
+ if (lhs && REFERENCE_CLASS_P (lhs))
+ {
+ tree new_lhs = maybe_fold_reference (lhs, true);
+ if (new_lhs)
+ {
+ gimple_set_lhs (stmt, new_lhs);
+ changed = true;
+ }
+ }
+ }
+
return changed;
}
/* Perform the minimal folding on statement STMT. Only operations like
*&x created by constant propagation are handled. The statement cannot
be replaced with a new one. Return true if the statement was
- changed, false otherwise. */
+ changed, false otherwise.
+ The statement STMT should be in valid gimple form but may
+ be in unfolded state as resulting from for example constant propagation
+ which can produce *&x = 0. */
bool
fold_stmt_inplace (gimple stmt)
{
- tree res;
- struct fold_stmt_r_data fold_stmt_r_data;
- struct walk_stmt_info wi;
gimple_stmt_iterator si;
-
bool changed = false;
- bool inside_addr_expr = false;
-
- fold_stmt_r_data.stmt = stmt;
- fold_stmt_r_data.changed_p = &changed;
- fold_stmt_r_data.inside_addr_expr_p = &inside_addr_expr;
-
- memset (&wi, 0, sizeof (wi));
- wi.info = &fold_stmt_r_data;
-
- /* Fold the individual operands.
- For example, fold instances of *&VAR into VAR, etc.
-
- It appears that, at one time, maybe_fold_stmt_indirect
- would cause the walk to return non-null in order to
- signal that the entire statement should be replaced with
- a call to _builtin_trap. This functionality is currently
- disabled, as noted in a FIXME, and cannot be supported here. */
- res = walk_gimple_op (stmt, fold_stmt_r, &wi);
- gcc_assert (!res);
/* Fold the main computation performed by the statement. */
switch (gimple_code (stmt))
@@ -3036,6 +2936,21 @@ fold_stmt_inplace (gimple stmt)
break;
}
+ /* Fold *& on the lhs. */
+ if (gimple_has_lhs (stmt))
+ {
+ tree lhs = gimple_get_lhs (stmt);
+ if (lhs && REFERENCE_CLASS_P (lhs))
+ {
+ tree new_lhs = maybe_fold_reference (lhs, true);
+ if (new_lhs)
+ {
+ gimple_set_lhs (stmt, new_lhs);
+ changed = true;
+ }
+ }
+ }
+
return changed;
}