| /* Support routines for Value Range Propagation (VRP). |
| Copyright (C) 2005-2019 Free Software Foundation, Inc. |
| |
| 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 "gimple.h" |
| #include "tree-pass.h" |
| #include "ssa.h" |
| #include "gimple-pretty-print.h" |
| #include "cfganal.h" |
| #include "gimple-fold.h" |
| #include "tree-eh.h" |
| #include "gimple-iterator.h" |
| #include "tree-cfg.h" |
| #include "tree-ssa-loop-manip.h" |
| #include "tree-ssa-loop.h" |
| #include "cfgloop.h" |
| #include "tree-scalar-evolution.h" |
| #include "tree-ssa-propagate.h" |
| #include "alloc-pool.h" |
| #include "domwalk.h" |
| #include "tree-cfgcleanup.h" |
| #include "vr-values.h" |
| #include "gimple-ssa-evrp-analyze.h" |
| |
| class evrp_folder : public substitute_and_fold_engine |
| { |
| public: |
| tree get_value (tree) FINAL OVERRIDE; |
| evrp_folder (class vr_values *vr_values_) : vr_values (vr_values_) { } |
| bool simplify_stmt_using_ranges (gimple_stmt_iterator *gsi) |
| { return vr_values->simplify_stmt_using_ranges (gsi); } |
| class vr_values *vr_values; |
| |
| private: |
| DISABLE_COPY_AND_ASSIGN (evrp_folder); |
| }; |
| |
| tree |
| evrp_folder::get_value (tree op) |
| { |
| return vr_values->op_with_constant_singleton_value_range (op); |
| } |
| |
| /* evrp_dom_walker visits the basic blocks in the dominance order and set |
| the Value Ranges (VR) for SSA_NAMEs in the scope. Use this VR to |
| discover more VRs. */ |
| |
| class evrp_dom_walker : public dom_walker |
| { |
| public: |
| evrp_dom_walker () |
| : dom_walker (CDI_DOMINATORS), |
| evrp_range_analyzer (true), |
| evrp_folder (evrp_range_analyzer.get_vr_values ()) |
| { |
| need_eh_cleanup = BITMAP_ALLOC (NULL); |
| } |
| ~evrp_dom_walker () |
| { |
| BITMAP_FREE (need_eh_cleanup); |
| } |
| virtual edge before_dom_children (basic_block); |
| virtual void after_dom_children (basic_block); |
| void cleanup (void); |
| |
| private: |
| DISABLE_COPY_AND_ASSIGN (evrp_dom_walker); |
| bitmap need_eh_cleanup; |
| auto_vec<gimple *> stmts_to_fixup; |
| auto_vec<gimple *> stmts_to_remove; |
| |
| class evrp_range_analyzer evrp_range_analyzer; |
| class evrp_folder evrp_folder; |
| }; |
| |
| edge |
| evrp_dom_walker::before_dom_children (basic_block bb) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, "Visiting BB%d\n", bb->index); |
| |
| evrp_range_analyzer.enter (bb); |
| |
| for (gphi_iterator gpi = gsi_start_phis (bb); |
| !gsi_end_p (gpi); gsi_next (&gpi)) |
| { |
| gphi *phi = gpi.phi (); |
| tree lhs = PHI_RESULT (phi); |
| if (virtual_operand_p (lhs)) |
| continue; |
| |
| value_range *vr = evrp_range_analyzer.get_value_range (lhs); |
| /* Mark PHIs whose lhs we fully propagate for removal. */ |
| tree val = value_range_constant_singleton (vr); |
| if (val && may_propagate_copy (lhs, val)) |
| { |
| stmts_to_remove.safe_push (phi); |
| continue; |
| } |
| } |
| |
| edge taken_edge = NULL; |
| |
| /* Visit all other stmts and discover any new VRs possible. */ |
| for (gimple_stmt_iterator gsi = gsi_start_bb (bb); |
| !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple *stmt = gsi_stmt (gsi); |
| tree output = NULL_TREE; |
| gimple *old_stmt = stmt; |
| bool was_noreturn = (is_gimple_call (stmt) |
| && gimple_call_noreturn_p (stmt)); |
| |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| { |
| fprintf (dump_file, "Visiting stmt "); |
| print_gimple_stmt (dump_file, stmt, 0); |
| } |
| |
| evrp_range_analyzer.record_ranges_from_stmt (stmt, false); |
| |
| if (gcond *cond = dyn_cast <gcond *> (stmt)) |
| { |
| evrp_range_analyzer.vrp_visit_cond_stmt (cond, &taken_edge); |
| if (taken_edge) |
| { |
| if (taken_edge->flags & EDGE_TRUE_VALUE) |
| gimple_cond_make_true (cond); |
| else if (taken_edge->flags & EDGE_FALSE_VALUE) |
| gimple_cond_make_false (cond); |
| else |
| gcc_unreachable (); |
| update_stmt (stmt); |
| } |
| } |
| else if (stmt_interesting_for_vrp (stmt)) |
| { |
| output = get_output_for_vrp (stmt); |
| if (output) |
| { |
| tree val; |
| value_range *vr = evrp_range_analyzer.get_value_range (output); |
| |
| /* Mark stmts whose output we fully propagate for removal. */ |
| if ((val = value_range_constant_singleton (vr)) |
| && may_propagate_copy (output, val) |
| && !stmt_could_throw_p (cfun, stmt) |
| && !gimple_has_side_effects (stmt)) |
| { |
| stmts_to_remove.safe_push (stmt); |
| continue; |
| } |
| } |
| } |
| |
| /* Try folding stmts with the VR discovered. */ |
| bool did_replace = evrp_folder.replace_uses_in (stmt); |
| if (fold_stmt (&gsi, follow_single_use_edges) |
| || did_replace) |
| { |
| stmt = gsi_stmt (gsi); |
| update_stmt (stmt); |
| did_replace = true; |
| } |
| if (evrp_folder.simplify_stmt_using_ranges (&gsi)) |
| { |
| stmt = gsi_stmt (gsi); |
| update_stmt (stmt); |
| did_replace = true; |
| } |
| |
| if (did_replace) |
| { |
| /* If we cleaned up EH information from the statement, |
| remove EH edges. */ |
| if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt)) |
| bitmap_set_bit (need_eh_cleanup, bb->index); |
| |
| /* If we turned a not noreturn call into a noreturn one |
| schedule it for fixup. */ |
| if (!was_noreturn |
| && is_gimple_call (stmt) |
| && gimple_call_noreturn_p (stmt)) |
| stmts_to_fixup.safe_push (stmt); |
| |
| if (gimple_assign_single_p (stmt)) |
| { |
| tree rhs = gimple_assign_rhs1 (stmt); |
| if (TREE_CODE (rhs) == ADDR_EXPR) |
| recompute_tree_invariant_for_addr_expr (rhs); |
| } |
| } |
| } |
| |
| /* Visit BB successor PHI nodes and replace PHI args. */ |
| edge e; |
| edge_iterator ei; |
| FOR_EACH_EDGE (e, ei, bb->succs) |
| { |
| for (gphi_iterator gpi = gsi_start_phis (e->dest); |
| !gsi_end_p (gpi); gsi_next (&gpi)) |
| { |
| gphi *phi = gpi.phi (); |
| use_operand_p use_p = PHI_ARG_DEF_PTR_FROM_EDGE (phi, e); |
| tree arg = USE_FROM_PTR (use_p); |
| if (TREE_CODE (arg) != SSA_NAME |
| || virtual_operand_p (arg)) |
| continue; |
| value_range *vr = evrp_range_analyzer.get_value_range (arg); |
| tree val = value_range_constant_singleton (vr); |
| if (val && may_propagate_copy (arg, val)) |
| propagate_value (use_p, val); |
| } |
| } |
| |
| return taken_edge; |
| } |
| |
| void |
| evrp_dom_walker::after_dom_children (basic_block bb) |
| { |
| evrp_range_analyzer.leave (bb); |
| } |
| |
| /* Perform any cleanups after the main phase of EVRP has completed. */ |
| |
| void |
| evrp_dom_walker::cleanup (void) |
| { |
| if (dump_file) |
| { |
| fprintf (dump_file, "\nValue ranges after Early VRP:\n\n"); |
| evrp_range_analyzer.dump_all_value_ranges (dump_file); |
| fprintf (dump_file, "\n"); |
| } |
| |
| /* Remove stmts in reverse order to make debug stmt creation possible. */ |
| while (! stmts_to_remove.is_empty ()) |
| { |
| gimple *stmt = stmts_to_remove.pop (); |
| if (dump_file && dump_flags & TDF_DETAILS) |
| { |
| fprintf (dump_file, "Removing dead stmt "); |
| print_gimple_stmt (dump_file, stmt, 0); |
| fprintf (dump_file, "\n"); |
| } |
| gimple_stmt_iterator gsi = gsi_for_stmt (stmt); |
| if (gimple_code (stmt) == GIMPLE_PHI) |
| remove_phi_node (&gsi, true); |
| else |
| { |
| unlink_stmt_vdef (stmt); |
| gsi_remove (&gsi, true); |
| release_defs (stmt); |
| } |
| } |
| |
| if (!bitmap_empty_p (need_eh_cleanup)) |
| gimple_purge_all_dead_eh_edges (need_eh_cleanup); |
| |
| /* Fixup stmts that became noreturn calls. This may require splitting |
| blocks and thus isn't possible during the dominator walk. Do this |
| in reverse order so we don't inadvertedly remove a stmt we want to |
| fixup by visiting a dominating now noreturn call first. */ |
| while (!stmts_to_fixup.is_empty ()) |
| { |
| gimple *stmt = stmts_to_fixup.pop (); |
| fixup_noreturn_call (stmt); |
| } |
| |
| evrp_folder.vr_values->cleanup_edges_and_switches (); |
| } |
| |
| /* 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. */ |
| |
| static unsigned int |
| execute_early_vrp () |
| { |
| /* Ideally this setup code would move into the ctor for the dominator |
| walk. However, this setup can change the number of blocks which |
| invalidates the internal arrays that are set up by the dominator |
| walker. */ |
| loop_optimizer_init (LOOPS_NORMAL | LOOPS_HAVE_RECORDED_EXITS); |
| rewrite_into_loop_closed_ssa (NULL, TODO_update_ssa); |
| scev_initialize (); |
| calculate_dominance_info (CDI_DOMINATORS); |
| |
| /* Walk stmts in dominance order and propagate VRP. */ |
| evrp_dom_walker walker; |
| walker.walk (ENTRY_BLOCK_PTR_FOR_FN (cfun)); |
| walker.cleanup (); |
| |
| scev_finalize (); |
| loop_optimizer_finalize (); |
| return 0; |
| } |
| |
| namespace { |
| |
| const pass_data pass_data_early_vrp = |
| { |
| GIMPLE_PASS, /* type */ |
| "evrp", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_TREE_EARLY_VRP, /* tv_id */ |
| PROP_ssa, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| ( TODO_cleanup_cfg | TODO_update_ssa | TODO_verify_all ), |
| }; |
| |
| class pass_early_vrp : public gimple_opt_pass |
| { |
| public: |
| pass_early_vrp (gcc::context *ctxt) |
| : gimple_opt_pass (pass_data_early_vrp, ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| opt_pass * clone () { return new pass_early_vrp (m_ctxt); } |
| virtual bool gate (function *) |
| { |
| return flag_tree_vrp != 0; |
| } |
| virtual unsigned int execute (function *) |
| { return execute_early_vrp (); } |
| |
| }; // class pass_vrp |
| } // anon namespace |
| |
| gimple_opt_pass * |
| make_pass_early_vrp (gcc::context *ctxt) |
| { |
| return new pass_early_vrp (ctxt); |
| } |
| |