mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
New early __builtin_unreachable processing.
in VRP passes before __builtin_unreachable MUST be removed, only remove it if all exports affected by the unreachable can have global values updated, and do not involve loads from memory. PR tree-optimization/110080 PR tree-optimization/110249 gcc/ * tree-vrp.cc (remove_unreachable::final_p): New. (remove_unreachable::maybe_register): Rename from maybe_register_block and call early or final routine. (fully_replaceable): New. (remove_unreachable::handle_early): New. (remove_unreachable::remove_and_update_globals): Remove non-final processing. (rvrp_folder::rvrp_folder): Add final flag to constructor. (rvrp_folder::post_fold_bb): Remove unreachable registration. (rvrp_folder::pre_fold_stmt): Move unreachable processing to here. (execute_ranger_vrp): Adjust some call parameters. gcc/testsuite/ * g++.dg/pr110249.C: New. * gcc.dg/pr110080.c: New. * gcc.dg/pr93917.c: Adjust.
This commit is contained in:
16
gcc/testsuite/g++.dg/pr110249.C
Normal file
16
gcc/testsuite/g++.dg/pr110249.C
Normal file
@@ -0,0 +1,16 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-vrp1-alias" } */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
uint64_t read64r(const uint64_t &x) {
|
||||
if ((uint64_t) &x % 8 ) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
uint64_t value;
|
||||
memcpy( &value, &x, sizeof(uint64_t) );
|
||||
return value;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump "fff8" "vrp1" } } */
|
||||
27
gcc/testsuite/gcc.dg/pr110080.c
Normal file
27
gcc/testsuite/gcc.dg/pr110080.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-optimized" } */
|
||||
|
||||
void foo(void);
|
||||
static unsigned char a = 131;
|
||||
static int *b;
|
||||
static int **c = &b;
|
||||
static void d(int e, unsigned f) {
|
||||
int *g;
|
||||
if (f != 131) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
if (!e){
|
||||
for (; a; ++a)
|
||||
for (e = 0; 0;)
|
||||
;
|
||||
g = &e;
|
||||
int **h = &g;
|
||||
if (**h) {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
*c = &e;
|
||||
}
|
||||
int main() { d(4 & a, a); }
|
||||
|
||||
/* { dg-final { scan-tree-dump-not "foo" "optimized" } } */
|
||||
@@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-vrp1" } */
|
||||
/* { dg-options "-O2 -fdump-tree-vrp1 -fdump-tree-vrp2" } */
|
||||
|
||||
void f3(int n);
|
||||
|
||||
@@ -17,4 +17,7 @@ void f2(int*n)
|
||||
f3 (*n);
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "Global Exported" 2 "vrp1" } } */
|
||||
/* { dg-final { scan-tree-dump-times "Global Export.*0, \\+INF" 1 "vrp1" } } */
|
||||
/* { dg-final { scan-tree-dump-times "__builtin_unreachable" 1 "vrp1" } } */
|
||||
/* { dg-final { scan-tree-dump-times "Global Export.*0, \\+INF" 1 "vrp2" } } */
|
||||
/* { dg-final { scan-tree-dump-times "__builtin_unreachable" 0 "vrp2" } } */
|
||||
|
||||
203
gcc/tree-vrp.cc
203
gcc/tree-vrp.cc
@@ -56,11 +56,17 @@ along with GCC; see the file COPYING3. If not see
|
||||
// This class is utilized by VRP and ranger to remove __builtin_unreachable
|
||||
// calls, and reflect any resulting global ranges.
|
||||
//
|
||||
// maybe_register_block () is called on basic blocks, and if that block
|
||||
// matches the pattern of one branch being a builtin_unreachable, register
|
||||
// the resulting executable edge in a list.
|
||||
// maybe_register() is called on condition statements , and if that
|
||||
// matches the pattern of one branch being a builtin_unreachable, either check
|
||||
// for early removal or register the resulting executable edge in a list.
|
||||
//
|
||||
// After all blocks have been processed, remove_and_update_globals() will
|
||||
// During early/non-final processing, we check to see if ALL exports from the
|
||||
// block can be safely updated with a new global value. If they can, then
|
||||
// we rewrite the condition and update those values immediately. Otherwise
|
||||
// the unreachable condition is left in the IL until the final pass.
|
||||
//
|
||||
// During final processing, after all blocks have been registered,
|
||||
// remove_and_update_globals() will
|
||||
// - check all exports from registered blocks
|
||||
// - ensure the cache entry of each export is set with the appropriate range
|
||||
// - rewrite the conditions to take the executable edge
|
||||
@@ -71,23 +77,25 @@ along with GCC; see the file COPYING3. If not see
|
||||
|
||||
class remove_unreachable {
|
||||
public:
|
||||
remove_unreachable (gimple_ranger &r) : m_ranger (r) { m_list.create (30); }
|
||||
remove_unreachable (gimple_ranger &r, bool all) : m_ranger (r), final_p (all)
|
||||
{ m_list.create (30); }
|
||||
~remove_unreachable () { m_list.release (); }
|
||||
void maybe_register_block (basic_block bb);
|
||||
bool remove_and_update_globals (bool final_p);
|
||||
void handle_early (gimple *s, edge e);
|
||||
void maybe_register (gimple *s);
|
||||
bool remove_and_update_globals ();
|
||||
vec<std::pair<int, int> > m_list;
|
||||
gimple_ranger &m_ranger;
|
||||
bool final_p;
|
||||
};
|
||||
|
||||
// Check if block BB has a __builtin_unreachable () call on one arm, and
|
||||
// register the executable edge if so.
|
||||
|
||||
void
|
||||
remove_unreachable::maybe_register_block (basic_block bb)
|
||||
remove_unreachable::maybe_register (gimple *s)
|
||||
{
|
||||
gimple *s = gimple_outgoing_range_stmt_p (bb);
|
||||
if (!s || gimple_code (s) != GIMPLE_COND)
|
||||
return;
|
||||
gcc_checking_assert (gimple_code (s) == GIMPLE_COND);
|
||||
basic_block bb = gimple_bb (s);
|
||||
|
||||
edge e0 = EDGE_SUCC (bb, 0);
|
||||
basic_block bb0 = e0->dest;
|
||||
@@ -102,21 +110,150 @@ remove_unreachable::maybe_register_block (basic_block bb)
|
||||
if (un0 == un1)
|
||||
return;
|
||||
|
||||
if (un0)
|
||||
m_list.safe_push (std::make_pair (e1->src->index, e1->dest->index));
|
||||
// Constant expressions are ignored.
|
||||
if (TREE_CODE (gimple_cond_lhs (s)) != SSA_NAME
|
||||
&& TREE_CODE (gimple_cond_rhs (s)) != SSA_NAME)
|
||||
return;
|
||||
|
||||
edge e = un0 ? e1 : e0;
|
||||
|
||||
if (!final_p)
|
||||
handle_early (s, e);
|
||||
else
|
||||
m_list.safe_push (std::make_pair (e0->src->index, e0->dest->index));
|
||||
m_list.safe_push (std::make_pair (e->src->index, e->dest->index));
|
||||
}
|
||||
|
||||
// Return true if all uses of NAME are dominated by by block BB. 1 use
|
||||
// is allowed in block BB, This is one we hope to remove.
|
||||
// ie
|
||||
// _2 = _1 & 7;
|
||||
// if (_2 != 0)
|
||||
// goto <bb 3>; [0.00%]
|
||||
// Any additional use of _1 or _2 in this block invalidates early replacement.
|
||||
|
||||
static bool
|
||||
fully_replaceable (tree name, basic_block bb)
|
||||
{
|
||||
use_operand_p use_p;
|
||||
imm_use_iterator iter;
|
||||
bool saw_in_bb = false;
|
||||
|
||||
// If a name loads from memory, we may lose information used in
|
||||
// commoning opportunities later. See tree-ssa/ssa-pre-34.c.
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (name);
|
||||
if (gimple_vuse (def_stmt))
|
||||
return false;
|
||||
|
||||
FOR_EACH_IMM_USE_FAST (use_p, iter, name)
|
||||
{
|
||||
gimple *use_stmt = USE_STMT (use_p);
|
||||
// Ignore debug stmts and the branch.
|
||||
if (is_gimple_debug (use_stmt))
|
||||
continue;
|
||||
basic_block use_bb = gimple_bb (use_stmt);
|
||||
// Only one use in the block allowed to avoid complicated cases.
|
||||
if (use_bb == bb)
|
||||
{
|
||||
if (saw_in_bb)
|
||||
return false;
|
||||
else
|
||||
saw_in_bb = true;
|
||||
}
|
||||
else if (!dominated_by_p (CDI_DOMINATORS, use_bb, bb))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This routine is called to check builtin_unreachable calls during any
|
||||
// time before final removal. The only way we can be sure it does not
|
||||
// provide any additional information is to expect that we can update the
|
||||
// global values of all exports from a block. This means the branch
|
||||
// to the unreachable call must dominate all uses of those ssa-names, with
|
||||
// the exception that there can be a single use in the block containing
|
||||
// the branch. IF the name used in the branch is defined in the block, it may
|
||||
// contain the name of something else that will be an export. And likewise
|
||||
// that may also use another name that is an export etc.
|
||||
//
|
||||
// As long as there is only a single use, we can be sure that there are no other
|
||||
// side effects (like being passed to a call, or stored to a global, etc.
|
||||
// This means we will miss cases where there are 2 or more uses that have
|
||||
// no interveneing statements that may had side effects, but it catches most
|
||||
// of the caes we care about, and prevents expensive in depth analysis.
|
||||
//
|
||||
// Ranger will still reflect the proper ranges at other places in these missed
|
||||
// cases, we simply will not remove/set globals early.
|
||||
|
||||
void
|
||||
remove_unreachable::handle_early (gimple *s, edge e)
|
||||
{
|
||||
bool lhs_p = TREE_CODE (gimple_cond_lhs (s)) == SSA_NAME;
|
||||
bool rhs_p = TREE_CODE (gimple_cond_rhs (s)) == SSA_NAME;
|
||||
// Do not remove __builtin_unreachable if it confers a relation, or
|
||||
// that relation may be lost in subsequent passes.
|
||||
if (lhs_p && rhs_p)
|
||||
return;
|
||||
// Do not remove addresses early. ie if (x == &y)
|
||||
if (lhs_p && TREE_CODE (gimple_cond_rhs (s)) == ADDR_EXPR)
|
||||
return;
|
||||
|
||||
gcc_checking_assert (gimple_outgoing_range_stmt_p (e->src) == s);
|
||||
gcc_checking_assert (!final_p);
|
||||
|
||||
// Check if every export use is dominated by this branch.
|
||||
tree name;
|
||||
FOR_EACH_GORI_EXPORT_NAME (m_ranger.gori (), e->src, name)
|
||||
{
|
||||
if (!fully_replaceable (name, e->src))
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the global value for each.
|
||||
FOR_EACH_GORI_EXPORT_NAME (m_ranger.gori (), e->src, name)
|
||||
{
|
||||
Value_Range r (TREE_TYPE (name));
|
||||
m_ranger.range_on_entry (r, e->dest, name);
|
||||
// Nothing at this late stage we can do if the write fails.
|
||||
if (!set_range_info (name, r))
|
||||
continue;
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "Global Exported (via early unreachable): ");
|
||||
print_generic_expr (dump_file, name, TDF_SLIM);
|
||||
fprintf (dump_file, " = ");
|
||||
gimple_range_global (r, name);
|
||||
r.dump (dump_file);
|
||||
fputc ('\n', dump_file);
|
||||
}
|
||||
}
|
||||
|
||||
tree ssa = lhs_p ? gimple_cond_lhs (s) : gimple_cond_rhs (s);
|
||||
|
||||
// Rewrite the condition.
|
||||
if (e->flags & EDGE_TRUE_VALUE)
|
||||
gimple_cond_make_true (as_a<gcond *> (s));
|
||||
else
|
||||
gimple_cond_make_false (as_a<gcond *> (s));
|
||||
update_stmt (s);
|
||||
|
||||
// If the name on S is defined in this block, see if there is DCE work to do.
|
||||
if (gimple_bb (SSA_NAME_DEF_STMT (ssa)) == e->src)
|
||||
{
|
||||
auto_bitmap dce;
|
||||
bitmap_set_bit (dce, SSA_NAME_VERSION (ssa));
|
||||
simple_dce_from_worklist (dce);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process the edges in the list, change the conditions and removing any
|
||||
// dead code feeding those conditions. Calculate the range of any
|
||||
// names that may have been exported from those blocks, and determine if
|
||||
// there is any updates to their global ranges..
|
||||
// FINAL_P indicates all builtin_unreachable calls should be removed.
|
||||
// Return true if any builtin_unreachables/globals eliminated/updated.
|
||||
|
||||
bool
|
||||
remove_unreachable::remove_and_update_globals (bool final_p)
|
||||
remove_unreachable::remove_and_update_globals ()
|
||||
{
|
||||
if (m_list.length () == 0)
|
||||
return false;
|
||||
@@ -140,19 +277,7 @@ remove_unreachable::remove_and_update_globals (bool final_p)
|
||||
edge e = find_edge (src, dest);
|
||||
gimple *s = gimple_outgoing_range_stmt_p (e->src);
|
||||
gcc_checking_assert (gimple_code (s) == GIMPLE_COND);
|
||||
bool lhs_p = TREE_CODE (gimple_cond_lhs (s)) == SSA_NAME;
|
||||
bool rhs_p = TREE_CODE (gimple_cond_rhs (s)) == SSA_NAME;
|
||||
// Do not remove __builtin_unreachable if it confers a relation, or
|
||||
// that relation will be lost in subsequent passes. Unless its the
|
||||
// final pass.
|
||||
if (!final_p && lhs_p && rhs_p)
|
||||
continue;
|
||||
// If this is already a constant condition, don't look either
|
||||
if (!lhs_p && !rhs_p)
|
||||
continue;
|
||||
// Do not remove addresses early. ie if (x == &y)
|
||||
if (!final_p && lhs_p && TREE_CODE (gimple_cond_rhs (s)) == ADDR_EXPR)
|
||||
continue;
|
||||
|
||||
bool dominate_exit_p = true;
|
||||
FOR_EACH_GORI_EXPORT_NAME (m_ranger.gori (), e->src, name)
|
||||
{
|
||||
@@ -827,9 +952,10 @@ class rvrp_folder : public substitute_and_fold_engine
|
||||
{
|
||||
public:
|
||||
|
||||
rvrp_folder (gimple_ranger *r) : substitute_and_fold_engine (),
|
||||
m_unreachable (*r),
|
||||
m_simplifier (r, r->non_executable_edge_flag)
|
||||
rvrp_folder (gimple_ranger *r, bool all)
|
||||
: substitute_and_fold_engine (),
|
||||
m_unreachable (*r, all),
|
||||
m_simplifier (r, r->non_executable_edge_flag)
|
||||
{
|
||||
m_ranger = r;
|
||||
m_pta = new pointer_equiv_analyzer (m_ranger);
|
||||
@@ -883,8 +1009,6 @@ public:
|
||||
void post_fold_bb (basic_block bb) override
|
||||
{
|
||||
m_pta->leave (bb);
|
||||
if (cfun->after_inlining)
|
||||
m_unreachable.maybe_register_block (bb);
|
||||
}
|
||||
|
||||
void pre_fold_stmt (gimple *stmt) override
|
||||
@@ -893,7 +1017,12 @@ public:
|
||||
// If this is the last stmt and there are inferred ranges, reparse the
|
||||
// block for transitive inferred ranges that occur earlier in the block.
|
||||
if (stmt == m_last_bb_stmt)
|
||||
m_ranger->register_transitive_inferred_ranges (gimple_bb (stmt));
|
||||
{
|
||||
m_ranger->register_transitive_inferred_ranges (gimple_bb (stmt));
|
||||
// Also check for builtin_unreachable calls.
|
||||
if (cfun->after_inlining && gimple_code (stmt) == GIMPLE_COND)
|
||||
m_unreachable.maybe_register (stmt);
|
||||
}
|
||||
}
|
||||
|
||||
bool fold_stmt (gimple_stmt_iterator *gsi) override
|
||||
@@ -928,11 +1057,11 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p,
|
||||
|
||||
set_all_edges_as_executable (fun);
|
||||
gimple_ranger *ranger = enable_ranger (fun, false);
|
||||
rvrp_folder folder (ranger);
|
||||
rvrp_folder folder (ranger, final_p);
|
||||
phi_analysis_initialize (ranger->const_query ());
|
||||
folder.substitute_and_fold ();
|
||||
// Remove tagged builtin-unreachable and maybe update globals.
|
||||
folder.m_unreachable.remove_and_update_globals (final_p);
|
||||
folder.m_unreachable.remove_and_update_globals ();
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
ranger->dump (dump_file);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user