static prop_value_t *const_val;
static void canonicalize_float_value (prop_value_t *);
+static bool ccp_fold_stmt (gimple_stmt_iterator *);
/* Dump constant propagation value VAL to file OUTF prefixed by PREFIX. */
for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
{
gimple stmt = gsi_stmt (i);
- bool is_varying = surely_varying_stmt_p (stmt);
+ bool is_varying;
+
+ /* If the statement is a control insn, then we do not
+ want to avoid simulating the statement once. Failure
+ to do so means that those edges will never get added. */
+ if (stmt_ends_bb_p (stmt))
+ is_varying = false;
+ else
+ is_varying = surely_varying_stmt_p (stmt);
if (is_varying)
{
do_dbg_cnt ();
/* Perform substitutions based on the known constant values. */
- something_changed = substitute_and_fold (const_val, false);
+ something_changed = substitute_and_fold (const_val, ccp_fold_stmt);
free (const_val);
const_val = NULL;
&& TREE_CODE (op0) == ADDR_EXPR
&& TREE_CODE (op1) == INTEGER_CST)
{
- tree lhs = gimple_assign_lhs (stmt);
tree tem = maybe_fold_offset_to_address
- (loc, op0, op1, TREE_TYPE (lhs));
+ (loc, op0, op1, TREE_TYPE (op0));
if (tem != NULL_TREE)
return tem;
}
else if (code == GIMPLE_SWITCH)
simplified = gimple_switch_index (stmt);
else
- /* These cannot satisfy is_gimple_min_invariant without folding. */
- gcc_assert (code == GIMPLE_CALL || code == GIMPLE_COND);
+ /* These cannot satisfy is_gimple_min_invariant without folding. */
+ gcc_assert (code == GIMPLE_CALL || code == GIMPLE_COND);
}
is_constant = simplified && is_gimple_min_invariant (simplified);
return val;
}
+/* Fold the stmt at *GSI with CCP specific information that propagating
+ and regular folding does not catch. */
+
+static bool
+ccp_fold_stmt (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ prop_value_t val;
+
+ if (gimple_code (stmt) != GIMPLE_COND)
+ return false;
+
+ /* Statement evaluation will handle type mismatches in constants
+ more gracefully than the final propagation. This allows us to
+ fold more conditionals here. */
+ val = evaluate_stmt (stmt);
+ if (val.lattice_val != CONSTANT
+ || TREE_CODE (val.value) != INTEGER_CST)
+ return false;
+
+ if (integer_zerop (val.value))
+ gimple_cond_make_false (stmt);
+ else
+ gimple_cond_make_true (stmt);
+
+ return true;
+}
+
/* Visit the assignment statement STMT. Set the value of its LHS to the
value computed by the RHS and store LHS in *OUTPUT_P. If STMT
creates virtual definitions, set the value of each new name to that
static tree
maybe_fold_offset_to_component_ref (location_t loc, tree record_type,
- tree base, tree offset,
- tree orig_type, bool base_is_ptr)
+ tree base, tree offset, tree orig_type)
{
tree f, t, field_type, tail_array_field, field_offset;
tree ret;
if (cmp == 0
&& useless_type_conversion_p (orig_type, field_type))
{
- if (base_is_ptr)
- base = build1 (INDIRECT_REF, record_type, base);
t = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
return t;
}
/* If we matched, then set offset to the displacement into
this field. */
- if (base_is_ptr)
- new_base = build1 (INDIRECT_REF, record_type, base);
- else
- new_base = base;
- protected_set_expr_location (new_base, loc);
- new_base = build3 (COMPONENT_REF, field_type, new_base, f, NULL_TREE);
- protected_set_expr_location (new_base, loc);
+ new_base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
+ SET_EXPR_LOCATION (new_base, loc);
/* Recurse to possibly find the match. */
ret = maybe_fold_offset_to_array_ref (loc, new_base, t, orig_type,
if (ret)
return ret;
ret = maybe_fold_offset_to_component_ref (loc, field_type, new_base, t,
- orig_type, false);
+ orig_type);
if (ret)
return ret;
}
/* If we get here, we've got an aggregate field, and a possibly
nonzero offset into them. Recurse and hope for a valid match. */
- if (base_is_ptr)
- {
- base = build1 (INDIRECT_REF, record_type, base);
- SET_EXPR_LOCATION (base, loc);
- }
base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
SET_EXPR_LOCATION (base, loc);
if (t)
return t;
return maybe_fold_offset_to_component_ref (loc, field_type, base, offset,
- orig_type, false);
+ orig_type);
}
/* Attempt to express (ORIG_TYPE)BASE+OFFSET as BASE->field_of_orig_type
{
tree ret;
tree type;
- bool base_is_ptr = true;
STRIP_NOPS (base);
- if (TREE_CODE (base) == ADDR_EXPR)
- {
- base_is_ptr = false;
-
- base = TREE_OPERAND (base, 0);
+ if (TREE_CODE (base) != ADDR_EXPR)
+ return NULL_TREE;
- /* Handle case where existing COMPONENT_REF pick e.g. wrong field of union,
- so it needs to be removed and new COMPONENT_REF constructed.
- The wrong COMPONENT_REF are often constructed by folding the
- (type *)&object within the expression (type *)&object+offset */
- if (handled_component_p (base))
+ base = TREE_OPERAND (base, 0);
+
+ /* Handle case where existing COMPONENT_REF pick e.g. wrong field of union,
+ so it needs to be removed and new COMPONENT_REF constructed.
+ The wrong COMPONENT_REF are often constructed by folding the
+ (type *)&object within the expression (type *)&object+offset */
+ if (handled_component_p (base))
+ {
+ HOST_WIDE_INT sub_offset, size, maxsize;
+ tree newbase;
+ newbase = get_ref_base_and_extent (base, &sub_offset,
+ &size, &maxsize);
+ gcc_assert (newbase);
+ if (size == maxsize
+ && size != -1
+ && !(sub_offset & (BITS_PER_UNIT - 1)))
{
- HOST_WIDE_INT sub_offset, size, maxsize;
- tree newbase;
- newbase = get_ref_base_and_extent (base, &sub_offset,
- &size, &maxsize);
- gcc_assert (newbase);
- if (size == maxsize
- && size != -1
- && !(sub_offset & (BITS_PER_UNIT - 1)))
- {
- base = newbase;
- if (sub_offset)
- offset = int_const_binop (PLUS_EXPR, offset,
- build_int_cst (TREE_TYPE (offset),
- sub_offset / BITS_PER_UNIT), 1);
- }
+ base = newbase;
+ if (sub_offset)
+ offset = int_const_binop (PLUS_EXPR, offset,
+ build_int_cst (TREE_TYPE (offset),
+ sub_offset / BITS_PER_UNIT), 1);
}
- if (useless_type_conversion_p (orig_type, TREE_TYPE (base))
- && integer_zerop (offset))
- return base;
- type = TREE_TYPE (base);
}
- else
- {
- base_is_ptr = true;
- if (!POINTER_TYPE_P (TREE_TYPE (base)))
- return NULL_TREE;
- type = TREE_TYPE (TREE_TYPE (base));
- }
- ret = maybe_fold_offset_to_component_ref (loc, type, base, offset,
- orig_type, base_is_ptr);
+ if (useless_type_conversion_p (orig_type, TREE_TYPE (base))
+ && integer_zerop (offset))
+ return base;
+ type = TREE_TYPE (base);
+
+ ret = maybe_fold_offset_to_component_ref (loc, type, base, offset, orig_type);
if (!ret)
- {
- if (base_is_ptr)
- {
- base = build1 (INDIRECT_REF, type, base);
- SET_EXPR_LOCATION (base, loc);
- }
- ret = maybe_fold_offset_to_array_ref (loc,
- base, offset, orig_type, true);
- }
+ ret = maybe_fold_offset_to_array_ref (loc, base, offset, orig_type, true);
+
return ret;
}
&& TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST
&& tree_int_cst_equal (gimple_assign_rhs2 (offset_def),
TYPE_SIZE_UNIT (TREE_TYPE (op0))))
- return build1 (ADDR_EXPR, res_type,
- build4 (ARRAY_REF, TREE_TYPE (op0),
+ return build_fold_addr_expr
+ (build4 (ARRAY_REF, TREE_TYPE (op0),
TREE_OPERAND (op0, 0),
gimple_assign_rhs1 (offset_def),
TREE_OPERAND (op0, 2),
TREE_OPERAND (op0, 3)));
else if (integer_onep (TYPE_SIZE_UNIT (TREE_TYPE (op0)))
&& gimple_assign_rhs_code (offset_def) != MULT_EXPR)
- return build1 (ADDR_EXPR, res_type,
- build4 (ARRAY_REF, TREE_TYPE (op0),
+ return build_fold_addr_expr
+ (build4 (ARRAY_REF, TREE_TYPE (op0),
TREE_OPERAND (op0, 0),
op1,
TREE_OPERAND (op0, 2),
t = maybe_fold_offset_to_array_ref (loc, op0, op1, ptd_type, true);
if (!t)
t = maybe_fold_offset_to_component_ref (loc, TREE_TYPE (op0), op0, op1,
- ptd_type, false);
+ ptd_type);
if (t)
{
t = build1 (ADDR_EXPR, res_type, t);
return false;
}
+static void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
/* Attempt to fold a call statement referenced by the statement iterator GSI.
The statement may be replaced by another statement, e.g., if the call
tree result = ccp_fold_builtin (stmt);
if (result)
- return update_call_from_tree (gsi, result);
+ {
+ if (!update_call_from_tree (gsi, result))
+ gimplify_and_update_call_from_tree (gsi, result);
+ return true;
+ }
}
else
{
static tree
optimize_stack_restore (gimple_stmt_iterator i)
{
- tree callee, rhs;
- gimple stmt, stack_save;
- gimple_stmt_iterator stack_save_gsi;
+ tree callee;
+ gimple stmt;
basic_block bb = gsi_bb (i);
gimple call = gsi_stmt (i);
return NULL_TREE;
if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_RESTORE)
- break;
+ goto second_stack_restore;
}
- if (gsi_end_p (i)
- && (! single_succ_p (bb)
- || single_succ_edge (bb)->dest != EXIT_BLOCK_PTR))
+ if (!gsi_end_p (i))
return NULL_TREE;
- stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
- if (gimple_code (stack_save) != GIMPLE_CALL
- || gimple_call_lhs (stack_save) != gimple_call_arg (call, 0)
- || stmt_could_throw_p (stack_save)
- || !has_single_use (gimple_call_arg (call, 0)))
- return NULL_TREE;
+ /* Allow one successor of the exit block, or zero successors. */
+ switch (EDGE_COUNT (bb->succs))
+ {
+ case 0:
+ break;
+ case 1:
+ if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR)
+ return NULL_TREE;
+ break;
+ default:
+ return NULL_TREE;
+ }
+ second_stack_restore:
- callee = gimple_call_fndecl (stack_save);
- if (!callee
- || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
- || DECL_FUNCTION_CODE (callee) != BUILT_IN_STACK_SAVE
- || gimple_call_num_args (stack_save) != 0)
- return NULL_TREE;
+ /* If there's exactly one use, then zap the call to __builtin_stack_save.
+ If there are multiple uses, then the last one should remove the call.
+ In any case, whether the call to __builtin_stack_save can be removed
+ or not is irrelevant to removing the call to __builtin_stack_restore. */
+ if (has_single_use (gimple_call_arg (call, 0)))
+ {
+ gimple stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
+ if (is_gimple_call (stack_save))
+ {
+ callee = gimple_call_fndecl (stack_save);
+ if (callee
+ && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_SAVE)
+ {
+ gimple_stmt_iterator stack_save_gsi;
+ tree rhs;
- stack_save_gsi = gsi_for_stmt (stack_save);
- rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
- if (!update_call_from_tree (&stack_save_gsi, rhs))
- return NULL_TREE;
+ stack_save_gsi = gsi_for_stmt (stack_save);
+ rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
+ update_call_from_tree (&stack_save_gsi, rhs);
+ }
+ }
+ }
/* No effect, so the statement will be deleted. */
return integer_zero_node;