diff options
author | Andrew Macleod <amacleod@gcc.gnu.org> | 2018-04-03 14:56:54 +0000 |
---|---|---|
committer | Andrew Macleod <amacleod@gcc.gnu.org> | 2018-04-03 14:56:54 +0000 |
commit | 55af7b3530fa80054640f71c0d122a9fed7fe47a (patch) | |
tree | 789ed72dd7bd0b21d9029cc7bd0045bfddf01bba | |
parent | 7ce6c8b321a7d6243a2bdecc4de7c64fca4f211a (diff) |
Check in initial version of Early Ranger VRP
disable with -fno-rvrp
From-SVN: r259033
-rw-r--r-- | gcc/common.opt | 5 | ||||
-rw-r--r-- | gcc/gimple-ssa-evrp.c | 174 | ||||
-rw-r--r-- | gcc/passes.c | 12 | ||||
-rw-r--r-- | gcc/passes.def | 1 | ||||
-rw-r--r-- | gcc/range-op.c | 16 | ||||
-rw-r--r-- | gcc/ssa-range-global.c | 15 | ||||
-rw-r--r-- | gcc/ssa-range-global.h | 1 | ||||
-rw-r--r-- | gcc/ssa-range-stmt.c | 18 | ||||
-rw-r--r-- | gcc/ssa-range.c | 189 | ||||
-rw-r--r-- | gcc/ssa-range.h | 7 | ||||
-rw-r--r-- | gcc/timevar.def | 1 | ||||
-rw-r--r-- | gcc/tree-pass.h | 1 |
12 files changed, 386 insertions, 54 deletions
diff --git a/gcc/common.opt b/gcc/common.opt index e0bc4d1bb18..c2a82aa83e3 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2646,6 +2646,11 @@ ftree-vrp Common Report Var(flag_tree_vrp) Init(0) Optimization Perform Value Range Propagation on trees. +ftree-rvrp +Common Report Var(flag_tree_rvrp) Init(1) Optimization +Perform New Ranger Value Range Propagation on trees. + + fsplit-paths Common Report Var(flag_split_paths) Init(0) Optimization Split paths leading to loop backedges. diff --git a/gcc/gimple-ssa-evrp.c b/gcc/gimple-ssa-evrp.c index c9bbf09a406..11d53ce8e11 100644 --- a/gcc/gimple-ssa-evrp.c +++ b/gcc/gimple-ssa-evrp.c @@ -41,6 +41,9 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfgcleanup.h" #include "vr-values.h" #include "gimple-ssa-evrp-analyze.h" +#include "ssa-range.h" +#include "ssa-range-global.h" + class evrp_folder : public substitute_and_fold_engine { @@ -347,3 +350,174 @@ make_pass_early_vrp (gcc::context *ctxt) return new pass_early_vrp (ctxt); } + +/* Main entry point for the early Ranger vrp pass. */ + +static bool +argument_ok_to_propagate (tree name) +{ + if (TREE_CODE (name) != SSA_NAME) + return true; + if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name)) + { + // From may_propagate_copy + if (SSA_NAME_IS_DEFAULT_DEF (name) && (SSA_NAME_VAR (name) == NULL_TREE + || TREE_CODE (SSA_NAME_VAR (name)) == VAR_DECL)) + return true; + } + // probably dont need this. + if (virtual_operand_p (name)) + return false; + return true; +} + +static unsigned int +execute_ranger_vrp () +{ + path_ranger ranger; + + basic_block bb; + irange r; + wide_int i; + unsigned x; + bitmap_iterator bi; + bitmap touched = BITMAP_ALLOC (NULL); + + FOR_EACH_BB_FN (bb, cfun) + { + gcond *cond; + gimple *stmt = last_stmt (bb); + if (stmt && (cond = dyn_cast <gcond *> (stmt))) + { + if (dump_file) + { + fprintf (dump_file, "Considering "); + print_gimple_stmt (dump_file, cond, 0,0); + } + if (ranger.path_range_stmt (r, stmt)) + { + if (dump_file) + { + fprintf (dump_file, "Found a range for the expression: "); + r.dump (dump_file); + fprintf (dump_file, "\n"); + } + + if (r.singleton_p (i)) + { + if (!argument_ok_to_propagate (gimple_cond_lhs (cond)) || + !argument_ok_to_propagate (gimple_cond_rhs (cond))) + { + if (dump_file) + { + fprintf (dump_file, "Found BB%d branch",bb->index); + print_gimple_stmt (dump_file, stmt, 0, 0); + fprintf (dump_file, "cannot eliminate. proprules.\n"); + fprintf (stderr,"\n *********************** caught one\n"); + } + continue; + } + + /* If either operand is an ssa_name, set the touched bit for + potential removal later if no uses are left. */ + tree t = valid_irange_ssa (gimple_cond_lhs (cond)); + if (t) + bitmap_set_bit (touched, SSA_NAME_VERSION (t)); + t = valid_irange_ssa (gimple_cond_rhs (cond)); + if (t) + bitmap_set_bit (touched, SSA_NAME_VERSION (t)); + + if (dump_file) + { + fprintf (dump_file, "eliminating BB%d branch",bb->index); + print_gimple_stmt (dump_file, stmt, 0, 0); + } + + /* Rewrite the condition to either true or false. */ + if (wi::eq_p (i, 0)) + gimple_cond_make_false (cond); + else + gimple_cond_make_true (cond); + + if (dump_file) + { + fprintf (dump_file, "Re-written to: \n"); + print_gimple_stmt (dump_file, cond, 0, 0); + fprintf (dump_file, "\n"); + } + } + } + } + + } + + // Now visit each name that was rewritten and see if the definiing statement + // can be deleted due to no more uses in the program + EXECUTE_IF_SET_IN_BITMAP (touched, 0, x, bi) + { + tree name = ssa_name (x); + gimple *s = SSA_NAME_DEF_STMT (name); + + if (s) + { + if (has_zero_uses (name)) + { + if (dump_file) + { + fprintf (dump_file, "Should delete"); + print_gimple_stmt (dump_file, s, 0, 0); + } + } + else + { + if (dump_file) + { + fprintf (dump_file, "Still has uses, Could not delete "); + print_gimple_stmt (dump_file, s, 0, 0); + } + } + } + } + return 0; +} + +namespace { + +const pass_data pass_data_ranger_vrp = +{ + GIMPLE_PASS, /* type */ + "rvrp", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_TREE_RANGER_VRP, /* tv_id */ + PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + ( TODO_cleanup_cfg | TODO_verify_all ), +}; + +class pass_ranger_vrp : public gimple_opt_pass +{ +public: + pass_ranger_vrp (gcc::context *ctxt) + : gimple_opt_pass (pass_data_ranger_vrp, ctxt) + {} + + /* opt_pass methods: */ + opt_pass * clone () { return new pass_ranger_vrp (m_ctxt); } + virtual bool gate (function *) + { + return flag_tree_rvrp != 0; + } + virtual unsigned int execute (function *) + { return execute_ranger_vrp (); } + +}; // class pass_vrp +} // anon namespace + +gimple_opt_pass * +make_pass_ranger_vrp (gcc::context *ctxt) +{ + return new pass_ranger_vrp (ctxt); +} + diff --git a/gcc/passes.c b/gcc/passes.c index c09cd921306..6d26da6c848 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -2494,6 +2494,12 @@ execute_one_pass (opt_pass *pass) do_per_function (verify_curr_properties, (void *)(size_t)pass->properties_required); + if (pass->name && !strcmp (pass->name, "rvrp")) + { + path_ranger r; + r.exercise (dump_file); + } + /* Do it! */ todo_after = pass->execute (cfun); @@ -2558,12 +2564,6 @@ execute_one_pass (opt_pass *pass) if (!current_function_decl) symtab->process_new_functions (); - if (pass->name && !strcmp (pass->name, "ssa")) - { - path_ranger r; - r.exercise (dump_file); - } - pass_fini_dump_file (pass); if (pass->type != SIMPLE_IPA_PASS && pass->type != IPA_PASS) diff --git a/gcc/passes.def b/gcc/passes.def index 3ebcfc30349..7f2b7ea76ed 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -93,6 +93,7 @@ along with GCC; see the file COPYING3. If not see execute TODO_rebuild_alias at this point. */ NEXT_PASS (pass_build_ealias); NEXT_PASS (pass_fre); + NEXT_PASS (pass_ranger_vrp); NEXT_PASS (pass_early_vrp); NEXT_PASS (pass_merge_phi); NEXT_PASS (pass_dse); diff --git a/gcc/range-op.c b/gcc/range-op.c index 61917274296..9837710db01 100644 --- a/gcc/range-op.c +++ b/gcc/range-op.c @@ -1011,6 +1011,14 @@ bool operator_multiply::op1_irange (irange& r, const irange& lhs, const irange& op2) const { + wide_int c; + // For overflow types. all we know is that the lower "precsion / 2" bits + // match up with the range of the LHS. See if we know anything about + // the zero bits. + if (lhs.singleton_p (c) && TYPE_OVERFLOW_WRAPS (lhs.get_type ())) + { + return false; + } return op_rr (OPM_DIV, r, lhs, op2); } @@ -1018,6 +1026,14 @@ bool operator_multiply::op2_irange (irange& r, const irange& lhs, const irange& op1) const { + wide_int c; + // For overflow types. all we know is that the lower "precsion / 2" bits + // match up with the range of the LHS. See if we know anything about + // the zero bits. + if (lhs.singleton_p (c) && TYPE_OVERFLOW_WRAPS (lhs.get_type ())) + { + return false; + } return op_rr (OPM_DIV, r, lhs, op1); } diff --git a/gcc/ssa-range-global.c b/gcc/ssa-range-global.c index d477be34b19..12a78f4c18b 100644 --- a/gcc/ssa-range-global.c +++ b/gcc/ssa-range-global.c @@ -59,6 +59,7 @@ public: ~ssa_global_cache (); bool get_global_range (irange& r, tree name) const; void set_global_range (tree name, const irange&r); + void clear_global_range (tree name); void clear (); void copy_to_range_info (); void dump (FILE *f = stderr); @@ -112,6 +113,13 @@ set_global_ssa_range (tree name, const irange&r) globals->set_global_range (name, r); } +/* Clear the global range for NAME. */ +void +clear_global_ssa_range (tree name) +{ + gcc_assert (globals); + globals->clear_global_range (name); +} // --------------------------------------------------------------- /* Initialize a global cache. */ @@ -157,6 +165,13 @@ ssa_global_cache::set_global_range (tree name, const irange& r) } } +/* Set the range for NAME to R in the glonbal cache. */ +void +ssa_global_cache::clear_global_range (tree name) +{ + tab[SSA_NAME_VERSION (name)] = NULL; +} + /* Clear the global cache. */ void ssa_global_cache::clear () diff --git a/gcc/ssa-range-global.h b/gcc/ssa-range-global.h index 95e94e39163..69accee2b83 100644 --- a/gcc/ssa-range-global.h +++ b/gcc/ssa-range-global.h @@ -27,5 +27,6 @@ void dump_global_ssa_range_cache (FILE *f); bool get_global_ssa_range (irange& r, tree name); void set_global_ssa_range (tree name, const irange&r); +void clear_global_ssa_range (tree name); #endif /* GCC_SSA_RANGE_GLOBAL_H */ diff --git a/gcc/ssa-range-stmt.c b/gcc/ssa-range-stmt.c index 606b1af6569..181a4b560a1 100644 --- a/gcc/ssa-range-stmt.c +++ b/gcc/ssa-range-stmt.c @@ -147,11 +147,22 @@ range_stmt::fold (irange &res, const irange& r1) const irange_operator *handler = irange_op_handler (code); gcc_assert (handler != NULL); + // Empty ranges are viral. + if (r1.empty_p ()) + { + if (lhs) + res.clear (TREE_TYPE (lhs)); + else + res.clear (r1.get_type ()); + return true; + } + /* Single ssa operations require the LHS type as the second range. */ if (lhs) r2.set_range_for_type (TREE_TYPE (lhs)); else r2.clear (); + return handler->fold_range (res, r1, r2); } @@ -165,6 +176,13 @@ range_stmt::fold (irange &res, const irange& r1, const irange& r2) const irange_operator *handler = irange_op_handler (code); gcc_assert (handler != NULL); + // Empty ranges are viral. + if (r1.empty_p () || r2.empty_p ()) + { + res.clear (r1.get_type ()); + return true; + } + // Make sure this isnt a unary operation being passed a second range. gcc_assert (op2); return handler->fold_range (res, r1, r2); diff --git a/gcc/ssa-range.c b/gcc/ssa-range.c index ceeb9cc903f..c185a9c51a6 100644 --- a/gcc/ssa-range.c +++ b/gcc/ssa-range.c @@ -253,16 +253,15 @@ bool path_ranger::path_range_entry (irange& r, tree name, basic_block bb) { gimple *def_stmt; - basic_block def_bb; + basic_block def_bb = NULL; if (!valid_irange_ssa (name)) return false; def_stmt = SSA_NAME_DEF_STMT (name); - if (!def_stmt) - return false; + if (def_stmt) + def_bb = gimple_bb (def_stmt);; - def_bb = gimple_bb (def_stmt);; if (!def_bb) def_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); @@ -291,18 +290,22 @@ path_ranger::path_range_edge (irange& r, tree name, edge e) if (!valid_irange_ssa (name)) return false; + /* Get an initial range for NAME. */ + gimple *stmt = SSA_NAME_DEF_STMT (name); if (stmt && gimple_bb (stmt) == e->src) { - if (!path_range_of_def (r, stmt)) - get_global_ssa_range (r, name); + /* If it is in this block, evaluate it. */ + if (!path_range_stmt (r, stmt)) + return false; } else - /* Get the range for the def and possible basic block. */ + /* The name comes from outside this block, so evaluate it's value on + entry to the block. */ if (!path_range_entry (r, name, bb)) - return false; + error (" Why can't we get a live on entry range? "); - /* Now intersect it with what we know about this edge. */ + /* Now intersect it with what NAME evaluates to on this edge. */ irange edge_range; if (range_on_edge (edge_range, name, e)) { @@ -333,7 +336,7 @@ path_ranger::determine_block (tree name, basic_block bb, basic_block def_bb) /* Avoid infinite recursion by marking this block as calculated. */ block_cache->get_block_ranges (name).set_bb_range_for_type (bb); - /* Visit each predecessor to reseolve them. */ + /* Visit each predecessor to resolve them. */ FOR_EACH_EDGE (e, ei, bb->preds) { determine_block (name, e->src, def_bb); @@ -345,7 +348,7 @@ path_ranger::determine_block (tree name, basic_block bb, basic_block def_bb) { irange pred_range; basic_block src = e->src; - // Should be using range_on_def + // Should be using range_on_def??? or something. if (src == def_bb) get_global_ssa_range (pred_range, name); else @@ -374,58 +377,146 @@ path_ranger::determine_block (tree name, basic_block bb, basic_block def_bb) block_cache->get_block_ranges (name).set_bb_range (bb, block_result); } + +bool +path_ranger::process_phi (irange &r, gphi *phi) +{ + tree phi_def = gimple_phi_result (phi); + unsigned x; + + if (!valid_irange_ssa (phi_def)) + return false; + + // If this node has already been processed, just return it. + if (get_global_ssa_range (r, phi_def)) + return true; + + // Avoid infinite recursion by initializing global cache + r.set_range (phi_def); + set_global_ssa_range (phi_def, r); + + // And start with an empty range, unioning in each areument's range. + r.clear (TREE_TYPE (phi_def)); + for (x = 0; x < gimple_phi_num_args (phi); x++) + { + irange arg_range; + tree arg = gimple_phi_arg_def (phi, x); + edge e = gimple_phi_arg_edge (phi, x); + if (!path_range_edge (arg_range, arg, e)) + if (!get_operand_range (arg_range, arg)) + return false; + + normalize_bool_type (r, arg_range); + r.union_ (arg_range); + if (r.range_for_type_p ()) + break; + } + + set_global_ssa_range (phi_def, r); + return true; +} + bool path_ranger::path_range_stmt (irange& r, tree name, gimple *g) { -#if 0 + return path_get_operand (r, name, gimple_bb (g)); +} + + +bool +path_ranger::path_range_stmt (irange& r, gimple *g) +{ + tree name = gimple_get_lhs (g); + irange range_op1, range_op2; + range_stmt rn; + basic_block bb = gimple_bb (g); + if (is_a <gphi *> (g)) { gphi *phi = as_a <gphi *> (g); - tree phi_def = gimple_phi_result (phi); - irange tmp; - unsigned x; + return process_phi (r, phi); + } + + // Not all statements have a LHS. */ + if (name) + { + // If this STMT has already been processed, return that value. + if (get_global_ssa_range (r, name)) + return true; + + // avoid infinite recursion by initializing global cache + r.set_range (name); + set_global_ssa_range (name, r); + } - /* Only calculate ranges for PHI defs. */ - if (phi_def != name) - return false; + rn = g; + if (!rn.valid ()) + return false; - r.clear (TREE_TYPE (name)); + if (!path_get_operand (range_op1, rn.operand1 (), bb)) + return false; + + // If this is a unary operation, call fold now. + if (!rn.operand2 ()) + return rn.fold (r, range_op1); - for (x = 0; x < gimple_phi_num_args (phi); x++) - { - bool res; - tree arg = gimple_phi_arg_def (phi, x); - if (TREE_CODE (arg) == SSA_NAME) - res = path_range_edge (tmp, arg, gimple_phi_arg_edge (phi, x)); - else - res = get_operand_range (tmp, arg); - if (res) - r.union_ (tmp); - else - { - r.set_range_for_type (name); - return false; - } - if (r.range_for_type_p ()) - return true; - } - return true; + if (!path_get_operand (range_op2, rn.operand2 (), bb)) + return false; + + normalize_bool_type (range_op1, range_op2); + bool res = rn.fold (r, range_op1, range_op2); + if (name) + { + if (res) + set_global_ssa_range (name, r); + else + clear_global_ssa_range (name); } -#endif - return range_on_stmt (r, name, g); + return res; } -static inline bool +static inline gimple * ssa_name_same_bb_p (tree name, basic_block bb) { gimple *g = SSA_NAME_DEF_STMT (name); if (!g || gimple_bb (g) != bb) - return false; - return true; + return NULL; + return g; } + +/* Determine a range for NAME in basic block BB. + If FULL is true, then rescursively call the path ranger to fully evaluate + a range for NAME. + if FULL is false, then dont go outside the current block to find a best + guess range. + if edge E is specified, then only follow E for calculations. */ + +bool +path_ranger::path_get_operand (irange &r, tree name, basic_block bb) +{ + if (!valid_irange_ssa (name)) + return get_operand_range (r, name); + + // check if the defining statement is in the same block. + gimple *s = ssa_name_same_bb_p (name, bb); + if (s) + { + // This means NAME is defined in the same block, simply try to extract + // a range from that statement. + if (!path_range_stmt (r, s)) + return get_operand_range (r, name); + return true; + } + + if (path_range_entry (r, name, bb)) + return true; + return get_operand_range (r, name); +} + + bool path_ranger::path_fold_stmt (irange &r, range_stmt &rn, basic_block bb, edge e) { @@ -482,8 +573,7 @@ path_ranger::path_range_of_def (irange &r, gimple *g, edge e) range_stmt rn(g); if (!rn.valid()) - return false; - + return false; return path_fold_stmt (r, rn, bb, e); } @@ -494,7 +584,7 @@ path_ranger::path_range_of_def (irange &r, gimple *g, edge e) bool path_ranger::path_range_of_def (irange &r, gimple *g) { - tree name = gimple_get_lhs (g); + tree name; basic_block bb = gimple_bb (g); tree arg; irange range_op1, range_op2; @@ -517,7 +607,7 @@ path_ranger::path_range_of_def (irange &r, gimple *g) if (get_global_ssa_range (r, phi_def)) return true; - // avoid infinite recursion by initializing global cache + // Avoid infinite recursion by initializing global cache r.set_range (phi_def); set_global_ssa_range (phi_def, r); @@ -545,7 +635,10 @@ path_ranger::path_range_of_def (irange &r, gimple *g) return false; name = gimple_get_lhs (g); - gcc_checking_assert (name); + + /* If there is no LHS, then we are simply folding an expression. */ + if (!name) + return path_fold_stmt (r, rn, bb); if (get_global_ssa_range (r, name)) return true; diff --git a/gcc/ssa-range.h b/gcc/ssa-range.h index 1ae47d3a2b7..98fb5801376 100644 --- a/gcc/ssa-range.h +++ b/gcc/ssa-range.h @@ -35,6 +35,9 @@ private: bool path_range_reverse (irange &r, tree name, const vec<basic_block> &); bool path_fold_stmt (irange &r, range_stmt &rn, basic_block bb, edge e = NULL); + bool process_phi (irange &r, gphi *phi); + bool path_get_operand (irange &r, tree name, basic_block bb); + public: enum path_range_direction { FORWARD, REVERSE }; path_ranger (); @@ -43,7 +46,11 @@ public: /* What is the known range of name from its DEF point to edge E. */ bool path_range_edge (irange& r, tree name, edge e); bool path_range_entry (irange& r, tree name, basic_block bb); + // Specifying NAME give the range of NAME before G is executed. bool path_range_stmt (irange& r, tree name, gimple *g); + // no NAME, give the range of the LHS of the statement. + bool path_range_stmt (irange& r, gimple *g); + bool path_range (irange &r, tree name, const vec<basic_block> &bbs, enum path_range_direction, edge start_edge = NULL); // Evaluate expression within a BB as much as possible. diff --git a/gcc/timevar.def b/gcc/timevar.def index 91221ae5b0e..d1a72ecd38d 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -152,6 +152,7 @@ DEFTIMEVAR (TV_TREE_CLEANUP_CFG , "tree CFG cleanup") DEFTIMEVAR (TV_TREE_TAIL_MERGE , "tree tail merge") DEFTIMEVAR (TV_TREE_VRP , "tree VRP") DEFTIMEVAR (TV_TREE_EARLY_VRP , "tree Early VRP") +DEFTIMEVAR (TV_TREE_RANGER_VRP , "tree Ranger VRP") DEFTIMEVAR (TV_TREE_COPY_PROP , "tree copy propagation") DEFTIMEVAR (TV_FIND_REFERENCED_VARS , "tree find ref. vars") DEFTIMEVAR (TV_TREE_PTA , "tree PTA") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 93a6a99eb7a..2f06a83c795 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -450,6 +450,7 @@ extern gimple_opt_pass *make_pass_check_data_deps (gcc::context *ctxt); extern gimple_opt_pass *make_pass_copy_prop (gcc::context *ctxt); extern gimple_opt_pass *make_pass_isolate_erroneous_paths (gcc::context *ctxt); extern gimple_opt_pass *make_pass_early_vrp (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_ranger_vrp (gcc::context *ctxt); extern gimple_opt_pass *make_pass_vrp (gcc::context *ctxt); extern gimple_opt_pass *make_pass_uncprop (gcc::context *ctxt); extern gimple_opt_pass *make_pass_return_slot (gcc::context *ctxt); |