aboutsummaryrefslogtreecommitdiff
path: root/gcc/ipa-predicate.c
diff options
context:
space:
mode:
authorJan Hubicka <hubicka@gcc.gnu.org>2017-05-18 16:14:10 +0000
committerJan Hubicka <hubicka@gcc.gnu.org>2017-05-18 16:14:10 +0000
commitb679b55b5eb8ea463af3459092c19ba05cde664b (patch)
treee3393d1db3f70a23661e59bd26269ba67b9d40e3 /gcc/ipa-predicate.c
parent00d600138536a4978f40b25233c2f37c0fc426d4 (diff)
downloadgcc-b679b55b5eb8ea463af3459092c19ba05cde664b.zip
gcc-b679b55b5eb8ea463af3459092c19ba05cde664b.tar.gz
gcc-b679b55b5eb8ea463af3459092c19ba05cde664b.tar.bz2
Makefile.in: Add ipa-predicate.o and ipa-predicate.h
* Makefile.in: Add ipa-predicate.o and ipa-predicate.h * ipa-inline-analysis.c (NUM_CONDITIONS): turn into predicate::num_conditions (IS_NOT_CONSTANT): turn into predicate::is_not_constant. (CHANGED): turn into predicate::changed. (agg_position_info): Move to ipa-predicate.h (add_condition, predicate::add_clause, predicate::operator &=, predicate::or_with, predicate::evaluate, predicate::probability, dump_condition, dump_clause, predicate::dump, predicate::remap_after_duplication, predicate::remap_after_inlining, predicate::stream_in, predicate::stream_out): Move to ipa-predicate.c (evaluate_conditions_for_known_args): Update. (set_cond_stmt_execution_predicate): Update. * ipa-inline.h: Include ipa-predicate.h (condition, inline_param_summary, conditions, agg_position_info, predicate): Move to ipa-predicate.h * ipa-predicate.c: New file. * ipa-predicate.h: New file. From-SVN: r248241
Diffstat (limited to 'gcc/ipa-predicate.c')
-rw-r--r--gcc/ipa-predicate.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/gcc/ipa-predicate.c b/gcc/ipa-predicate.c
new file mode 100644
index 0000000..660e327
--- /dev/null
+++ b/gcc/ipa-predicate.c
@@ -0,0 +1,573 @@
+/* IPA predicates.
+ Copyright (C) 2003-2017 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 "symbol-summary.h"
+#include "alloc-pool.h"
+#include "ipa-prop.h"
+#include "ipa-inline.h"
+#include "real.h"
+#include "fold-const.h"
+#include "tree-pretty-print.h"
+#include "gimple.h"
+#include "data-streamer.h"
+
+
+/* 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
+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 << 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 << 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 = predicate::first_dynamic_condition;
+ c1 < num_conditions; c1++)
+ {
+ condition *cc1;
+ if (!(new_clause & (1 << c1)))
+ continue;
+ cc1 = &(*conditions)[c1 - 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 *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
+ && cc2->code != is_not_constant
+ && cc2->code != predicate::changed
+ && 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. */
+
+predicate &
+predicate::operator &= (const 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 predicates match. */
+ for (i = 0; m_clause[i] && m_clause[i] == p.m_clause[i]; i++)
+ {
+ gcc_checking_assert (i < max_clauses);
+ }
+
+ /* Combine the 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. */
+
+predicate
+predicate::or_with (conditions conditions,
+ const 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. */
+ 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
+predicate::evaluate (clause_t possible_truths) const
+{
+ int i;
+
+ /* True remains true. */
+ if (*this == true)
+ return true;
+
+ gcc_assert (!(possible_truths & (1 << 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
+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 << 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 >= predicate::first_dynamic_condition)
+ {
+ condition *c =
+ &(*conds)[i2 - predicate::first_dynamic_condition];
+ if (c->code == 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 == predicate::false_condition)
+ fprintf (f, "false");
+ else if (cond == predicate::not_inlined_condition)
+ fprintf (f, "not inlined");
+ else
+ {
+ c = &(*conditions)[cond - 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);
+ if (c->code == predicate::is_not_constant)
+ {
+ fprintf (f, " not constant");
+ return;
+ }
+ if (c->code == 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 < 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 evauating
+ predicats. When NL is true new line is output at the end of dump. */
+
+void
+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
+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. */
+
+predicate
+predicate::remap_after_duplication (clause_t possible_truths)
+{
+ int j;
+ 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 inline_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 clausule 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 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). */
+
+predicate
+predicate::remap_after_inlining (struct inline_summary *info,
+ struct inline_summary *callee_info,
+ vec<int> operand_map,
+ vec<int> offset_map,
+ clause_t possible_truths,
+ const predicate &toplev_predicate)
+{
+ int i;
+ predicate out = true;
+
+ /* True predicate is easy. */
+ if (*this == true)
+ return toplev_predicate;
+ for (i = 0; m_clause[i]; i++)
+ {
+ clause_t clause = m_clause[i];
+ int cond;
+ 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))
+ {
+ predicate cond_predicate;
+ /* Work out if the condition can translate to predicate in the
+ inlined function. */
+ if (cond >= predicate::first_dynamic_condition)
+ {
+ struct condition *c;
+
+ c = &(*callee_info->conds)[cond
+ -
+ predicate::first_dynamic_condition];
+ /* 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,
+ operand_map[c->operand_num],
+ c->size, &ap, c->code,
+ c->val);
+ }
+ }
+ /* Fixed conditions remains same, construct single
+ condition predicate. */
+ else
+ cond_predicate = 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
+predicate::stream_in (struct 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
+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, 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. */
+
+predicate
+add_condition (struct inline_summary *summary, int operand_num,
+ HOST_WIDE_INT size, struct agg_position_info *aggpos,
+ enum tree_code code, tree val)
+{
+ int i;
+ struct condition *c;
+ struct condition new_cond;
+ HOST_WIDE_INT offset;
+ bool agg_contents, by_ref;
+
+ 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->size == size
+ && c->code == code
+ && c->val == val
+ && c->agg_contents == agg_contents
+ && (!agg_contents || (c->offset == offset && c->by_ref == by_ref)))
+ return predicate::predicate_testing_cond (i);
+ }
+ /* Too many conditions. Give up and return constant true. */
+ if (i == predicate::num_conditions - predicate::first_dynamic_condition)
+ return true;
+
+ new_cond.operand_num = operand_num;
+ new_cond.code = code;
+ new_cond.val = val;
+ new_cond.agg_contents = agg_contents;
+ new_cond.by_ref = by_ref;
+ new_cond.offset = offset;
+ new_cond.size = size;
+ vec_safe_push (summary->conds, new_cond);
+
+ return predicate::predicate_testing_cond (i);
+}