--- /dev/null
- virtual const value_range_equiv *get_value_range (const_tree expr,
- gimple *stmt = NULL);
+/* Header file for gimple range GORI structures.
+ Copyright (C) 2017-2020 Free Software Foundation, Inc.
+ Contributed by Andrew MacLeod <amacleod@redhat.com>
+ and Aldy Hernandez <aldyh@redhat.com>.
+
+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/>. */
+
+#ifndef GCC_GIMPLE_RANGE_GORI_H
+#define GCC_GIMPLE_RANGE_GORI_H
+
+/* RANGE_DEF_CHAIN is used to determine what SSA names in a block can
+ have range information calculated for them, and what the
+ dependencies on each other are.
+
+ Information for a basic block is calculated once and stored. It is
+ only calculated the first time a query is made, so if no queries
+ are made, there is little overhead.
+
+ The def_chain bitmap is indexed by SSA_NAME_VERSION. Bits are set
+ within this bitmap to indicate SSA names that are defined in the
+ SAME block and used to calculate this SSA name.
+
+ One import is maintained per def-chain. An IMPORT is defined as an
+ SSA name in the def chain which occurs outside the basic block. A
+ change in the value of this SSA name can change the value of any
+ name in the chain.
+
+ If there is more than one import, or an ssa_name originates WITHIN
+ the same basic block, but is defined by a statement that the range
+ engine does not know how to calculate, then there is no import for
+ the entire chain.
+
+ <bb 2> :
+ _1 = x_4(D) + -2;
+ _2 = _1 * 4;
+ j_7 = foo ();
+ q_5 = _2 + 3;
+ if (q_5 <= 13)
+
+ _1 : (import : x_4(D)) :x_4(D)
+ _2 : (import : x_4(D)) :_1 x_4(D)
+ q_5 : (import : x_4(D)) :_1 _2 x_4(D)
+
+ This dump indicates the bits set in the def_chain vector and their
+ import, as well as demonstrates the def_chain bits for the related
+ ssa_names.
+
+ Checking the chain for _2 indicates that _1 and x_4 are used in
+ its evaluation, and with x_4 being an import.
+
+ For the purpose of defining an import, PHI node defintions are
+ considered imports as they don't really reside in the block, but
+ are accumulators of values from incoming edges.
+
+ Def chains also only include statements which are valid gimple
+ so a def chain will only span statements for which the range
+ engine implements operations for. */
+
+
+class range_def_chain
+{
+public:
+ range_def_chain ();
+ ~range_def_chain ();
+ tree terminal_name (tree name);
+ bool has_def_chain (tree name);
+ bitmap get_def_chain (tree name);
+ bool in_chain_p (tree name, tree def);
+private:
+ vec<bitmap> m_def_chain; // SSA_NAME : def chain components.
+ vec<tree> m_terminal; // SSA_NAME : chain terminal name.
+ tree build_def_chain (tree name, bitmap result, basic_block bb);
+};
+
+
+/* GORI_MAP is used to accumulate what SSA names in a block can
+ generate range information, and provides tools for the block ranger
+ to enable it to efficiently calculate these ranges.
+
+ GORI stands for "Generates Outgoing Range Information."
+
+ It utilizes the range_def_chain class to contruct def_chains.
+ Information for a basic block is calculated once and stored. It is
+ only calculated the first time a query is made. If no queries are
+ made, there is little overhead.
+
+ 2 bitmaps are maintained for each basic block:
+
+ m_outgoing : a set bit indicates a range can be generated for a name.
+ m_incoming : a set bit means a this name come from outside the
+ block and is used in the calculation of some outgoing
+ range.
+
+ Generally speaking, the m_outgoing vector is the union of the
+ entire def_chain of all SSA names used in the last statement of the
+ block which generate ranges. The m_incoming vector is the union of
+ all the terminal names of those def chains. They act as a one-stop
+ summary for the block. */
+
+class gori_map : public range_def_chain
+{
+public:
+ gori_map ();
+ ~gori_map ();
+
+ bool is_export_p (tree name, basic_block bb);
+ bool def_chain_in_export_p (tree name, basic_block bb);
+ bool is_import_p (tree name, basic_block bb);
+
+ void dump (FILE *f);
+ void dump (FILE *f, basic_block bb);
+private:
+ bitmap_obstack m_bitmaps;
+ vec<bitmap> m_outgoing; // BB: Outgoing ranges calculatable on edges
+ vec<bitmap> m_incoming; // BB: block imports
+ void maybe_add_gori (tree name, basic_block bb);
+ void calculate_gori (basic_block bb);
+ bitmap imports (basic_block bb);
+public:
+ // FIXME: Temporarily set as public.
+ bitmap exports (basic_block bb);
+};
+
+// Generic object to return a range for an SSA.
+class range_store
+{
+public:
+ virtual bool range_of_expr (irange &r, tree expr, gimple *stmt = NULL) = 0;
++ virtual const class value_range_equiv *get_value_range (const_tree expr,
++ gimple *stmt = NULL);
+};
+
+// This class utilizes a GORI map to determine which SSA_NAMES can
+// have ranges calculated for them on outgoing edges from basic
+// blocks.
+
+class gori_compute : public range_store
+{
+public:
+ gori_compute ();
+ /* Destructor is virtual to silence:
+
+ warning: deleting object of polymorphic class type ‘vr_values’
+ which has non-virtual destructor might cause undefined
+ behavior. */
+ virtual ~gori_compute ();
+ virtual bool range_of_expr (irange &r, tree expr, gimple *stmt = NULL);
+ virtual bool outgoing_edge_range_p (irange &r, edge e, tree name,
+ const irange *name_range = NULL);
+protected:
+ virtual void range_of_ssa_name (irange &r, tree name, gimple *stmt = NULL);
+ virtual bool compute_operand_range (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name,
+ const irange *name_range = NULL);
+ bool has_edge_range_p (edge e, tree name);
+ virtual bool compute_logical_operands (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name, const irange *name_range);
+ void compute_logical_operands_in_chain (class tf_range &range,
+ gimple *stmt, const irange &lhs,
+ tree name,
+ const irange *name_range,
+ tree op, bool op_in_chain);
+ bool optimize_logical_operands (tf_range &range,
+ gimple *stmt, const irange &lhs,
+ tree name, const irange *name_range,
+ tree op);
+
+ bool logical_combine (irange &r, enum tree_code code,
+ const irange &lhs,
+ const class tf_range &op1_range,
+ const class tf_range &op2_range);
+ int_range<1> m_bool_zero; // Boolean false cached.
+ int_range<1> m_bool_one; // Boolean true cached.
+
+ gori_map m_gori_map;
+private:
+ void get_tree_range (irange &, tree expr, tree name,
+ const irange *range_of_name);
+ bool compute_operand_range_switch (irange &r, gswitch *stmt,
+ const irange &lhs,
+ tree name, const irange *name_range);
+ bool compute_name_range_op (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name, const irange *name_range);
+ bool compute_operand1_range (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name, const irange *name_range);
+ bool compute_operand2_range (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name, const irange *name_range);
+ bool compute_operand1_and_operand2_range
+ (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name, const irange *name_range);
+};
+
+class gori_compute_cache : public gori_compute
+{
+public:
+ gori_compute_cache ();
+ ~gori_compute_cache ();
+protected:
+ virtual bool compute_operand_range (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name,
+ const irange *name_range = NULL);
+private:
+ void cache_comparison (gimple *);
+ void cache_comparison_with_int (gimple *, enum tree_code,
+ tree op1, tree op2);
+ void cache_comparison_with_ssa (gimple *, enum tree_code,
+ tree op1, tree op2);
+ typedef gori_compute super;
+ class logical_stmt_cache *m_cache;
+};
+
+class trace_gori_compute : public gori_compute_cache
+{
+public:
+ trace_gori_compute ();
+ virtual bool range_of_expr (irange &r, tree expr, gimple *stmt = NULL);
+ virtual bool outgoing_edge_range_p (irange &r, edge e, tree name,
+ const irange *name_range = NULL);
+protected:
+ virtual void range_of_ssa_name (irange &r, tree name, gimple *stmt = NULL);
+ virtual bool compute_operand_range (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name,
+ const irange *name_range = NULL);
+ virtual bool compute_logical_operands (irange &r, gimple *stmt,
+ const irange &lhs,
+ tree name, const irange *name_range);
+private:
+ typedef gori_compute_cache super;
+protected:
+ static const unsigned bump = 2;
+ unsigned indent;
+ unsigned trace_count; // Current trace index count.
+
+ bool dumping (unsigned counter, bool trailing = false);
+ bool trailer (unsigned counter, const char *caller, bool result, tree name,
+ const irange &r);
+};
+
+#endif // GCC_GIMPLE_RANGE_GORI_H
#include "tree-cfgcleanup.h"
#include "vr-values.h"
#include "gimple-ssa-evrp-analyze.h"
+#include "misc.h"
class evrp_folder : public substitute_and_fold_engine
- {
- public:
- tree get_value (tree, gimple *) FINAL OVERRIDE;
- evrp_folder (class vr_values *vr_values_)
- : vr_values (vr_values_),
- simplifier (vr_values_) { }
- bool simplify_stmt_using_ranges (gimple_stmt_iterator *gsi)
- {
- return simplifier.simplify (gsi);
- }
- class vr_values *vr_values;
-
- private:
- DISABLE_COPY_AND_ASSIGN (evrp_folder);
- simplify_using_ranges simplifier;
- };
-
- tree
- evrp_folder::get_value (tree op, gimple *stmt ATTRIBUTE_UNUSED)
- {
- 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;
-
- const value_range_equiv *vr = evrp_range_analyzer.get_value_range (lhs);
- /* Mark PHIs whose lhs we fully propagate for removal. */
- tree val;
- if (vr->singleton_p (&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)
- {
- const value_range_equiv *vr
- = evrp_range_analyzer.get_value_range (output);
-
- /* Mark stmts whose output we fully propagate for removal. */
- tree val;
- if (vr->singleton_p (&val)
- && 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);
- gimple_stmt_iterator prev_gsi = gsi;
- gsi_prev (&prev_gsi);
- 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 wound up generating new stmts during folding
- drop all their defs to VARYING. We can't easily
- process them because we've already instantiated
- ranges on uses on STMT that only hold after it. */
- if (gsi_end_p (prev_gsi))
- prev_gsi = gsi_start_bb (bb);
- else
- gsi_next (&prev_gsi);
- while (gsi_stmt (prev_gsi) != gsi_stmt (gsi))
- {
- evrp_range_analyzer.get_vr_values ()
- ->set_defs_to_varying (gsi_stmt (prev_gsi));
- gsi_next (&prev_gsi);
- }
-
- /* 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;
- const value_range_equiv
- *vr = evrp_range_analyzer.get_value_range (arg);
- tree val;
- if (vr->singleton_p (&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);
- }
- }
-
- class xevrp_folder : public substitute_and_fold_engine
{
public:
- xevrp_folder () : range_analyzer (true),
- vr_values (range_analyzer.get_vr_values ()),
- simplifier (vr_values)
+ evrp_folder () : m_range_analyzer (/*update_global_ranges=*/true),
- m_vr_values (m_range_analyzer.get_vr_values ())
++ m_vr_values (m_range_analyzer.get_vr_values ()),
++ simplifier (m_vr_values)
{
}
- ~xevrp_folder ()
+ ~evrp_folder ()
{
- m_vr_values->cleanup_edges_and_switches ();
-
if (dump_file)
{
fprintf (dump_file, "\nValue ranges after Early VRP:\n\n");
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
- fprintf (dump_file, "Visiting stmt ");
+ fprintf (dump_file, "evrp visiting stmt ");
print_gimple_stmt (dump_file, stmt, 0);
}
- range_analyzer.record_ranges_from_stmt (stmt, false);
+ m_gimple_state.set_orig_stmt (stmt);
+ m_range_analyzer.record_ranges_from_stmt (stmt, false);
}
- void tmp_stats_remove_stmt (gimple *stmt, tree lhs) OVERRIDE
+ bool fold_stmt (gimple_stmt_iterator *gsi) OVERRIDE
{
- m_gimple_state.maybe_dump_differences_and_trap (stmt, lhs);
- return m_vr_values->simplify_stmt_using_ranges (gsi);
++ bool res = simplifier.simplify (gsi);
++ if (m_modified || res)
++ m_gimple_state.maybe_dump_differences_and_trap (gsi_stmt (*gsi));
++ return res;
}
- void tmp_stats_changed_phi (gphi *orig_phi, gphi *new_phi) OVERRIDE
+ void post_fold_bb (basic_block bb) OVERRIDE
{
- gimple *save = m_gimple_state.set_orig_stmt (orig_phi);
- m_gimple_state.maybe_dump_differences_and_trap (new_phi);
- m_gimple_state.set_orig_stmt (save);
+ m_range_analyzer.leave (bb);
}
- void tmp_stats_set_modified (bool modified) OVERRIDE
+ void post_new_stmt (gimple *stmt) OVERRIDE
{
- m_modified = modified;
- m_vr_values->set_defs_to_varying (stmt);
++ m_range_analyzer.get_vr_values ()->set_defs_to_varying (stmt);
+ }
+
- bool fold_stmt (gimple_stmt_iterator *gsi)
++ void tmp_stats_remove_stmt (gimple *stmt, tree lhs) OVERRIDE
+ {
- bool res = simplifier.simplify (gsi);
- if (m_modified || res)
- m_gimple_state.maybe_dump_differences_and_trap (gsi_stmt (*gsi));
- return res;
++ m_gimple_state.maybe_dump_differences_and_trap (stmt, lhs);
+ }
+
- void post_fold_bb (basic_block bb)
++ void tmp_stats_changed_phi (gphi *orig_phi, gphi *new_phi) OVERRIDE
+ {
- range_analyzer.leave (bb);
++ gimple *save = m_gimple_state.set_orig_stmt (orig_phi);
++ m_gimple_state.maybe_dump_differences_and_trap (new_phi);
++ m_gimple_state.set_orig_stmt (save);
+ }
+
- void post_new_stmt (gimple *stmt)
++ void tmp_stats_set_modified (bool modified) OVERRIDE
+ {
- range_analyzer.get_vr_values ()->set_defs_to_varying (stmt);
++ m_modified = modified;
}
private:
- DISABLE_COPY_AND_ASSIGN (xevrp_folder);
- class evrp_range_analyzer range_analyzer;
- class vr_values *vr_values;
+ DISABLE_COPY_AND_ASSIGN (evrp_folder);
+ class evrp_range_analyzer m_range_analyzer;
+ class vr_values *m_vr_values;
++
+ simplify_using_ranges simplifier;
+ class gimple_state m_gimple_state;
+ bool m_modified;
};
/* Main entry point for the early vrp pass which is a simplified non-iterative
/* { dg-do compile } */
- /* { dg-options "-O2 -fno-tree-ccp -fno-tree-forwprop -fno-tree-fre -fno-tree-vrp -fdisable-tree-evrp-copyprop" } */
- /* { dg-additional-options "-fdump-tree-dse1-details" } */
+ /* { dg-options "-O2 -fno-tree-ccp -fno-tree-forwprop -fno-tree-fre -fno-tree-vrp -fdump-tree-dse1-details" } */
++/* { dg-additional-options "-fdisable-tree-evrp-copyprop" } */
int
f ()
enum ssa_prop_result visit_stmt (gimple *, edge *, tree *) FINAL OVERRIDE;
enum ssa_prop_result visit_phi (gphi *) FINAL OVERRIDE;
- void vrp_initialize (void);
+ struct function *fun;
+
+ void vrp_initialize (struct function *);
- void vrp_finalize (bool);
+ void vrp_finalize (class vrp_folder *, bool);
- void check_all_array_refs (void);
- bool check_array_ref (location_t, tree, bool);
- bool check_mem_ref (location_t, tree, bool);
- void search_for_addr_array (tree, location_t);
class vr_values vr_values;
+
+ private:
/* Temporary delegator to minimize code churn. */
const value_range_equiv *get_value_range (const_tree op)
{ return vr_values.get_value_range (op); }
class vrp_folder : public substitute_and_fold_engine
{
-public:
- vrp_folder () : substitute_and_fold_engine (/* Fold all stmts. */ true) { }
+ public:
+ vrp_folder (vr_values *v)
+ : substitute_and_fold_engine (/* Fold all stmts. */ true),
+ m_vr_values (v), simplifier (v)
+ { }
- tree get_value (tree, gimple *) FINAL OVERRIDE;
+ tree get_value (tree, gimple *stmt) FINAL OVERRIDE;
bool fold_stmt (gimple_stmt_iterator *) FINAL OVERRIDE;
- bool fold_predicate_in (gimple_stmt_iterator *);
- class vr_values *vr_values;
++ class vr_values *m_vr_values;
+
+ private:
+ bool fold_predicate_in (gimple_stmt_iterator *);
/* Delegators. */
tree vrp_evaluate_conditional (tree_code code, tree op0,
tree op1, gimple *stmt)
- { return vr_values->vrp_evaluate_conditional (code, op0, op1, stmt); }
+ { return simplifier.vrp_evaluate_conditional (code, op0, op1, stmt); }
bool simplify_stmt_using_ranges (gimple_stmt_iterator *gsi)
- { return vr_values->simplify_stmt_using_ranges (gsi); }
+ { return simplifier.simplify (gsi); }
tree op_with_constant_singleton_value_range (tree op)
- { return vr_values->op_with_constant_singleton_value_range (op); }
+ { return m_vr_values->op_with_constant_singleton_value_range (op); }
+
- private:
- vr_values *m_vr_values;
+ simplify_using_ranges simplifier;
};
/* If the statement pointed by SI has a predicate whose value can be
check_array_bounds_dom_walker's ctor; vrp_folder may clear
it from some edges. */
if (warn_array_bounds && warn_array_bounds_p)
- set_all_edges_as_executable (cfun);
+ set_all_edges_as_executable (fun);
- class vrp_folder vrp_folder;
- vrp_folder.vr_values = &vr_values;
- vrp_folder.substitute_and_fold ();
+ folder->substitute_and_fold ();
if (warn_array_bounds && warn_array_bounds_p)
- check_all_array_refs ();
+ {
+ array_bounds_checker array_checker (fun, &vr_values);
+ array_checker.check ();
+ }
}
/* Main entry point to VRP (Value Range Propagation). This pass is
mark_dfs_back_edges ();
class vrp_prop vrp_prop;
- vrp_prop.vrp_initialize ();
+ vrp_prop.vrp_initialize (fun);
vrp_prop.ssa_propagate ();
- vrp_prop.vrp_finalize (warn_array_bounds_p);
+ /* Instantiate the folder here, so that edge cleanups happen at the
+ end of this function. */
+ vrp_folder folder (&vrp_prop.vr_values);
+ vrp_prop.vrp_finalize (&folder, warn_array_bounds_p);
/* We must identify jump threading opportunities before we release
the datastructures built by VRP. */
{
gimple *last = last_stmt (bb);
if (last && gimple_code (last) == GIMPLE_COND)
- vrp_prop.vr_values.simplify_cond_using_ranges_2 (as_a <gcond *> (last));
+ simplify_cond_using_ranges_2 (&vrp_prop.vr_values,
+ as_a <gcond *> (last));
}
- free_numbers_of_iterations_estimates (cfun);
+ free_numbers_of_iterations_estimates (fun);
/* ASSERT_EXPRs must be removed before finalizing jump threads
as finalizing jump threads calls the CFG cleanup code which
--- /dev/null
-value_range_equiv::intersect (const value_range_equiv *other)
+ /* Support routines for value ranges with equivalences.
+ Copyright (C) 2020 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 "ssa.h"
+ #include "tree-pretty-print.h"
+ #include "value-range-equiv.h"
+
+ value_range_equiv::value_range_equiv (tree min, tree max, bitmap equiv,
+ value_range_kind kind)
+ {
++ m_discriminator = VRANGE_KIND_INT_WITH_EQUIVS;
+ m_equiv = NULL;
+ set (min, max, equiv, kind);
+ }
+
+ value_range_equiv::value_range_equiv (const value_range &other)
+ {
++ m_discriminator = VRANGE_KIND_INT_WITH_EQUIVS;
+ m_equiv = NULL;
+ set (other.min(), other.max (), NULL, other.kind ());
+ }
+
+ void
+ value_range_equiv::set (tree min, tree max, bitmap equiv,
+ value_range_kind kind)
+ {
+ value_range::set (min, max, kind);
+ set_equiv (equiv);
+ if (flag_checking)
+ check ();
+ }
+
+ void
+ value_range_equiv::set (tree val)
+ {
+ gcc_assert (TREE_CODE (val) == SSA_NAME || is_gimple_min_invariant (val));
+ if (TREE_OVERFLOW_P (val))
+ val = drop_tree_overflow (val);
+ set (val, val);
+ }
+
+ void
+ value_range_equiv::set_undefined ()
+ {
+ set (NULL, NULL, NULL, VR_UNDEFINED);
+ }
+
+ void
+ value_range_equiv::set_varying (tree type)
+ {
+ value_range::set_varying (type);
+ equiv_clear ();
+ }
+
+ /* Like set, but keep the equivalences in place. */
+
+ void
+ value_range_equiv::update (tree min, tree max, value_range_kind kind)
+ {
+ set (min, max,
+ (kind != VR_UNDEFINED && kind != VR_VARYING) ? m_equiv : NULL, kind);
+ }
+
+ /* Copy value_range in FROM into THIS while avoiding bitmap sharing.
+
+ Note: The code that avoids the bitmap sharing looks at the existing
+ this->m_equiv, so this function cannot be used to initalize an
+ object. Use the constructors for initialization. */
+
+ void
+ value_range_equiv::deep_copy (const value_range_equiv *from)
+ {
+ set (from->min (), from->max (), from->m_equiv, from->m_kind);
+ }
+
+ void
+ value_range_equiv::move (value_range_equiv *from)
+ {
+ set (from->min (), from->max (), NULL, from->m_kind);
+ m_equiv = from->m_equiv;
+ from->m_equiv = NULL;
+ }
+
+ void
+ value_range_equiv::set_equiv (bitmap equiv)
+ {
+ if (undefined_p () || varying_p ())
+ equiv = NULL;
+ /* Since updating the equivalence set involves deep copying the
+ bitmaps, only do it if absolutely necessary.
+
+ All equivalence bitmaps are allocated from the same obstack. So
+ we can use the obstack associated with EQUIV to allocate vr->equiv. */
+ if (m_equiv == NULL
+ && equiv != NULL)
+ m_equiv = BITMAP_ALLOC (equiv->obstack);
+
+ if (equiv != m_equiv)
+ {
+ if (equiv && !bitmap_empty_p (equiv))
+ bitmap_copy (m_equiv, equiv);
+ else
+ bitmap_clear (m_equiv);
+ }
+ }
+
+ void
+ value_range_equiv::check ()
+ {
+ value_range::check ();
+ switch (m_kind)
+ {
+ case VR_UNDEFINED:
+ case VR_VARYING:
+ gcc_assert (!m_equiv || bitmap_empty_p (m_equiv));
+ default:;
+ }
+ }
+
+ /* Return true if the bitmaps B1 and B2 are equal. */
+
+ static bool
+ vr_bitmap_equal_p (const_bitmap b1, const_bitmap b2)
+ {
+ return (b1 == b2
+ || ((!b1 || bitmap_empty_p (b1))
+ && (!b2 || bitmap_empty_p (b2)))
+ || (b1 && b2
+ && bitmap_equal_p (b1, b2)));
+ }
+
+ /* Returns TRUE if THIS == OTHER. Ignores the equivalence bitmap if
+ IGNORE_EQUIVS is TRUE. */
+
+ bool
+ value_range_equiv::equal_p (const value_range_equiv &other,
+ bool ignore_equivs) const
+ {
+ return (value_range::equal_p (other)
+ && (ignore_equivs || vr_bitmap_equal_p (m_equiv, other.m_equiv)));
+ }
+
+ void
+ value_range_equiv::equiv_clear ()
+ {
+ if (m_equiv)
+ bitmap_clear (m_equiv);
+ }
+
+ /* Add VAR and VAR's equivalence set (VAR_VR) to the equivalence
+ bitmap. If no equivalence table has been created, OBSTACK is the
+ obstack to use (NULL for the default obstack).
+
+ This is the central point where equivalence processing can be
+ turned on/off. */
+
+ void
+ value_range_equiv::equiv_add (const_tree var,
+ const value_range_equiv *var_vr,
+ bitmap_obstack *obstack)
+ {
+ if (!m_equiv)
+ m_equiv = BITMAP_ALLOC (obstack);
+ unsigned ver = SSA_NAME_VERSION (var);
+ bitmap_set_bit (m_equiv, ver);
+ if (var_vr && var_vr->m_equiv)
+ bitmap_ior_into (m_equiv, var_vr->m_equiv);
+ }
+
+ void
-value_range_equiv::union_ (const value_range_equiv *other)
++value_range_equiv::intersect (const vrange &vother)
+ {
++ if (!is_a <const value_range_equiv *> (&vother))
++ {
++ irange::intersect (vother);
++ return;
++ }
++ const value_range_equiv *other = as_a <const value_range_equiv *> (&vother);
++ gcc_checking_assert (other != NULL);
++
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Intersecting\n ");
+ dump_value_range (dump_file, this);
+ fprintf (dump_file, "\nand\n ");
+ dump_value_range (dump_file, other);
+ fprintf (dump_file, "\n");
+ }
+
+ /* If THIS is varying we want to pick up equivalences from OTHER.
+ Just special-case this here rather than trying to fixup after the
+ fact. */
+ if (this->varying_p ())
+ this->deep_copy (other);
+ else
+ {
+ value_range tem = intersect_helper (this, other);
+ this->update (tem.min (), tem.max (), tem.kind ());
+
+ /* If the result is VR_UNDEFINED there is no need to mess with
+ equivalencies. */
+ if (!undefined_p ())
+ {
+ /* The resulting set of equivalences for range intersection
+ is the union of the two sets. */
+ if (m_equiv && other->m_equiv && m_equiv != other->m_equiv)
+ bitmap_ior_into (m_equiv, other->m_equiv);
+ else if (other->m_equiv && !m_equiv)
+ {
+ /* All equivalence bitmaps are allocated from the same
+ obstack. So we can use the obstack associated with
+ VR to allocate this->m_equiv. */
+ m_equiv = BITMAP_ALLOC (other->m_equiv->obstack);
+ bitmap_copy (m_equiv, other->m_equiv);
+ }
+ }
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "to\n ");
+ dump_value_range (dump_file, this);
+ fprintf (dump_file, "\n");
+ }
+ }
+
+ void
- value_range tem = union_helper (this, other);
++value_range_equiv::intersect (const value_range_equiv *other)
++{
++ intersect (*other);
++}
++
++void
++value_range_equiv::union_ (const vrange &vother)
+ {
++ if (!is_a <const value_range_equiv *> (&vother))
++ {
++ irange::union_ (vother);
++ return;
++ }
++ const value_range_equiv *other = as_a <const value_range_equiv *> (&vother);
++ gcc_checking_assert (other != NULL);
++
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Meeting\n ");
+ dump_value_range (dump_file, this);
+ fprintf (dump_file, "\nand\n ");
+ dump_value_range (dump_file, other);
+ fprintf (dump_file, "\n");
+ }
+
+ /* If THIS is undefined we want to pick up equivalences from OTHER.
+ Just special-case this here rather than trying to fixup after the fact. */
+ if (this->undefined_p ())
+ this->deep_copy (other);
+ else
+ {
++ gcc_checking_assert (other->simple_ranges_p ());
++ value_range tem = union_helper (this, (const value_range *) other);
+ this->update (tem.min (), tem.max (), tem.kind ());
+
+ /* The resulting set of equivalences is always the intersection of
+ the two sets. */
+ if (this->m_equiv && other->m_equiv && this->m_equiv != other->m_equiv)
+ bitmap_and_into (this->m_equiv, other->m_equiv);
+ else if (this->m_equiv && !other->m_equiv)
+ bitmap_clear (this->m_equiv);
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "to\n ");
+ dump_value_range (dump_file, this);
+ fprintf (dump_file, "\n");
+ }
+ }
+
++void
++value_range_equiv::union_ (const value_range_equiv *other)
++{
++ union_ (*other);
++}
++
+ void
+ value_range_equiv::dump (FILE *file) const
+ {
+ value_range::dump (file);
+ if ((m_kind == VR_RANGE || m_kind == VR_ANTI_RANGE)
+ && m_equiv)
+ {
+ bitmap_iterator bi;
+ unsigned i, c = 0;
+
+ fprintf (file, " EQUIVALENCES: { ");
+ EXECUTE_IF_SET_IN_BITMAP (m_equiv, 0, i, bi)
+ {
+ print_generic_expr (file, ssa_name (i));
+ fprintf (file, " ");
+ c++;
+ }
+ fprintf (file, "} (%u elements)", c);
+ }
+ }
+
+ void
+ value_range_equiv::dump () const
+ {
+ dump (stderr);
+ }
+
+ void
+ dump_value_range (FILE *file, const value_range_equiv *vr)
+ {
+ if (!vr)
+ fprintf (file, "[]");
+ else
+ vr->dump (file);
+ }
+
+ DEBUG_FUNCTION void
+ debug (const value_range_equiv *vr)
+ {
+ dump_value_range (stderr, vr);
+ }
+
+ DEBUG_FUNCTION void
+ debug (const value_range_equiv &vr)
+ {
+ dump_value_range (stderr, &vr);
+ }
--- /dev/null
- value_range_equiv () : value_range () { m_equiv = NULL; }
+ /* Support routines for value ranges with equivalences.
+ Copyright (C) 2020 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/>. */
+
+ #ifndef GCC_VALUE_RANGE_EQUIV_H
+ #define GCC_VALUE_RANGE_EQUIV_H
+
+ #include "value-range.h"
+
+ /* Note value_range_equiv cannot currently be used with GC memory,
+ only value_range is fully set up for this. */
+ class GTY((user)) value_range_equiv : public value_range
+ {
+ public:
++ value_range_equiv () : value_range ()
++ {
++ m_discriminator = VRANGE_KIND_INT_WITH_EQUIVS;
++ m_equiv = NULL;
++ }
+ value_range_equiv (const value_range &);
+ /* Deep-copies equiv bitmap argument. */
+ value_range_equiv (tree, tree, bitmap = NULL, value_range_kind = VR_RANGE);
+
+ /* Shallow-copies equiv bitmap. */
+ value_range_equiv (const value_range_equiv &) /* = delete */;
+ /* Shallow-copies equiv bitmap. */
+ value_range_equiv& operator=(const value_range_equiv &) /* = delete */;
+
+ /* Move equiv bitmap from source range. */
+ void move (value_range_equiv *);
+
+ /* Leaves equiv bitmap alone. */
+ void update (tree, tree, value_range_kind = VR_RANGE);
+ /* Deep-copies equiv bitmap argument. */
+ void set (tree, tree, bitmap = NULL, value_range_kind = VR_RANGE);
+ void set (tree);
+
+ bool operator== (const value_range_equiv &) const /* = delete */;
+ bool operator!= (const value_range_equiv &) const /* = delete */;
+ void intersect (const value_range_equiv *);
+ void union_ (const value_range_equiv *);
++ virtual void intersect (const vrange &);
++ virtual void union_ (const vrange &);
+ bool equal_p (const value_range_equiv &, bool ignore_equivs) const;
+
+ /* Types of value ranges. */
+ void set_undefined ();
+ void set_varying (tree);
+
+ /* Equivalence bitmap methods. */
+ bitmap equiv () const { return m_equiv; }
+ void equiv_clear ();
+ void equiv_add (const_tree, const value_range_equiv *,
+ bitmap_obstack * = NULL);
+
+ /* Misc methods. */
+ void deep_copy (const value_range_equiv *);
+ void dump (FILE *) const;
+ void dump () const;
+
+ private:
+ /* Deep-copies bitmap argument. */
+ void set_equiv (bitmap);
+ void check ();
+
+ /* Set of SSA names whose value ranges are equivalent to this one.
+ This set is only valid when TYPE is VR_RANGE or VR_ANTI_RANGE. */
+ bitmap m_equiv;
+ };
+
+ extern void dump_value_range (FILE *, const value_range_equiv *);
+
++template <>
++template <>
++inline bool
++is_a_helper <const value_range_equiv *>::test (const vrange *p)
++{
++ return p && p->m_discriminator == VRANGE_KIND_INT_WITH_EQUIVS;
++}
++
++template <>
++template <>
++inline bool
++is_a_helper <value_range_equiv *>::test (vrange *p)
++{
++ return p && p->m_discriminator == VRANGE_KIND_INT_WITH_EQUIVS;
++}
++
+ #endif // GCC_VALUE_RANGE_EQUIV_H
return true;
}
+ /* If COND can be folded entirely as TRUE or FALSE, rewrite the
+ conditional as such, and return TRUE. */
+
bool
- simplify_using_ranges::simplify_cond_using_ranges_when_edge_is_known
- (gcond *cond)
-vr_values::fold_cond (gcond *cond)
++simplify_using_ranges::fold_cond (gcond *cond)
{
/* ?? vrp_folder::fold_predicate_in() is a superset of this. At
some point we should merge all variants of this code. */
#ifndef GCC_VR_VALUES_H
#define GCC_VR_VALUES_H
- bool simplify_cond_using_ranges_when_edge_is_known (gcond *);
+ #include "value-range-equiv.h"
+#include "gimple-range-gori.h"
+
+class simplify_using_ranges
+{
+public:
+ simplify_using_ranges (range_store *);
+ ~simplify_using_ranges ();
+ bool simplify (gimple_stmt_iterator *);
+
+ // ?? These should be cleaned, merged, and made private.
+ tree vrp_evaluate_conditional (tree_code, tree, tree, gimple *);
+ void vrp_visit_cond_stmt (gcond *, edge *);
+ tree vrp_evaluate_conditional_warnv_with_ops (enum tree_code,
+ tree, tree, bool,
+ bool *, bool *);
+
+private:
+ // This is named differently than get_value_range to make it obvious
+ // that it returns an equivalence. Only use this for calculations
+ // that may take equivalences, otherwise use range_of_expr.
+ const value_range_equiv *get_value_range_equiv (const_tree op,
+ gimple *stmt = NULL)
+ { return store->get_value_range (op, stmt); }
+ bool simplify_truth_ops_using_ranges (gimple_stmt_iterator *, gimple *);
+ bool simplify_div_or_mod_using_ranges (gimple_stmt_iterator *, gimple *);
+ bool simplify_abs_using_ranges (gimple_stmt_iterator *, gimple *);
+ bool simplify_bit_ops_using_ranges (gimple_stmt_iterator *, gimple *);
+ bool simplify_min_or_max_using_ranges (gimple_stmt_iterator *, gimple *);
+ bool simplify_cond_using_ranges_1 (gcond *);
++ bool fold_cond (gcond *);
+ bool simplify_switch_using_ranges (gswitch *);
+ bool simplify_float_conversion_using_ranges (gimple_stmt_iterator *,
+ gimple *);
+ bool simplify_internal_call_using_ranges (gimple_stmt_iterator *, gimple *);
+
+ bool two_valued_val_range_p (tree, tree *, tree *);
+ bool op_with_boolean_value_range_p (tree, gimple *);
+ tree compare_name_with_value (enum tree_code, tree, tree, bool *, bool);
+ tree compare_names (enum tree_code, tree, tree, bool *);
+ const value_range_equiv *get_vr_for_comparison (int, value_range_equiv *);
+ tree vrp_evaluate_conditional_warnv_with_ops_using_ranges (enum tree_code,
+ tree, tree,
+ bool *);
+ void cleanup_edges_and_switches (void);
+
+ /* Vectors of edges that need removing and switch statements that
+ need updating. It is expected that a pass using the simplification
+ routines will, at the end of the pass, clean up the edges and
+ switch statements. The class dtor will try to detect cases
+ that do not follow that expectation. */
+ struct switch_update {
+ gswitch *stmt;
+ tree vec;
+ };
+
+ vec<edge> to_remove_edges;
+ vec<switch_update> to_update_switch_stmts;
+ gimple *m_stmt;
+ range_store *store;
+};
/* The VR_VALUES class holds the current view of range information
for all the SSA_NAMEs in the IL.