diff options
author | Andrew MacLeod <amacleod@redhat.com> | 2022-02-07 15:52:16 -0500 |
---|---|---|
committer | Andrew MacLeod <amacleod@redhat.com> | 2022-02-09 09:09:54 -0500 |
commit | c6bb1db76b3ac127aff7dacf391fc1798a94bb7d (patch) | |
tree | ece35b744603236671d2ee0e8cc8fdf1c8f06828 | |
parent | da2bf62d9e2a25f2d6a99176144c250b51fbdee7 (diff) | |
download | gcc-c6bb1db76b3ac127aff7dacf391fc1798a94bb7d.zip gcc-c6bb1db76b3ac127aff7dacf391fc1798a94bb7d.tar.gz gcc-c6bb1db76b3ac127aff7dacf391fc1798a94bb7d.tar.bz2 |
Register non-null side effects properly.
This patch adjusts uses of nonnull to accurately reflect "somewhere in block".
It also adds the ability to register statement side effects within a block
for ranger which will apply for the rest of the block.
PR tree-optimization/104288
gcc/
* gimple-range-cache.cc (non_null_ref::set_nonnull): New.
(non_null_ref::adjust_range): Move to header.
(ranger_cache::range_of_def): Don't check non-null.
(ranger_cache::entry_range): Don't check non-null.
(ranger_cache::range_on_edge): Check for nonnull on normal edges.
(ranger_cache::update_to_nonnull): New.
(non_null_loadstore): New.
(ranger_cache::block_apply_nonnull): New.
* gimple-range-cache.h (class non_null_ref): Update prototypes.
(non_null_ref::adjust_range): Move to here and inline.
(class ranger_cache): Update prototypes.
* gimple-range-path.cc (path_range_query::range_defined_in_block): Do
not search dominators.
(path_range_query::adjust_for_non_null_uses): Ditto.
* gimple-range.cc (gimple_ranger::range_of_expr): Check on-entry for
def overrides. Do not check nonnull.
(gimple_ranger::range_on_entry): Check dominators for nonnull.
(gimple_ranger::range_on_edge): Check for nonnull on normal edges..
(gimple_ranger::register_side_effects): New.
* gimple-range.h (gimple_ranger::register_side_effects): New.
* tree-vrp.cc (rvrp_folder::fold_stmt): Call register_side_effects.
gcc/testsuite/
* gcc.dg/pr104288.c: New.
-rw-r--r-- | gcc/gimple-range-cache.cc | 135 | ||||
-rw-r--r-- | gcc/gimple-range-cache.h | 31 | ||||
-rw-r--r-- | gcc/gimple-range-path.cc | 4 | ||||
-rw-r--r-- | gcc/gimple-range.cc | 27 | ||||
-rw-r--r-- | gcc/gimple-range.h | 1 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/pr104288.c | 23 | ||||
-rw-r--r-- | gcc/tree-vrp.cc | 8 |
7 files changed, 187 insertions, 42 deletions
diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc index 16c881b..6131352 100644 --- a/gcc/gimple-range-cache.cc +++ b/gcc/gimple-range-cache.cc @@ -29,6 +29,10 @@ along with GCC; see the file COPYING3. If not see #include "gimple-pretty-print.h" #include "gimple-range.h" #include "tree-cfg.h" +#include "target.h" +#include "attribs.h" +#include "gimple-iterator.h" +#include "gimple-walk.h" #define DEBUG_RANGE_CACHE (dump_file \ && (param_ranger_debug & RANGER_DEBUG_CACHE)) @@ -50,6 +54,21 @@ non_null_ref::~non_null_ref () m_nn.release (); } +// This routine will update NAME in BB to be nonnull if it is not already. +// return TRUE if the update happens. + +bool +non_null_ref::set_nonnull (basic_block bb, tree name) +{ + gcc_checking_assert (gimple_range_ssa_p (name) + && POINTER_TYPE_P (TREE_TYPE (name))); + // Only process when its not already set. + if (non_null_deref_p (name, bb, false)) + return false; + bitmap_set_bit (m_nn[SSA_NAME_VERSION (name)], bb->index); + return true; +} + // Return true if NAME has a non-null dereference in block bb. If this is the // first query for NAME, calculate the summary first. // If SEARCH_DOM is true, the search the dominator tree as well. @@ -87,35 +106,6 @@ non_null_ref::non_null_deref_p (tree name, basic_block bb, bool search_dom) return false; } -// If NAME has a non-null dereference in block BB, adjust R with the -// non-zero information from non_null_deref_p, and return TRUE. If -// SEARCH_DOM is true, non_null_deref_p should search the dominator tree. - -bool -non_null_ref::adjust_range (irange &r, tree name, basic_block bb, - bool search_dom) -{ - // Non-call exceptions mean we could throw in the middle of the - // block, so just punt on those for now. - if (cfun->can_throw_non_call_exceptions) - return false; - - // We only care about the null / non-null property of pointers. - if (!POINTER_TYPE_P (TREE_TYPE (name))) - return false; - if (r.undefined_p () || r.lower_bound () != 0 || r.upper_bound () == 0) - return false; - // Check if pointers have any non-null dereferences. - if (non_null_deref_p (name, bb, search_dom)) - { - // Remove zero from the range. - unsigned prec = TYPE_PRECISION (TREE_TYPE (name)); - r.intersect (wi::one (prec), wi::max_value (prec, UNSIGNED)); - return true; - } - return false; -} - // Allocate an populate the bitmap for NAME. An ON bit for a block // index indicates there is a non-null reference in that block. In // order to populate the bitmap, a quick run of all the immediate uses @@ -1014,9 +1004,6 @@ ranger_cache::range_of_def (irange &r, tree name, basic_block bb) else r = gimple_range_global (name); } - - if (bb) - m_non_null.adjust_range (r, name, bb, false); } // Get the range of NAME as it occurs on entry to block BB. @@ -1034,8 +1021,6 @@ ranger_cache::entry_range (irange &r, tree name, basic_block bb) // Otherwise pick up the best available global value. if (!m_on_entry.get_bb_range (r, name, bb)) range_of_def (r, name); - - m_non_null.adjust_range (r, name, bb, false); } // Get the range of NAME as it occurs on exit from block BB. @@ -1089,6 +1074,9 @@ ranger_cache::range_of_expr (irange &r, tree name, gimple *stmt) if (gimple_range_ssa_p (expr)) { exit_range (r, expr, e->src); + // If this is not an abnormal edge, check for a non-null exit. + if ((e->flags & (EDGE_EH | EDGE_ABNORMAL)) == 0) + m_non_null.adjust_range (r, expr, e->src, false); int_range_max edge_range; if (m_gori.outgoing_edge_range_p (edge_range, e, expr, *this)) r.intersect (edge_range); @@ -1467,3 +1455,82 @@ ranger_cache::range_from_dom (irange &r, tree name, basic_block bb) } return true; } + +// This routine will update NAME in block BB to the nonnull state. +// It will then update the on-entry cache for this block to be non-null +// if it isn't already. + +void +ranger_cache::update_to_nonnull (basic_block bb, tree name) +{ + tree type = TREE_TYPE (name); + if (gimple_range_ssa_p (name) && POINTER_TYPE_P (type)) + { + m_non_null.set_nonnull (bb, name); + // Update the on-entry cache for BB to be non-zero. Note this can set + // the on entry value in the DEF block, which can override the def. + int_range_max r; + exit_range (r, name, bb); + if (r.varying_p ()) + { + r.set_nonzero (type); + m_on_entry.set_bb_range (name, bb, r); + } + } +} + +// Adapted from infer_nonnull_range_by_dereference and check_loadstore +// to process nonnull ssa_name OP in S. DATA contains the ranger_cache. + +static bool +non_null_loadstore (gimple *s, tree op, tree, void *data) +{ + if (TREE_CODE (op) == MEM_REF || TREE_CODE (op) == TARGET_MEM_REF) + { + /* Some address spaces may legitimately dereference zero. */ + addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (op)); + if (!targetm.addr_space.zero_address_valid (as)) + { + tree ssa = TREE_OPERAND (op, 0); + basic_block bb = gimple_bb (s); + ((ranger_cache *)data)->update_to_nonnull (bb, ssa); + } + } + return false; +} + +// This routine is used during a block walk to move the state of non-null for +// any operands on stmt S to nonnull. + +void +ranger_cache::block_apply_nonnull (gimple *s) +{ + if (!flag_delete_null_pointer_checks) + return; + if (is_a<gphi *> (s)) + return; + if (gimple_code (s) == GIMPLE_ASM || gimple_clobber_p (s)) + return; + if (is_a<gcall *> (s)) + { + tree fntype = gimple_call_fntype (s); + bitmap nonnullargs = get_nonnull_args (fntype); + // Process any non-null arguments + if (nonnullargs) + { + basic_block bb = gimple_bb (s); + for (unsigned i = 0; i < gimple_call_num_args (s); i++) + { + if (bitmap_empty_p (nonnullargs) || bitmap_bit_p (nonnullargs, i)) + { + tree op = gimple_call_arg (s, i); + update_to_nonnull (bb, op); + } + } + BITMAP_FREE (nonnullargs); + } + // Fallthru and walk load/store ops now. + } + walk_stmt_load_store_ops (s, (void *)this, non_null_loadstore, + non_null_loadstore); +} diff --git a/gcc/gimple-range-cache.h b/gcc/gimple-range-cache.h index b54b688..589b649 100644 --- a/gcc/gimple-range-cache.h +++ b/gcc/gimple-range-cache.h @@ -36,12 +36,41 @@ public: bool non_null_deref_p (tree name, basic_block bb, bool search_dom = true); bool adjust_range (irange &r, tree name, basic_block bb, bool search_dom = true); + bool set_nonnull (basic_block bb, tree name); private: vec <bitmap> m_nn; void process_name (tree name); bitmap_obstack m_bitmaps; }; +// If NAME has a non-null dereference in block BB, adjust R with the +// non-zero information from non_null_deref_p, and return TRUE. If +// SEARCH_DOM is true, non_null_deref_p should search the dominator tree. + +inline bool +non_null_ref::adjust_range (irange &r, tree name, basic_block bb, + bool search_dom) +{ + // Non-call exceptions mean we could throw in the middle of the + // block, so just punt on those for now. + if (cfun->can_throw_non_call_exceptions) + return false; + // We only care about the null / non-null property of pointers. + if (!POINTER_TYPE_P (TREE_TYPE (name))) + return false; + if (r.undefined_p () || r.lower_bound () != 0 || r.upper_bound () == 0) + return false; + // Check if pointers have any non-null dereferences. + if (non_null_deref_p (name, bb, search_dom)) + { + // Remove zero from the range. + unsigned prec = TYPE_PRECISION (TREE_TYPE (name)); + r.intersect (wi::one (prec), wi::max_value (prec, UNSIGNED)); + return true; + } + return false; +} + // This class manages a vector of pointers to ssa_block ranges. It // provides the basis for the "range on entry" cache for all // SSA names. @@ -106,6 +135,8 @@ public: void propagate_updated_value (tree name, basic_block bb); + void block_apply_nonnull (gimple *s); + void update_to_nonnull (basic_block bb, tree name); non_null_ref m_non_null; gori_compute m_gori; diff --git a/gcc/gimple-range-path.cc b/gcc/gimple-range-path.cc index 3bf9bd1..483bcd2 100644 --- a/gcc/gimple-range-path.cc +++ b/gcc/gimple-range-path.cc @@ -358,7 +358,7 @@ path_range_query::range_defined_in_block (irange &r, tree name, basic_block bb) } if (bb) - m_non_null.adjust_range (r, name, bb); + m_non_null.adjust_range (r, name, bb, false); if (DEBUG_SOLVER && (bb || !r.varying_p ())) { @@ -528,7 +528,7 @@ path_range_query::adjust_for_non_null_uses (basic_block bb) else r.set_varying (TREE_TYPE (name)); - if (m_non_null.adjust_range (r, name, bb)) + if (m_non_null.adjust_range (r, name, bb, false)) set_cache (r, name); } } diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc index 73398dd..04075a9 100644 --- a/gcc/gimple-range.cc +++ b/gcc/gimple-range.cc @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-scalar-evolution.h" #include "gimple-range.h" #include "gimple-fold.h" +#include "gimple-walk.h" gimple_ranger::gimple_ranger () : non_executable_edge_flag (cfun), @@ -117,8 +118,10 @@ gimple_ranger::range_of_expr (irange &r, tree expr, gimple *stmt) // If name is defined in this block, try to get an range from S. if (def_stmt && gimple_bb (def_stmt) == bb) { - range_of_stmt (r, def_stmt, expr); - m_cache.m_non_null.adjust_range (r, expr, bb, true); + // Check for a definition override from a block walk. + if (!POINTER_TYPE_P (TREE_TYPE (expr)) + || !m_cache.block_range (r, bb, expr, false)) + range_of_stmt (r, def_stmt, expr); } // Otherwise OP comes from outside this block, use range on entry. else @@ -151,7 +154,12 @@ gimple_ranger::range_on_entry (irange &r, basic_block bb, tree name) if (m_cache.block_range (entry_range, bb, name)) r.intersect (entry_range); - m_cache.m_non_null.adjust_range (r, name, bb, true); + if (dom_info_available_p (CDI_DOMINATORS)) + { + basic_block dom_bb = get_immediate_dominator (CDI_DOMINATORS, bb); + if (dom_bb) + m_cache.m_non_null.adjust_range (r, name, dom_bb, true); + } if (idx) tracer.trailer (idx, "range_on_entry", true, name, r); @@ -227,6 +235,9 @@ gimple_ranger::range_on_edge (irange &r, edge e, tree name) else { range_on_exit (r, e->src, name); + // If this is not an abnormal edge, check for a non-null exit . + if ((e->flags & (EDGE_EH | EDGE_ABNORMAL)) == 0) + m_cache.m_non_null.adjust_range (r, name, e->src, false); gcc_checking_assert (r.undefined_p () || range_compatible_p (r.type(), TREE_TYPE (name))); @@ -436,6 +447,16 @@ gimple_ranger::fold_stmt (gimple_stmt_iterator *gsi, tree (*valueize) (tree)) return ret; } +// Called during dominator walks to register any side effects that take effect +// from this point forward. Current release is only for tracking non-null +// within a block. + +void +gimple_ranger::register_side_effects (gimple *s) +{ + m_cache.block_apply_nonnull (s); +} + // This routine will export whatever global ranges are known to GCC // SSA_RANGE_NAME_INFO and SSA_NAME_PTR_INFO fields. diff --git a/gcc/gimple-range.h b/gcc/gimple-range.h index ec56815..0733a53 100644 --- a/gcc/gimple-range.h +++ b/gcc/gimple-range.h @@ -60,6 +60,7 @@ public: void dump_bb (FILE *f, basic_block bb); auto_edge_flag non_executable_edge_flag; bool fold_stmt (gimple_stmt_iterator *gsi, tree (*) (tree)); + void register_side_effects (gimple *s); protected: bool fold_range_internal (irange &r, gimple *s, tree name); void prefill_name (irange &r, tree name); diff --git a/gcc/testsuite/gcc.dg/pr104288.c b/gcc/testsuite/gcc.dg/pr104288.c new file mode 100644 index 0000000..95eb196 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr104288.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-evrp -fdelete-null-pointer-checks" } */ +/* { dg-skip-if "" { keeps_null_pointer_checks } } */ + +void keep(int result) __attribute__((noipa)); +void keep(int result) +{ + if (result) + __builtin_exit(0); +} + +void foo (void *p) __attribute__((nonnull(1))); + +void bar (void *p) +{ + keep (p == 0); + foo (p); + if (!p) + __builtin_abort (); +} + +/* { dg-final { scan-tree-dump-not "abort" "evrp" } } */ +/* { dg-final { scan-tree-dump-times "== 0B;" 1 "evrp" } } */ diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc index 6294645..e9f19d0 100644 --- a/gcc/tree-vrp.cc +++ b/gcc/tree-vrp.cc @@ -4309,9 +4309,11 @@ public: bool fold_stmt (gimple_stmt_iterator *gsi) OVERRIDE { - if (m_simplifier.simplify (gsi)) - return true; - return m_ranger->fold_stmt (gsi, follow_single_use_edges); + bool ret = m_simplifier.simplify (gsi); + if (!ret) + ret = m_ranger->fold_stmt (gsi, follow_single_use_edges); + m_ranger->register_side_effects (gsi_stmt (*gsi)); + return ret; } private: |