| /* IPA predicates. |
| Copyright (C) 2003-2022 Free Software Foundation, Inc. |
| Contributed by Jan Hubicka |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "tree.h" |
| #include "cgraph.h" |
| #include "tree-vrp.h" |
| #include "alloc-pool.h" |
| #include "symbol-summary.h" |
| #include "ipa-prop.h" |
| #include "ipa-fnsummary.h" |
| #include "real.h" |
| #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 |
| sane. */ |
| |
| void |
| ipa_predicate::add_clause (conditions conditions, clause_t new_clause) |
| { |
| int i; |
| int i2; |
| int insert_here = -1; |
| int c1, c2; |
| |
| /* True clause. */ |
| if (!new_clause) |
| return; |
| |
| /* False clause makes the whole predicate false. Kill the other variants. */ |
| if (new_clause == (1 << ipa_predicate::false_condition)) |
| { |
| *this = false; |
| return; |
| } |
| if (*this == false) |
| return; |
| |
| /* No one should be silly enough to add false into nontrivial clauses. */ |
| gcc_checking_assert (!(new_clause & (1 << ipa_predicate::false_condition))); |
| |
| /* Look where to insert the new_clause. At the same time prune out |
| new_clauses of P that are implied by the new new_clause and thus |
| redundant. */ |
| for (i = 0, i2 = 0; i <= max_clauses; i++) |
| { |
| m_clause[i2] = m_clause[i]; |
| |
| if (!m_clause[i]) |
| break; |
| |
| /* If m_clause[i] implies new_clause, there is nothing to add. */ |
| if ((m_clause[i] & new_clause) == m_clause[i]) |
| { |
| /* We had nothing to add, none of clauses should've become |
| redundant. */ |
| gcc_checking_assert (i == i2); |
| return; |
| } |
| |
| if (m_clause[i] < new_clause && insert_here < 0) |
| insert_here = i2; |
| |
| /* If new_clause implies clause[i], then clause[i] becomes redundant. |
| Otherwise the clause[i] has to stay. */ |
| if ((m_clause[i] & new_clause) != new_clause) |
| i2++; |
| } |
| |
| /* Look for clauses that are obviously true. I.e. |
| op0 == 5 || op0 != 5. */ |
| if (conditions) |
| for (c1 = ipa_predicate::first_dynamic_condition; |
| c1 < num_conditions; c1++) |
| { |
| condition *cc1; |
| if (!(new_clause & (1 << c1))) |
| continue; |
| cc1 = &(*conditions)[c1 - ipa_predicate::first_dynamic_condition]; |
| /* We have no way to represent !changed and !is_not_constant |
| and thus there is no point for looking for them. */ |
| if (cc1->code == changed || cc1->code == is_not_constant) |
| continue; |
| for (c2 = c1 + 1; c2 < num_conditions; c2++) |
| if (new_clause & (1 << c2)) |
| { |
| condition *cc2 = |
| &(*conditions)[c2 - ipa_predicate::first_dynamic_condition]; |
| if (cc1->operand_num == cc2->operand_num |
| && vrp_operand_equal_p (cc1->val, cc2->val) |
| && cc2->code != is_not_constant |
| && 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; |
| } |
| } |
| |
| |
| /* We run out of variants. Be conservative in positive direction. */ |
| if (i2 == max_clauses) |
| return; |
| /* Keep clauses in decreasing order. This makes equivalence testing easy. */ |
| m_clause[i2 + 1] = 0; |
| if (insert_here >= 0) |
| for (; i2 > insert_here; i2--) |
| m_clause[i2] = m_clause[i2 - 1]; |
| else |
| insert_here = i2; |
| m_clause[insert_here] = new_clause; |
| } |
| |
| |
| /* Do THIS &= P. */ |
| |
| ipa_predicate & |
| ipa_predicate::operator &= (const ipa_predicate &p) |
| { |
| /* Avoid busy work. */ |
| if (p == false || *this == true) |
| { |
| *this = p; |
| return *this; |
| } |
| if (*this == false || p == true || this == &p) |
| return *this; |
| |
| int i; |
| |
| /* See how far ipa_predicates match. */ |
| for (i = 0; m_clause[i] && m_clause[i] == p.m_clause[i]; i++) |
| { |
| gcc_checking_assert (i < max_clauses); |
| } |
| |
| /* Combine the ipa_predicates rest. */ |
| for (; p.m_clause[i]; i++) |
| { |
| gcc_checking_assert (i < max_clauses); |
| add_clause (NULL, p.m_clause[i]); |
| } |
| return *this; |
| } |
| |
| |
| |
| /* Return THIS | P2. */ |
| |
| ipa_predicate |
| ipa_predicate::or_with (conditions conditions, |
| const ipa_predicate &p) const |
| { |
| /* Avoid busy work. */ |
| if (p == false || *this == true || *this == p) |
| return *this; |
| if (*this == false || p == true) |
| return p; |
| |
| /* OK, combine the predicates. */ |
| ipa_predicate out = true; |
| |
| for (int i = 0; m_clause[i]; i++) |
| for (int j = 0; p.m_clause[j]; j++) |
| { |
| gcc_checking_assert (i < max_clauses && j < max_clauses); |
| out.add_clause (conditions, m_clause[i] | p.m_clause[j]); |
| } |
| return out; |
| } |
| |
| |
| /* Having partial truth assignment in POSSIBLE_TRUTHS, return false |
| if predicate P is known to be false. */ |
| |
| bool |
| ipa_predicate::evaluate (clause_t possible_truths) const |
| { |
| int i; |
| |
| /* True remains true. */ |
| if (*this == true) |
| return true; |
| |
| gcc_assert (!(possible_truths & (1 << ipa_predicate::false_condition))); |
| |
| /* See if we can find clause we can disprove. */ |
| for (i = 0; m_clause[i]; i++) |
| { |
| gcc_checking_assert (i < max_clauses); |
| if (!(m_clause[i] & possible_truths)) |
| return false; |
| } |
| return true; |
| } |
| |
| /* Return the probability in range 0...REG_BR_PROB_BASE that the predicated |
| instruction will be recomputed per invocation of the inlined call. */ |
| |
| int |
| ipa_predicate::probability (conditions conds, |
| clause_t possible_truths, |
| vec<inline_param_summary> inline_param_summary) const |
| { |
| int i; |
| int combined_prob = REG_BR_PROB_BASE; |
| |
| /* True remains true. */ |
| if (*this == true) |
| return REG_BR_PROB_BASE; |
| |
| if (*this == false) |
| return 0; |
| |
| gcc_assert (!(possible_truths & (1 << ipa_predicate::false_condition))); |
| |
| /* See if we can find clause we can disprove. */ |
| for (i = 0; m_clause[i]; i++) |
| { |
| gcc_checking_assert (i < max_clauses); |
| if (!(m_clause[i] & possible_truths)) |
| return 0; |
| else |
| { |
| int this_prob = 0; |
| int i2; |
| if (!inline_param_summary.exists ()) |
| return REG_BR_PROB_BASE; |
| for (i2 = 0; i2 < num_conditions; i2++) |
| if ((m_clause[i] & possible_truths) & (1 << i2)) |
| { |
| if (i2 >= ipa_predicate::first_dynamic_condition) |
| { |
| condition *c = |
| &(*conds)[i2 - ipa_predicate::first_dynamic_condition]; |
| if (c->code == ipa_predicate::changed |
| && (c->operand_num < |
| (int) inline_param_summary.length ())) |
| { |
| int iprob = |
| inline_param_summary[c->operand_num].change_prob; |
| this_prob = MAX (this_prob, iprob); |
| } |
| else |
| this_prob = REG_BR_PROB_BASE; |
| } |
| else |
| this_prob = REG_BR_PROB_BASE; |
| } |
| combined_prob = MIN (this_prob, combined_prob); |
| if (!combined_prob) |
| return 0; |
| } |
| } |
| return combined_prob; |
| } |
| |
| |
| /* Dump conditional COND. */ |
| |
| void |
| dump_condition (FILE *f, conditions conditions, int cond) |
| { |
| condition *c; |
| if (cond == ipa_predicate::false_condition) |
| fprintf (f, "false"); |
| else if (cond == ipa_predicate::not_inlined_condition) |
| fprintf (f, "not inlined"); |
| else |
| { |
| c = &(*conditions)[cond - ipa_predicate::first_dynamic_condition]; |
| fprintf (f, "op%i", c->operand_num); |
| 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 == ipa_predicate::is_not_constant) |
| { |
| fprintf (f, " not constant"); |
| return; |
| } |
| if (c->code == ipa_predicate::changed) |
| { |
| fprintf (f, " changed"); |
| return; |
| } |
| fprintf (f, " %s ", op_symbol_code (c->code)); |
| print_generic_expr (f, c->val); |
| } |
| } |
| |
| |
| /* Dump clause CLAUSE. */ |
| |
| static void |
| dump_clause (FILE *f, conditions conds, clause_t clause) |
| { |
| int i; |
| bool found = false; |
| fprintf (f, "("); |
| if (!clause) |
| fprintf (f, "true"); |
| for (i = 0; i < ipa_predicate::num_conditions; i++) |
| if (clause & (1 << i)) |
| { |
| if (found) |
| fprintf (f, " || "); |
| found = true; |
| dump_condition (f, conds, i); |
| } |
| fprintf (f, ")"); |
| } |
| |
| |
| /* Dump THIS to F. CONDS a vector of conditions used when evaluating |
| ipa_predicates. When NL is true new line is output at the end of dump. */ |
| |
| void |
| ipa_predicate::dump (FILE *f, conditions conds, bool nl) const |
| { |
| int i; |
| if (*this == true) |
| dump_clause (f, conds, 0); |
| else |
| for (i = 0; m_clause[i]; i++) |
| { |
| if (i) |
| fprintf (f, " && "); |
| dump_clause (f, conds, m_clause[i]); |
| } |
| if (nl) |
| fprintf (f, "\n"); |
| } |
| |
| |
| void |
| ipa_predicate::debug (conditions conds) const |
| { |
| dump (stderr, conds); |
| } |
| |
| |
| /* Remap predicate THIS of former function to be predicate of duplicated function. |
| POSSIBLE_TRUTHS is clause of possible truths in the duplicated node, |
| INFO is inline summary of the duplicated node. */ |
| |
| ipa_predicate |
| ipa_predicate::remap_after_duplication (clause_t possible_truths) |
| { |
| int j; |
| ipa_predicate out = true; |
| for (j = 0; m_clause[j]; j++) |
| if (!(possible_truths & m_clause[j])) |
| return false; |
| else |
| out.add_clause (NULL, possible_truths & m_clause[j]); |
| return out; |
| } |
| |
| |
| /* Translate all conditions from callee representation into caller |
| representation and symbolically evaluate predicate THIS into new predicate. |
| |
| INFO is ipa_fn_summary of function we are adding predicate into, CALLEE_INFO |
| is summary of function predicate P is from. OPERAND_MAP is array giving |
| callee formal IDs the caller formal IDs. POSSSIBLE_TRUTHS is clause of all |
| callee conditions that may be true in caller context. TOPLEV_PREDICATE is |
| predicate under which callee is executed. OFFSET_MAP is an array of |
| offsets that need to be added to conditions, negative offset means that |
| conditions relying on values passed by reference have to be discarded |
| because they might not be preserved (and should be considered offset zero |
| for other purposes). */ |
| |
| ipa_predicate |
| ipa_predicate::remap_after_inlining (class ipa_fn_summary *info, |
| class ipa_node_params *params_summary, |
| class ipa_fn_summary *callee_info, |
| const vec<int> &operand_map, |
| const vec<HOST_WIDE_INT> &offset_map, |
| clause_t possible_truths, |
| const ipa_predicate &toplev_predicate) |
| { |
| int i; |
| ipa_predicate out = true; |
| |
| /* True ipa_predicate is easy. */ |
| if (*this == true) |
| return toplev_predicate; |
| for (i = 0; m_clause[i]; i++) |
| { |
| clause_t clause = m_clause[i]; |
| int cond; |
| ipa_predicate clause_predicate = false; |
| |
| gcc_assert (i < max_clauses); |
| |
| for (cond = 0; cond < num_conditions; cond++) |
| /* Do we have condition we can't disprove? */ |
| if (clause & possible_truths & (1 << cond)) |
| { |
| ipa_predicate cond_predicate; |
| /* Work out if the condition can translate to predicate in the |
| inlined function. */ |
| if (cond >= ipa_predicate::first_dynamic_condition) |
| { |
| struct condition *c; |
| |
| int index = cond - ipa_predicate::first_dynamic_condition; |
| c = &(*callee_info->conds)[index]; |
| /* See if we can remap condition operand to caller's operand. |
| Otherwise give up. */ |
| if (!operand_map.exists () |
| || (int) operand_map.length () <= c->operand_num |
| || operand_map[c->operand_num] == -1 |
| /* TODO: For non-aggregate conditions, adding an offset is |
| basically an arithmetic jump function processing which |
| we should support in future. */ |
| || ((!c->agg_contents || !c->by_ref) |
| && offset_map[c->operand_num] > 0) |
| || (c->agg_contents && c->by_ref |
| && offset_map[c->operand_num] < 0)) |
| cond_predicate = true; |
| else |
| { |
| struct agg_position_info ap; |
| HOST_WIDE_INT offset_delta = offset_map[c->operand_num]; |
| if (offset_delta < 0) |
| { |
| gcc_checking_assert (!c->agg_contents || !c->by_ref); |
| offset_delta = 0; |
| } |
| gcc_assert (!c->agg_contents |
| || c->by_ref || offset_delta == 0); |
| ap.offset = c->offset + offset_delta; |
| ap.agg_contents = c->agg_contents; |
| ap.by_ref = c->by_ref; |
| cond_predicate = add_condition (info, params_summary, |
| operand_map[c->operand_num], |
| c->type, &ap, c->code, |
| c->val, c->param_ops); |
| } |
| } |
| /* Fixed conditions remains same, construct single |
| condition predicate. */ |
| else |
| cond_predicate = ipa_predicate::predicate_testing_cond (cond); |
| clause_predicate = clause_predicate.or_with (info->conds, |
| cond_predicate); |
| } |
| out &= clause_predicate; |
| } |
| out &= toplev_predicate; |
| return out; |
| } |
| |
| |
| /* Read predicate from IB. */ |
| |
| void |
| ipa_predicate::stream_in (class lto_input_block *ib) |
| { |
| clause_t clause; |
| int k = 0; |
| |
| do |
| { |
| gcc_assert (k <= max_clauses); |
| clause = m_clause[k++] = streamer_read_uhwi (ib); |
| } |
| while (clause); |
| |
| /* Zero-initialize the remaining clauses in OUT. */ |
| while (k <= max_clauses) |
| m_clause[k++] = 0; |
| } |
| |
| |
| /* Write predicate P to OB. */ |
| |
| void |
| ipa_predicate::stream_out (struct output_block *ob) |
| { |
| int j; |
| for (j = 0; m_clause[j]; j++) |
| { |
| gcc_assert (j < max_clauses); |
| streamer_write_uhwi (ob, m_clause[j]); |
| } |
| streamer_write_uhwi (ob, 0); |
| } |
| |
| |
| /* 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. */ |
| |
| ipa_predicate |
| add_condition (class ipa_fn_summary *summary, |
| class ipa_node_params *params_summary, |
| int operand_num, |
| tree type, struct agg_position_info *aggpos, |
| enum tree_code code, tree val, expr_eval_ops param_ops) |
| { |
| int i, j; |
| struct condition *c; |
| struct condition new_cond; |
| HOST_WIDE_INT offset; |
| bool agg_contents, by_ref; |
| expr_eval_op *op; |
| |
| if (params_summary) |
| ipa_set_param_used_by_ipa_predicates (params_summary, operand_num, true); |
| |
| if (aggpos) |
| { |
| offset = aggpos->offset; |
| agg_contents = aggpos->agg_contents; |
| by_ref = aggpos->by_ref; |
| } |
| else |
| { |
| offset = 0; |
| agg_contents = false; |
| by_ref = false; |
| } |
| |
| gcc_checking_assert (operand_num >= 0); |
| for (i = 0; vec_safe_iterate (summary->conds, i, &c); i++) |
| { |
| if (c->operand_num == operand_num |
| && c->code == code |
| && 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 ipa_predicate::predicate_testing_cond (i); |
| } |
| /* Too many conditions. Give up and return constant true. */ |
| if (i == ipa_predicate::num_conditions - ipa_predicate::first_dynamic_condition) |
| return true; |
| |
| new_cond.operand_num = operand_num; |
| new_cond.code = code; |
| 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.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 ipa_predicate::predicate_testing_cond (i); |
| } |