/* Support routines for Value Range Propagation (VRP).
- Copyright (C) 2005-2020 Free Software Foundation, Inc.
+ Copyright (C) 2005-2021 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree-cfgcleanup.h"
#include "vr-values.h"
#include "gimple-ssa-evrp-analyze.h"
+#include "gimple-range.h"
+#include "fold-const.h"
+#include "value-pointer-equiv.h"
+
+// This is the classic EVRP folder which uses a dominator walk and pushes
+// ranges into the next block if it is a single predecessor block.
class evrp_folder : public substitute_and_fold_engine
{
public:
- evrp_folder () : m_range_analyzer (/*update_global_ranges=*/true),
- m_vr_values (m_range_analyzer.get_vr_values ())
- {
- }
+ evrp_folder () :
+ substitute_and_fold_engine (),
+ m_range_analyzer (/*update_global_ranges=*/true),
+ simplifier (&m_range_analyzer)
+ { }
~evrp_folder ()
{
- m_vr_values->cleanup_edges_and_switches ();
-
if (dump_file)
{
fprintf (dump_file, "\nValue ranges after Early VRP:\n\n");
- m_range_analyzer.dump_all_value_ranges (dump_file);
+ m_range_analyzer.dump (dump_file);
fprintf (dump_file, "\n");
}
}
- tree get_value (tree op, gimple *stmt ATTRIBUTE_UNUSED) OVERRIDE
+ tree value_of_expr (tree name, gimple *stmt) OVERRIDE
{
- return m_vr_values->op_with_constant_singleton_value_range (op);
+ return m_range_analyzer.value_of_expr (name, stmt);
}
void pre_fold_bb (basic_block bb) OVERRIDE
bool fold_stmt (gimple_stmt_iterator *gsi) OVERRIDE
{
- return m_vr_values->simplify_stmt_using_ranges (gsi);
+ return simplifier.simplify (gsi);
}
void post_fold_bb (basic_block bb) OVERRIDE
void post_new_stmt (gimple *stmt) OVERRIDE
{
- m_vr_values->set_defs_to_varying (stmt);
+ m_range_analyzer.set_defs_to_varying (stmt);
}
-private:
+protected:
DISABLE_COPY_AND_ASSIGN (evrp_folder);
- class evrp_range_analyzer m_range_analyzer;
- class vr_values *m_vr_values;
+ evrp_range_analyzer m_range_analyzer;
+ simplify_using_ranges simplifier;
+};
+
+// This is a ranger based folder which continues to use the dominator
+// walk to access the substitute and fold machinery. Ranges are calculated
+// on demand.
+
+class rvrp_folder : public substitute_and_fold_engine
+{
+public:
+
+ rvrp_folder () : substitute_and_fold_engine (), m_simplifier ()
+ {
+ m_ranger = enable_ranger (cfun);
+ m_simplifier.set_range_query (m_ranger);
+ m_pta = new pointer_equiv_analyzer (m_ranger);
+ }
+
+ ~rvrp_folder ()
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ m_ranger->dump (dump_file);
+
+ m_ranger->export_global_ranges ();
+ disable_ranger (cfun);
+ delete m_pta;
+ }
+
+ tree value_of_expr (tree name, gimple *s = NULL) OVERRIDE
+ {
+ tree ret = m_ranger->value_of_expr (name, s);
+ if (!ret && supported_pointer_equiv_p (name))
+ ret = m_pta->get_equiv (name);
+ return ret;
+ }
+
+ tree value_on_edge (edge e, tree name) OVERRIDE
+ {
+ tree ret = m_ranger->value_on_edge (e, name);
+ if (!ret && supported_pointer_equiv_p (name))
+ ret = m_pta->get_equiv (name);
+ return ret;
+ }
+
+ tree value_of_stmt (gimple *s, tree name = NULL) OVERRIDE
+ {
+ return m_ranger->value_of_stmt (s, name);
+ }
+
+ void pre_fold_bb (basic_block bb) OVERRIDE
+ {
+ m_pta->enter (bb);
+ }
+
+ void post_fold_bb (basic_block bb) OVERRIDE
+ {
+ m_pta->leave (bb);
+ }
+
+ void pre_fold_stmt (gimple *stmt) OVERRIDE
+ {
+ m_pta->visit_stmt (stmt);
+ }
+
+ bool fold_stmt (gimple_stmt_iterator *gsi) OVERRIDE
+ {
+ return m_simplifier.simplify (gsi);
+ }
+
+private:
+ DISABLE_COPY_AND_ASSIGN (rvrp_folder);
+ gimple_ranger *m_ranger;
+ simplify_using_ranges m_simplifier;
+ pointer_equiv_analyzer *m_pta;
+};
+
+// In a hybrid folder, start with an EVRP folder, and add the required
+// fold_stmt bits to either try the ranger first or second.
+//
+// The 3 value_* routines will always query both EVRP and the ranger for
+// a result, and ensure they return the same value. If either returns a value
+// when the other doesn't, it is flagged in the listing, and the discoverd
+// value is returned.
+//
+// The simplifier is unable to process 2 different sources, thus we try to
+// use one engine, and if it fails to simplify, try using the other engine.
+// It is reported when the first attempt fails and the second succeeds.
+
+class hybrid_folder : public evrp_folder
+{
+public:
+ hybrid_folder (bool evrp_first)
+ {
+ m_ranger = enable_ranger (cfun);
+
+ if (evrp_first)
+ {
+ first = &m_range_analyzer;
+ second = m_ranger;
+ }
+ else
+ {
+ first = m_ranger;
+ second = &m_range_analyzer;
+ }
+ m_pta = new pointer_equiv_analyzer (m_ranger);
+ }
+
+ ~hybrid_folder ()
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ m_ranger->dump (dump_file);
+
+ m_ranger->export_global_ranges ();
+ disable_ranger (cfun);
+ delete m_pta;
+ }
+
+ bool fold_stmt (gimple_stmt_iterator *gsi) OVERRIDE
+ {
+ simplifier.set_range_query (first);
+ if (simplifier.simplify (gsi))
+ return true;
+
+ simplifier.set_range_query (second);
+ if (simplifier.simplify (gsi))
+ {
+ if (dump_file)
+ fprintf (dump_file, "EVRP:hybrid: Second query simplifed stmt\n");
+ return true;
+ }
+ return false;
+ }
+
+ void pre_fold_stmt (gimple *stmt) OVERRIDE
+ {
+ evrp_folder::pre_fold_stmt (stmt);
+ m_pta->visit_stmt (stmt);
+ }
+
+ void pre_fold_bb (basic_block bb) OVERRIDE
+ {
+ evrp_folder::pre_fold_bb (bb);
+ m_pta->enter (bb);
+ }
+
+ void post_fold_bb (basic_block bb) OVERRIDE
+ {
+ evrp_folder::post_fold_bb (bb);
+ m_pta->leave (bb);
+ }
+
+ tree value_of_expr (tree name, gimple *) OVERRIDE;
+ tree value_on_edge (edge, tree name) OVERRIDE;
+ tree value_of_stmt (gimple *, tree name) OVERRIDE;
+
+private:
+ DISABLE_COPY_AND_ASSIGN (hybrid_folder);
+ gimple_ranger *m_ranger;
+ range_query *first;
+ range_query *second;
+ pointer_equiv_analyzer *m_pta;
+ tree choose_value (tree evrp_val, tree ranger_val);
};
+
+tree
+hybrid_folder::value_of_expr (tree op, gimple *stmt)
+{
+ tree evrp_ret = evrp_folder::value_of_expr (op, stmt);
+ tree ranger_ret = m_ranger->value_of_expr (op, stmt);
+ if (!ranger_ret && supported_pointer_equiv_p (op))
+ ranger_ret = m_pta->get_equiv (op);
+ return choose_value (evrp_ret, ranger_ret);
+}
+
+tree
+hybrid_folder::value_on_edge (edge e, tree op)
+{
+ // Call evrp::value_of_expr directly. Otherwise another dual call is made
+ // via hybrid_folder::value_of_expr, but without an edge.
+ tree evrp_ret = evrp_folder::value_of_expr (op, NULL);
+ tree ranger_ret = m_ranger->value_on_edge (e, op);
+ if (!ranger_ret && supported_pointer_equiv_p (op))
+ ranger_ret = m_pta->get_equiv (op);
+ return choose_value (evrp_ret, ranger_ret);
+}
+
+tree
+hybrid_folder::value_of_stmt (gimple *stmt, tree op)
+{
+ // Call evrp::value_of_expr directly. Otherwise another dual call is made
+ // via hybrid_folder::value_of_expr, but without a stmt.
+ tree evrp_ret;
+ if (op)
+ evrp_ret = evrp_folder::value_of_expr (op, NULL);
+ else
+ evrp_ret = NULL_TREE;
+
+ tree ranger_ret = m_ranger->value_of_stmt (stmt, op);
+ return choose_value (evrp_ret, ranger_ret);
+}
+
+// Given trees returned by EVRP and Ranger, choose/report the value to use
+// by the folder.
+
+tree
+hybrid_folder::choose_value (tree evrp_val, tree ranger_val)
+{
+ // If both found the same value, just return it.
+ if (evrp_val && ranger_val && !compare_values (evrp_val, ranger_val))
+ return evrp_val;
+
+ // If neither returned a value, return NULL_TREE.
+ if (!ranger_val && !evrp_val)
+ return NULL_TREE;
+
+ // Otherwise there is a discrepancy to flag.
+ if (dump_file)
+ {
+ if (evrp_val && ranger_val)
+ fprintf (dump_file, "EVRP:hybrid: Disagreement\n");
+ if (evrp_val)
+ {
+ fprintf (dump_file, "EVRP:hybrid: EVRP found singleton ");
+ print_generic_expr (dump_file, evrp_val);
+ fprintf (dump_file, "\n");
+ }
+ if (ranger_val)
+ {
+ fprintf (dump_file, "EVRP:hybrid: RVRP found singleton ");
+ print_generic_expr (dump_file, ranger_val);
+ fprintf (dump_file, "\n");
+ }
+ }
+
+ // If one value was found, return it.
+ if (!evrp_val)
+ return ranger_val;
+ if (!ranger_val)
+ return evrp_val;
+
+ // If values are different, return the first calculated value.
+ if ((param_evrp_mode & EVRP_MODE_RVRP_FIRST) == EVRP_MODE_RVRP_FIRST)
+ return ranger_val;
+ return evrp_val;
+}
+
/* Main entry point for the early vrp pass which is a simplified non-iterative
version of vrp where basic blocks are visited in dominance order. Value
ranges discovered in early vrp will also be used by ipa-vrp. */
scev_initialize ();
calculate_dominance_info (CDI_DOMINATORS);
- evrp_folder folder;
- folder.substitute_and_fold ();
+ // Only the last 2 bits matter for choosing the folder.
+ switch (param_evrp_mode & EVRP_MODE_RVRP_FIRST)
+ {
+ case EVRP_MODE_EVRP_ONLY:
+ {
+ evrp_folder folder;
+ folder.substitute_and_fold ();
+ break;
+ }
+ case EVRP_MODE_RVRP_ONLY:
+ {
+ rvrp_folder folder;
+ folder.substitute_and_fold ();
+ break;
+ }
+ case EVRP_MODE_EVRP_FIRST:
+ {
+ hybrid_folder folder (true);
+ folder.substitute_and_fold ();
+ break;
+ }
+ case EVRP_MODE_RVRP_FIRST:
+ {
+ hybrid_folder folder (false);
+ folder.substitute_and_fold ();
+ break;
+ }
+ default:
+ gcc_unreachable ();
+ }
scev_finalize ();
loop_optimizer_finalize ();