{
tree val;
tree res;
+ int j;
+ struct expr_eval_op *op;
/* We allow call stmt to have fewer arguments than the callee function
(especially for K&R style programs). So bound check here (we assume
continue;
}
- if (maybe_ne (tree_to_poly_int64 (TYPE_SIZE (TREE_TYPE (val))), c->size))
+ if (TYPE_SIZE (c->type) != TYPE_SIZE (TREE_TYPE (val)))
{
clause |= 1 << (i + predicate::first_dynamic_condition);
nonspec_clause |= 1 << (i + predicate::first_dynamic_condition);
continue;
}
- val = fold_unary (VIEW_CONVERT_EXPR, TREE_TYPE (c->val), val);
+ val = fold_unary (VIEW_CONVERT_EXPR, c->type, val);
+ for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
+ {
+ if (!val)
+ break;
+ if (!op->val[0])
+ val = fold_unary (op->code, op->type, val);
+ else if (!op->val[1])
+ val = fold_binary (op->code, op->type,
+ op->index ? op->val[0] : val,
+ op->index ? val : op->val[0]);
+ else if (op->index == 0)
+ val = fold_ternary (op->code, op->type,
+ val, op->val[0], op->val[1]);
+ else if (op->index == 1)
+ val = fold_ternary (op->code, op->type,
+ op->val[0], val, op->val[1]);
+ else if (op->index == 2)
+ val = fold_ternary (op->code, op->type,
+ op->val[0], op->val[1], val);
+ else
+ val = NULL_TREE;
+ }
+
res = val
? fold_binary_to_constant (c->code, boolean_type_node, val, c->val)
: NULL;
}
}
+/* Analyze EXPR if it represents a series of simple operations performed on
+ a function parameter and return true if so. FBI, STMT, EXPR, INDEX_P and
+ AGGPOS have the same meaning like in unmodified_parm_or_parm_agg_item.
+ Type of the parameter or load from an aggregate via the parameter is
+ stored in *TYPE_P. Operations on the parameter are recorded to
+ PARAM_OPS_P if it is not NULL. */
+
+static bool
+decompose_param_expr (struct ipa_func_body_info *fbi,
+ gimple *stmt, tree expr,
+ int *index_p, tree *type_p,
+ struct agg_position_info *aggpos,
+ expr_eval_ops *param_ops_p = NULL)
+{
+ int op_limit = PARAM_VALUE (PARAM_IPA_MAX_PARAM_EXPR_OPS);
+ int op_count = 0;
+
+ if (param_ops_p)
+ *param_ops_p = NULL;
+
+ while (true)
+ {
+ expr_eval_op eval_op;
+ unsigned rhs_count;
+ unsigned cst_count = 0;
+
+ if (unmodified_parm_or_parm_agg_item (fbi, stmt, expr, index_p, NULL,
+ aggpos))
+ {
+ tree type = TREE_TYPE (expr);
+
+ if (aggpos->agg_contents)
+ {
+ /* Stop if containing bit-field. */
+ if (TREE_CODE (expr) == BIT_FIELD_REF
+ || contains_bitfld_component_ref_p (expr))
+ break;
+ }
+
+ *type_p = type;
+ return true;
+ }
+
+ if (TREE_CODE (expr) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (expr))
+ break;
+
+ if (!is_gimple_assign (stmt = SSA_NAME_DEF_STMT (expr)))
+ break;
+
+ switch (gimple_assign_rhs_class (stmt))
+ {
+ case GIMPLE_SINGLE_RHS:
+ expr = gimple_assign_rhs1 (stmt);
+ continue;
+
+ case GIMPLE_UNARY_RHS:
+ rhs_count = 1;
+ break;
+
+ case GIMPLE_BINARY_RHS:
+ rhs_count = 2;
+ break;
+
+ case GIMPLE_TERNARY_RHS:
+ rhs_count = 3;
+ break;
+
+ default:
+ goto fail;
+ }
+
+ /* Stop if expression is too complex. */
+ if (op_count++ == op_limit)
+ break;
+
+ if (param_ops_p)
+ {
+ eval_op.code = gimple_assign_rhs_code (stmt);
+ eval_op.type = TREE_TYPE (gimple_assign_lhs (stmt));
+ eval_op.val[0] = NULL_TREE;
+ eval_op.val[1] = NULL_TREE;
+ }
+
+ expr = NULL_TREE;
+ for (unsigned i = 0; i < rhs_count; i++)
+ {
+ tree op = gimple_op (stmt, i + 1);
+
+ gcc_assert (op && !TYPE_P (op));
+ if (is_gimple_ip_invariant (op))
+ {
+ if (++cst_count == rhs_count)
+ goto fail;
+
+ eval_op.val[cst_count - 1] = op;
+ }
+ else if (!expr)
+ {
+ /* Found a non-constant operand, and record its index in rhs
+ operands. */
+ eval_op.index = i;
+ expr = op;
+ }
+ else
+ {
+ /* Found more than one non-constant operands. */
+ goto fail;
+ }
+ }
+
+ if (param_ops_p)
+ vec_safe_insert (*param_ops_p, 0, eval_op);
+ }
+
+ /* Failed to decompose, free resource and return. */
+fail:
+ if (param_ops_p)
+ vec_free (*param_ops_p);
+
+ return false;
+}
/* If BB ends by a conditional we can turn into predicates, attach corresponding
predicates to the CFG edges. */
basic_block bb)
{
gimple *last;
- tree op;
+ tree op, op2;
int index;
- poly_int64 size;
struct agg_position_info aggpos;
enum tree_code code, inverted_code;
edge e;
edge_iterator ei;
gimple *set_stmt;
- tree op2;
+ tree param_type;
+ expr_eval_ops param_ops;
last = last_stmt (bb);
if (!last || gimple_code (last) != GIMPLE_COND)
if (!is_gimple_ip_invariant (gimple_cond_rhs (last)))
return;
op = gimple_cond_lhs (last);
- /* TODO: handle conditionals like
- var = op0 < 4;
- if (var != 0). */
- if (unmodified_parm_or_parm_agg_item (fbi, last, op, &index, &size, &aggpos))
+
+ if (decompose_param_expr (fbi, last, op, &index, ¶m_type, &aggpos,
+ ¶m_ops))
{
code = gimple_cond_code (last);
inverted_code = invert_tree_comparison (code, HONOR_NANS (op));
&& !dominated_by_p (CDI_POST_DOMINATORS, bb, e->dest))
{
predicate p
- = add_condition (summary, index, size, &aggpos, this_code,
- unshare_expr_without_location
- (gimple_cond_rhs (last)));
+ = add_condition (summary, index, param_type, &aggpos,
+ this_code, gimple_cond_rhs (last), param_ops);
e->aux = edge_predicate_pool.allocate ();
*(predicate *) e->aux = p;
}
}
+ vec_free (param_ops);
}
if (TREE_CODE (op) != SSA_NAME)
|| gimple_call_num_args (set_stmt) != 1)
return;
op2 = gimple_call_arg (set_stmt, 0);
- if (!unmodified_parm_or_parm_agg_item (fbi, set_stmt, op2, &index, &size,
- &aggpos))
+ if (!decompose_param_expr (fbi, set_stmt, op2, &index, ¶m_type, &aggpos))
return;
FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALSE_VALUE)
{
- predicate p = add_condition (summary, index, size, &aggpos,
+ predicate p = add_condition (summary, index, param_type, &aggpos,
predicate::is_not_constant, NULL_TREE);
e->aux = edge_predicate_pool.allocate ();
*(predicate *) e->aux = p;
gimple *lastg;
tree op;
int index;
- poly_int64 size;
struct agg_position_info aggpos;
edge e;
edge_iterator ei;
size_t n;
size_t case_idx;
+ tree param_type;
+ expr_eval_ops param_ops;
lastg = last_stmt (bb);
if (!lastg || gimple_code (lastg) != GIMPLE_SWITCH)
return;
gswitch *last = as_a <gswitch *> (lastg);
op = gimple_switch_index (last);
- if (!unmodified_parm_or_parm_agg_item (fbi, last, op, &index, &size, &aggpos))
+ if (!decompose_param_expr (fbi, last, op, &index, ¶m_type, &aggpos,
+ ¶m_ops))
return;
auto_vec<std::pair<tree, tree> > ranges;
tree max = CASE_HIGH (cl);
predicate p;
+ e = gimple_switch_edge (cfun, last, case_idx);
+
/* The case value might not have same type as switch expression,
extend the value based on the expression type. */
if (TREE_TYPE (min) != type)
if (dominated_by_p (CDI_POST_DOMINATORS, bb, e->dest))
p = true;
else if (min == max)
- p = add_condition (summary, index, size, &aggpos, EQ_EXPR,
- unshare_expr_without_location (min));
+ p = add_condition (summary, index, param_type, &aggpos, EQ_EXPR,
+ min, param_ops);
else
{
predicate p1, p2;
- p1 = add_condition (summary, index, size, &aggpos, GE_EXPR,
- unshare_expr_without_location (min));
- p2 = add_condition (summary, index, size, &aggpos, LE_EXPR,
- unshare_expr_without_location (max));
+ p1 = add_condition (summary, index, param_type, &aggpos, GE_EXPR,
+ min, param_ops);
+ p2 = add_condition (summary, index, param_type, &aggpos, LE_EXPR,
+ max, param_ops);
p = p1 & p2;
}
- e = gimple_switch_edge (cfun, last, case_idx);
*(class predicate *) e->aux
= p.or_with (summary->conds, *(class predicate *) e->aux);
if (bound_count > bound_limit)
{
*(class predicate *) e->aux = true;
+ vec_free (param_ops);
return;
}
tree max = ranges[i].second;
if (min == max)
- p_seg &= add_condition (summary, index, size, &aggpos, NE_EXPR,
- unshare_expr_without_location (min));
+ p_seg &= add_condition (summary, index, param_type, &aggpos, NE_EXPR,
+ min, param_ops);
else
{
/* Do not create sub-predicate for range that is beyond low bound
of switch index. */
if (wi::lt_p (vr_wmin, wi::to_wide (min), TYPE_SIGN (type)))
{
- p_seg &= add_condition (summary, index, size, &aggpos, LT_EXPR,
- unshare_expr_without_location (min));
+ p_seg &= add_condition (summary, index, param_type, &aggpos,
+ LT_EXPR, min, param_ops);
p_all = p_all.or_with (summary->conds, p_seg);
}
break;
}
- p_seg = add_condition (summary, index, size, &aggpos, GT_EXPR,
- unshare_expr_without_location (max));
+ p_seg = add_condition (summary, index, param_type, &aggpos, GT_EXPR,
+ max, param_ops);
}
}
p_all = p_all.or_with (summary->conds, p_seg);
*(class predicate *) e->aux
= p_all.or_with (summary->conds, *(class predicate *) e->aux);
+
+ vec_free (param_ops);
}
{
tree parm;
int index;
- poly_int64 size;
while (UNARY_CLASS_P (expr))
expr = TREE_OPERAND (expr, 0);
- parm = unmodified_parm (fbi, NULL, expr, &size);
+ parm = unmodified_parm (fbi, NULL, expr, NULL);
if (parm && (index = ipa_get_param_decl_index (fbi->info, parm)) >= 0)
- return add_condition (summary, index, size, NULL, predicate::changed,
- NULL_TREE);
+ return add_condition (summary, index, TREE_TYPE (parm), NULL,
+ predicate::changed, NULL_TREE);
if (is_gimple_min_invariant (expr))
return false;
if (TREE_CODE (expr) == SSA_NAME)
predicate p = true;
ssa_op_iter iter;
tree use;
+ tree param_type = NULL_TREE;
predicate op_non_const;
bool is_load;
int base_index;
- poly_int64 size;
struct agg_position_info aggpos;
/* What statments might be optimized away
/* Loads can be optimized when the value is known. */
if (is_load)
{
- tree op;
- gcc_assert (gimple_assign_single_p (stmt));
- op = gimple_assign_rhs1 (stmt);
- if (!unmodified_parm_or_parm_agg_item (fbi, stmt, op, &base_index, &size,
- &aggpos))
+ tree op = gimple_assign_rhs1 (stmt);
+ if (!decompose_param_expr (fbi, stmt, op, &base_index, ¶m_type,
+ &aggpos))
return p;
}
else
if (is_load)
op_non_const =
- add_condition (summary, base_index, size, &aggpos, predicate::changed,
- NULL);
+ add_condition (summary, base_index, param_type, &aggpos,
+ predicate::changed, NULL_TREE);
else
op_non_const = false;
FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
{
- poly_int64 size;
- tree parm = unmodified_parm (fbi, stmt, use, &size);
+ tree parm = unmodified_parm (fbi, stmt, use, NULL);
int index;
if (parm && (index = ipa_get_param_decl_index (fbi->info, parm)) >= 0)
{
if (index != base_index)
- p = add_condition (summary, index, size, NULL, predicate::changed,
- NULL_TREE);
+ p = add_condition (summary, index, TREE_TYPE (parm), NULL,
+ predicate::changed, NULL_TREE);
else
continue;
}
return REG_BR_PROB_BASE;
if (dump_file)
{
- fprintf (dump_file, " Analyzing param change probablity of ");
+ fprintf (dump_file, " Analyzing param change probability of ");
print_generic_expr (dump_file, op, TDF_SLIM);
fprintf (dump_file, "\n");
}
for (j = 0; j < count2; j++)
{
struct condition c;
+ unsigned int k, count3;
c.operand_num = streamer_read_uhwi (&ib);
- c.size = streamer_read_poly_uint64 (&ib);
c.code = (enum tree_code) streamer_read_uhwi (&ib);
+ c.type = stream_read_tree (&ib, data_in);
c.val = stream_read_tree (&ib, data_in);
bp = streamer_read_bitpack (&ib);
c.agg_contents = bp_unpack_value (&bp, 1);
c.by_ref = bp_unpack_value (&bp, 1);
if (c.agg_contents)
c.offset = streamer_read_uhwi (&ib);
+ c.param_ops = NULL;
+ count3 = streamer_read_uhwi (&ib);
+ for (k = 0; k < count3; k++)
+ {
+ struct expr_eval_op op;
+ enum gimple_rhs_class rhs_class;
+ op.code = (enum tree_code) streamer_read_uhwi (&ib);
+ op.type = stream_read_tree (&ib, data_in);
+ switch (rhs_class = get_gimple_rhs_class (op.code))
+ {
+ case GIMPLE_UNARY_RHS:
+ op.index = 0;
+ op.val[0] = NULL_TREE;
+ op.val[1] = NULL_TREE;
+ break;
+
+ case GIMPLE_BINARY_RHS:
+ case GIMPLE_TERNARY_RHS:
+ bp = streamer_read_bitpack (&ib);
+ op.index = bp_unpack_value (&bp, 2);
+ op.val[0] = stream_read_tree (&ib, data_in);
+ if (rhs_class == GIMPLE_BINARY_RHS)
+ op.val[1] = NULL_TREE;
+ else
+ op.val[1] = stream_read_tree (&ib, data_in);
+ break;
+
+ default:
+ fatal_error (UNKNOWN_LOCATION,
+ "invalid fnsummary in LTO stream");
+ }
+ vec_safe_push (c.param_ops, op);
+ }
if (info)
vec_safe_push (info->conds, c);
}
streamer_write_uhwi (ob, vec_safe_length (info->conds));
for (i = 0; vec_safe_iterate (info->conds, i, &c); i++)
{
+ int j;
+ struct expr_eval_op *op;
+
streamer_write_uhwi (ob, c->operand_num);
- streamer_write_poly_uint64 (ob, c->size);
streamer_write_uhwi (ob, c->code);
+ stream_write_tree (ob, c->type, true);
stream_write_tree (ob, c->val, true);
bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, c->agg_contents, 1);
streamer_write_bitpack (&bp);
if (c->agg_contents)
streamer_write_uhwi (ob, c->offset);
+ streamer_write_uhwi (ob, vec_safe_length (c->param_ops));
+ for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
+ {
+ streamer_write_uhwi (ob, op->code);
+ stream_write_tree (ob, op->type, true);
+ if (op->val[0])
+ {
+ bp = bitpack_create (ob->main_stream);
+ bp_pack_value (&bp, op->index, 2);
+ streamer_write_bitpack (&bp);
+ stream_write_tree (ob, op->val[0], true);
+ if (op->val[1])
+ stream_write_tree (ob, op->val[1], true);
+ }
+ }
}
streamer_write_uhwi (ob, vec_safe_length (info->size_time_table));
for (i = 0; vec_safe_iterate (info->size_time_table, i, &e); i++)
#include "fold-const.h"
#include "tree-pretty-print.h"
#include "gimple.h"
+#include "gimplify.h"
#include "data-streamer.h"
+/* Check whether two set of operations have same effects. */
+static bool
+expr_eval_ops_equal_p (expr_eval_ops ops1, expr_eval_ops ops2)
+{
+ if (ops1)
+ {
+ if (!ops2 || ops1->length () != ops2->length ())
+ return false;
+
+ for (unsigned i = 0; i < ops1->length (); i++)
+ {
+ expr_eval_op &op1 = (*ops1)[i];
+ expr_eval_op &op2 = (*ops2)[i];
+
+ if (op1.code != op2.code
+ || op1.index != op2.index
+ || !vrp_operand_equal_p (op1.val[0], op2.val[0])
+ || !vrp_operand_equal_p (op1.val[1], op2.val[1])
+ || !types_compatible_p (op1.type, op2.type))
+ return false;
+ }
+ return true;
+ }
+ return !ops2;
+}
+
/* Add clause CLAUSE into the predicate P.
When CONDITIONS is NULL do not perform checking whether NEW_CLAUSE
is obviously true. This is useful only when NEW_CLAUSE is known to be
for (c2 = c1 + 1; c2 < num_conditions; c2++)
if (new_clause & (1 << c2))
{
- condition *cc1 =
- &(*conditions)[c1 - predicate::first_dynamic_condition];
condition *cc2 =
&(*conditions)[c2 - predicate::first_dynamic_condition];
if (cc1->operand_num == cc2->operand_num
- && cc1->val == cc2->val
+ && vrp_operand_equal_p (cc1->val, cc2->val)
&& cc2->code != is_not_constant
- && cc2->code != predicate::changed
+ && cc2->code != changed
+ && expr_eval_ops_equal_p (cc1->param_ops, cc2->param_ops)
+ && cc2->agg_contents == cc1->agg_contents
+ && cc2->by_ref == cc1->by_ref
+ && types_compatible_p (cc2->type, cc1->type)
&& cc1->code == invert_tree_comparison (cc2->code,
HONOR_NANS (cc1->val)))
return;
if (c->agg_contents)
fprintf (f, "[%soffset: " HOST_WIDE_INT_PRINT_DEC "]",
c->by_ref ? "ref " : "", c->offset);
+
+ for (unsigned i = 0; i < vec_safe_length (c->param_ops); i++)
+ {
+ expr_eval_op &op = (*(c->param_ops))[i];
+ const char *op_name = op_symbol_code (op.code);
+
+ if (op_name == op_symbol_code (ERROR_MARK))
+ op_name = get_tree_code_name (op.code);
+
+ fprintf (f, ",(");
+
+ if (!op.val[0])
+ {
+ switch (op.code)
+ {
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIXED_CONVERT_EXPR:
+ case VIEW_CONVERT_EXPR:
+ CASE_CONVERT:
+ if (op.code == VIEW_CONVERT_EXPR)
+ fprintf (f, "VCE");
+ fprintf (f, "(");
+ print_generic_expr (f, op.type);
+ fprintf (f, ")" );
+ break;
+
+ default:
+ fprintf (f, "%s", op_name);
+ }
+ fprintf (f, " #");
+ }
+ else if (!op.val[1])
+ {
+ if (op.index)
+ {
+ print_generic_expr (f, op.val[0]);
+ fprintf (f, " %s #", op_name);
+ }
+ else
+ {
+ fprintf (f, "# %s ", op_name);
+ print_generic_expr (f, op.val[0]);
+ }
+ }
+ else
+ {
+ fprintf (f, "%s ", op_name);
+ switch (op.index)
+ {
+ case 0:
+ fprintf (f, "#, ");
+ print_generic_expr (f, op.val[0]);
+ fprintf (f, ", ");
+ print_generic_expr (f, op.val[1]);
+ break;
+
+ case 1:
+ print_generic_expr (f, op.val[0]);
+ fprintf (f, ", #, ");
+ print_generic_expr (f, op.val[1]);
+ break;
+
+ case 2:
+ print_generic_expr (f, op.val[0]);
+ fprintf (f, ", ");
+ print_generic_expr (f, op.val[1]);
+ fprintf (f, ", #");
+ break;
+
+ default:
+ fprintf (f, "*, *, *");
+ }
+ }
+ fprintf (f, ")");
+ }
+
if (c->code == predicate::is_not_constant)
{
fprintf (f, " not constant");
ap.by_ref = c->by_ref;
cond_predicate = add_condition (info,
operand_map[c->operand_num],
- c->size, &ap, c->code,
- c->val);
+ c->type, &ap, c->code,
+ c->val, c->param_ops);
}
}
/* Fixed conditions remains same, construct single
}
-/* Add condition to condition list SUMMARY. OPERAND_NUM, SIZE, CODE and VAL
- correspond to fields of condition structure. AGGPOS describes whether the
- used operand is loaded from an aggregate and where in the aggregate it is.
- It can be NULL, which means this not a load from an aggregate. */
+/* Add condition to condition list SUMMARY. OPERAND_NUM, TYPE, CODE, VAL and
+ PARAM_OPS correspond to fields of condition structure. AGGPOS describes
+ whether the used operand is loaded from an aggregate and where in the
+ aggregate it is. It can be NULL, which means this not a load from an
+ aggregate. */
predicate
add_condition (class ipa_fn_summary *summary, int operand_num,
- poly_int64 size, struct agg_position_info *aggpos,
- enum tree_code code, tree val)
+ tree type, struct agg_position_info *aggpos,
+ enum tree_code code, tree val, expr_eval_ops param_ops)
{
- int i;
+ int i, j;
struct condition *c;
struct condition new_cond;
HOST_WIDE_INT offset;
bool agg_contents, by_ref;
+ expr_eval_op *op;
if (aggpos)
{
for (i = 0; vec_safe_iterate (summary->conds, i, &c); i++)
{
if (c->operand_num == operand_num
- && known_eq (c->size, size)
&& c->code == code
- && c->val == val
+ && types_compatible_p (c->type, type)
+ && vrp_operand_equal_p (c->val, val)
&& c->agg_contents == agg_contents
+ && expr_eval_ops_equal_p (c->param_ops, param_ops)
&& (!agg_contents || (c->offset == offset && c->by_ref == by_ref)))
return predicate::predicate_testing_cond (i);
}
new_cond.operand_num = operand_num;
new_cond.code = code;
- new_cond.val = val;
+ new_cond.type = unshare_expr_without_location (type);
+ new_cond.val = val ? unshare_expr_without_location (val) : val;
new_cond.agg_contents = agg_contents;
new_cond.by_ref = by_ref;
new_cond.offset = offset;
- new_cond.size = size;
+ new_cond.param_ops = vec_safe_copy (param_ops);
+
+ for (j = 0; vec_safe_iterate (new_cond.param_ops, j, &op); j++)
+ {
+ if (op->val[0])
+ op->val[0] = unshare_expr_without_location (op->val[0]);
+ if (op->val[1])
+ op->val[1] = unshare_expr_without_location (op->val[1]);
+ }
+
vec_safe_push (summary->conds, new_cond);
return predicate::predicate_testing_cond (i);