--- /dev/null
+/* Code for GIMPLE range trace and debugging related routines.
+ Copyright (C) 2019-2021 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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "fold-const.h"
+#include "tree-cfg.h"
+#include "cfgloop.h"
+#include "tree-scalar-evolution.h"
+#include "gimple-range.h"
+
+
+// Breakpoint to trap at a specific index. From GDB, this provides a simple
+// place to put a breakpoint to stop at a given trace line.
+// ie. b range_tracer::breakpoint if index == 45678
+
+void
+range_tracer::breakpoint (unsigned index ATTRIBUTE_UNUSED)
+{
+}
+
+// Construct a range_tracer with component NAME.
+
+range_tracer::range_tracer (const char *name)
+{
+ gcc_checking_assert (strlen(name) < name_len -1);
+ strcpy (component, name);
+ indent = 0;
+ tracing = false;
+}
+
+// This routine does the initial line spacing/indenting for a trace.
+// If BLANKS is false, then IDX is printed, otherwise spaces.
+
+void
+range_tracer::print_prefix (unsigned idx, bool blanks)
+{
+ // Print counter index as well as INDENT spaces.
+ if (!blanks)
+ fprintf (dump_file, "%-7u ", idx);
+ else
+ fprintf (dump_file, " ");
+ fprintf (dump_file, "%s ", component);
+ unsigned x;
+ for (x = 0; x< indent; x++)
+ fputc (' ', dump_file);
+
+}
+// If dumping, return the next call index and print the prefix for the next
+// output line. If not, retrurn 0.
+// Counter is static to monotonically increase across the compilation unit.
+
+unsigned
+range_tracer::do_header (const char *str)
+{
+ static unsigned trace_count = 0;
+
+ unsigned idx = ++trace_count;
+ print_prefix (idx, false);
+ fprintf (dump_file, "%s", str);
+ indent += bump;
+ breakpoint (idx);
+ return idx;
+}
+
+// Print a line without starting or ending a trace.
+
+void
+range_tracer::print (unsigned counter, const char *str)
+{
+ print_prefix (counter, true);
+ fprintf (dump_file, "%s", str);
+}
+
+// End a trace and print the CALLER, NAME, and RESULT and range R,
+
+void
+range_tracer::trailer (unsigned counter, const char *caller, bool result,
+ tree name, const irange &r)
+{
+ gcc_checking_assert (tracing && counter != 0);
+
+ indent -= bump;
+ print_prefix (counter, true);
+ fputs(result ? "TRUE : " : "FALSE : ", dump_file);
+ fprintf (dump_file, "(%u) ", counter);
+ fputs (caller, dump_file);
+ fputs (" (",dump_file);
+ if (name)
+ print_generic_expr (dump_file, name, TDF_SLIM);
+ fputs (") ",dump_file);
+ if (result)
+ {
+ r.dump (dump_file);
+ fputc('\n', dump_file);
+ }
+ else
+ fputc('\n', dump_file);
+}
+
+// =========================================
+// Debugging helpers.
+// =========================================
+
+// Query all statements in the IL to precalculate computable ranges in RANGER.
+
+static DEBUG_FUNCTION void
+debug_seed_ranger (gimple_ranger &ranger)
+{
+ // Recalculate SCEV to make sure the dump lists everything.
+ if (scev_initialized_p ())
+ {
+ scev_finalize ();
+ scev_initialize ();
+ }
+
+ basic_block bb;
+ int_range_max r;
+ gimple_stmt_iterator gsi;
+ FOR_EACH_BB_FN (bb, cfun)
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+
+ if (is_gimple_debug (stmt))
+ continue;
+
+ ranger.range_of_stmt (r, stmt);
+ }
+}
+
+// Dump all that ranger knows for the current function.
+
+DEBUG_FUNCTION void
+dump_ranger (FILE *out)
+{
+ gimple_ranger ranger;
+ debug_seed_ranger (ranger);
+ ranger.dump (out);
+}
+
+DEBUG_FUNCTION void
+debug_ranger ()
+{
+ dump_ranger (stderr);
+}
+
+// Dump all that ranger knows on a path of BBs.
+//
+// Note that the blocks are in reverse order, thus the exit block is
+// path[0].
+
+DEBUG_FUNCTION void
+dump_ranger (FILE *dump_file, const vec<basic_block> &path)
+{
+ if (path.length () == 0)
+ {
+ fprintf (dump_file, "empty\n");
+ return;
+ }
+
+ gimple_ranger ranger;
+ debug_seed_ranger (ranger);
+
+ unsigned i = path.length ();
+ do
+ {
+ i--;
+ ranger.dump_bb (dump_file, path[i]);
+ }
+ while (i > 0);
+}
+
+DEBUG_FUNCTION void
+debug_ranger (const vec<basic_block> &path)
+{
+ dump_ranger (stderr, path);
+}
+
+#include "gimple-range-tests.cc"
#include "tree-scalar-evolution.h"
#include "gimple-range.h"
-gimple_ranger::gimple_ranger ()
+gimple_ranger::gimple_ranger () : tracer ("")
{
// If the cache has a relation oracle, use it.
m_oracle = m_cache.oracle ();
+ if (dump_file && (param_evrp_mode & EVRP_MODE_TRACE))
+ tracer.enable_trace ();
}
bool
gimple_ranger::range_of_expr (irange &r, tree expr, gimple *stmt)
{
+ unsigned idx;
if (!gimple_range_ssa_p (expr))
return get_tree_range (r, expr, stmt);
+ if ((idx = tracer.header ("range_of_expr(")))
+ {
+ print_generic_expr (dump_file, expr, TDF_SLIM);
+ fputs (")", dump_file);
+ if (stmt)
+ {
+ fputs (" at stmt ", dump_file);
+ print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+ }
+ else
+ fputs ("\n", dump_file);
+ }
+
// If there is no statement, just get the global value.
if (!stmt)
{
if (!m_cache.get_global_range (r, expr))
r = gimple_range_global (expr);
- return true;
}
-
// For a debug stmt, pick the best value currently available, do not
// trigger new value calculations. PR 100781.
- if (is_gimple_debug (stmt))
+ else if (is_gimple_debug (stmt))
+ m_cache.range_of_expr (r, expr, stmt);
+ else
{
- m_cache.range_of_expr (r, expr, stmt);
- return true;
- }
- basic_block bb = gimple_bb (stmt);
- gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
+ basic_block bb = gimple_bb (stmt);
+ gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
- // 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);
+ // 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);
+ }
+ // Otherwise OP comes from outside this block, use range on entry.
+ else
+ range_on_entry (r, bb, expr);
}
- else
- // Otherwise OP comes from outside this block, use range on entry.
- range_on_entry (r, bb, expr);
-
+ if (idx)
+ tracer.trailer (idx, "range_of_expr", true, expr, r);
return true;
}
int_range_max entry_range;
gcc_checking_assert (gimple_range_ssa_p (name));
+ unsigned idx;
+ if ((idx = tracer.header ("range_on_entry (")))
+ {
+ print_generic_expr (dump_file, name, TDF_SLIM);
+ fprintf (dump_file, ") to BB %d\n", bb->index);
+ }
+
// Start with any known range
range_of_stmt (r, SSA_NAME_DEF_STMT (name), name);
r.intersect (entry_range);
m_cache.m_non_null.adjust_range (r, name, bb, true);
+
+ if (idx)
+ tracer.trailer (idx, "range_on_entry", true, name, r);
}
// Calculate the range for NAME at the end of block BB and return it in R.
gcc_checking_assert (bb != EXIT_BLOCK_PTR_FOR_FN (cfun));
gcc_checking_assert (gimple_range_ssa_p (name));
+ unsigned idx;
+ if ((idx = tracer.header ("range_on_exit (")))
+ {
+ print_generic_expr (dump_file, name, TDF_SLIM);
+ fprintf (dump_file, ") from BB %d\n", bb->index);
+ }
+
gimple *s = SSA_NAME_DEF_STMT (name);
basic_block def_bb = gimple_bb (s);
// If this is not the definition block, get the range on the last stmt in
range_on_entry (r, bb, name);
gcc_checking_assert (r.undefined_p ()
|| range_compatible_p (r.type (), TREE_TYPE (name)));
+
+ if (idx)
+ tracer.trailer (idx, "range_on_exit", true, name, r);
}
// Calculate a range for NAME on edge E and return it in R.
if (!gimple_range_ssa_p (name))
return range_of_expr (r, name);
+ unsigned idx;
+ if ((idx = tracer.header ("range_on_edge (")))
+ {
+ print_generic_expr (dump_file, name, TDF_SLIM);
+ fprintf (dump_file, ") on edge %d->%d\n", e->src->index, e->dest->index);
+ }
+
range_on_exit (r, e->src, name);
gcc_checking_assert (r.undefined_p ()
|| range_compatible_p (r.type(), TREE_TYPE (name)));
if (m_cache.range_on_edge (edge_range, e, name))
r.intersect (edge_range);
+ if (idx)
+ tracer.trailer (idx, "range_on_edge", true, name, r);
return true;
}
bool
gimple_ranger::range_of_stmt (irange &r, gimple *s, tree name)
{
+ bool res;
r.set_undefined ();
+ unsigned idx;
+ if ((idx = tracer.header ("range_of_stmt (")))
+ {
+ if (name)
+ print_generic_expr (dump_file, name, TDF_SLIM);
+ fputs (") at stmt ", dump_file);
+ print_gimple_stmt (dump_file, s, 0, TDF_SLIM);
+ }
+
if (!name)
name = gimple_get_lhs (s);
// If no name, simply call the base routine.
if (!name)
- return fold_range_internal (r, s, NULL_TREE);
-
- if (!gimple_range_ssa_p (name))
- return false;
-
+ res = fold_range_internal (r, s, NULL_TREE);
+ else if (!gimple_range_ssa_p (name))
+ res = false;
// Check if the stmt has already been processed, and is not stale.
- if (m_cache.get_non_stale_global_range (r, name))
- return true;
-
- // Otherwise calculate a new value.
- int_range_max tmp;
- fold_range_internal (tmp, s, name);
-
- // Combine the new value with the old value. This is required because
- // the way value propagation works, when the IL changes on the fly we
- // can sometimes get different results. See PR 97741.
- r.intersect (tmp);
- m_cache.set_global_range (name, r);
+ else if (m_cache.get_non_stale_global_range (r, name))
+ {
+ if (idx)
+ tracer.trailer (idx, " cached", true, name, r);
+ return true;
+ }
+ else
+ {
+ // Otherwise calculate a new value.
+ int_range_max tmp;
+ fold_range_internal (tmp, s, name);
+
+ // Combine the new value with the old value. This is required because
+ // the way value propagation works, when the IL changes on the fly we
+ // can sometimes get different results. See PR 97741.
+ r.intersect (tmp);
+ m_cache.set_global_range (name, r);
+ res = true;
+ }
- return true;
+ if (idx)
+ tracer.trailer (idx, "range_of_stmt", res, name, r);
+ return res;
}
// This routine will export whatever global ranges are known to GCC
unsigned x;
edge_iterator ei;
edge e;
- int_range_max range;
+ int_range_max range, tmp_range;
fprintf (f, "\n=========== BB %d ============\n", bb->index);
m_cache.dump_bb (f, bb);
// the on entry cache for either end of the edge is
// set.
if ((s && bb == gimple_bb (s)) ||
- m_cache.block_range (range, bb, name, false) ||
- m_cache.block_range (range, e->dest, name, false))
+ m_cache.block_range (tmp_range, bb, name, false) ||
+ m_cache.block_range (tmp_range, e->dest, name, false))
{
- m_cache.range_on_edge (range, e, name);
if (!range.varying_p ())
{
fprintf (f, "%d->%d ", e->src->index,
m_cache.dump (f);
}
-// trace_ranger implementation.
-
-
-trace_ranger::trace_ranger ()
-{
- indent = 0;
- trace_count = 0;
-}
-
-// If dumping, return true and print the prefix for the next output line.
-
-bool
-trace_ranger::dumping (unsigned counter, bool trailing)
-{
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- // Print counter index as well as INDENT spaces.
- if (!trailing)
- fprintf (dump_file, " %-7u ", counter);
- else
- fprintf (dump_file, " ");
- unsigned x;
- for (x = 0; x< indent; x++)
- fputc (' ', dump_file);
- return true;
- }
- return false;
-}
-
-// After calling a routine, if dumping, print the CALLER, NAME, and RESULT,
-// returning RESULT.
-
-bool
-trace_ranger::trailer (unsigned counter, const char *caller, bool result,
- tree name, const irange &r)
-{
- if (dumping (counter, true))
- {
- indent -= bump;
- fputs(result ? "TRUE : " : "FALSE : ", dump_file);
- fprintf (dump_file, "(%u) ", counter);
- fputs (caller, dump_file);
- fputs (" (",dump_file);
- if (name)
- print_generic_expr (dump_file, name, TDF_SLIM);
- fputs (") ",dump_file);
- if (result)
- {
- r.dump (dump_file);
- fputc('\n', dump_file);
- }
- else
- fputc('\n', dump_file);
- // Marks the end of a request.
- if (indent == 0)
- fputc('\n', dump_file);
- }
- return result;
-}
-
-// Tracing version of range_on_edge. Call it with printing wrappers.
-
-bool
-trace_ranger::range_on_edge (irange &r, edge e, tree name)
-{
- unsigned idx = ++trace_count;
- if (dumping (idx))
- {
- fprintf (dump_file, "range_on_edge (");
- print_generic_expr (dump_file, name, TDF_SLIM);
- fprintf (dump_file, ") on edge %d->%d\n", e->src->index, e->dest->index);
- indent += bump;
- }
-
- bool res = gimple_ranger::range_on_edge (r, e, name);
- trailer (idx, "range_on_edge", true, name, r);
- return res;
-}
-
-// Tracing version of range_on_entry. Call it with printing wrappers.
-
-void
-trace_ranger::range_on_entry (irange &r, basic_block bb, tree name)
-{
- unsigned idx = ++trace_count;
- if (dumping (idx))
- {
- fprintf (dump_file, "range_on_entry (");
- print_generic_expr (dump_file, name, TDF_SLIM);
- fprintf (dump_file, ") to BB %d\n", bb->index);
- indent += bump;
- }
-
- gimple_ranger::range_on_entry (r, bb, name);
-
- trailer (idx, "range_on_entry", true, name, r);
-}
-
-// Tracing version of range_on_exit. Call it with printing wrappers.
-
-void
-trace_ranger::range_on_exit (irange &r, basic_block bb, tree name)
-{
- unsigned idx = ++trace_count;
- if (dumping (idx))
- {
- fprintf (dump_file, "range_on_exit (");
- print_generic_expr (dump_file, name, TDF_SLIM);
- fprintf (dump_file, ") from BB %d\n", bb->index);
- indent += bump;
- }
-
- gimple_ranger::range_on_exit (r, bb, name);
-
- trailer (idx, "range_on_exit", true, name, r);
-}
-
-// Tracing version of range_of_stmt. Call it with printing wrappers.
-
-bool
-trace_ranger::range_of_stmt (irange &r, gimple *s, tree name)
-{
- bool res;
- unsigned idx = ++trace_count;
- if (dumping (idx))
- {
- fprintf (dump_file, "range_of_stmt (");
- if (name)
- print_generic_expr (dump_file, name, TDF_SLIM);
- fputs (") at stmt ", dump_file);
- print_gimple_stmt (dump_file, s, 0, TDF_SLIM);
- indent += bump;
- }
-
- res = gimple_ranger::range_of_stmt (r, s, name);
-
- return trailer (idx, "range_of_stmt", res, name, r);
-}
-
-// Tracing version of range_of_expr. Call it with printing wrappers.
-
-bool
-trace_ranger::range_of_expr (irange &r, tree name, gimple *s)
-{
- bool res;
- unsigned idx = ++trace_count;
- if (dumping (idx))
- {
- fprintf (dump_file, "range_of_expr(");
- print_generic_expr (dump_file, name, TDF_SLIM);
- fputs (")", dump_file);
- if (s)
- {
- fputs (" at stmt ", dump_file);
- print_gimple_stmt (dump_file, s, 0, TDF_SLIM);
- }
- else
- fputs ("\n", dump_file);
- indent += bump;
- }
-
- res = gimple_ranger::range_of_expr (r, name, s);
-
- return trailer (idx, "range_of_expr", res, name, r);
-}
-
gimple_ranger *
enable_ranger (struct function *fun)
{
gimple_ranger *r;
- if (param_evrp_mode & EVRP_MODE_TRACE)
- r = new trace_ranger;
- else
- r = new gimple_ranger;
-
+ r = new gimple_ranger;
fun->x_range_query = r;
return r;
fun->x_range_query = &global_ranges;
}
-
-// =========================================
-// Debugging helpers.
-// =========================================
-
-// Query all statements in the IL to precalculate computable ranges in RANGER.
-
-static DEBUG_FUNCTION void
-debug_seed_ranger (gimple_ranger &ranger)
-{
- // Recalculate SCEV to make sure the dump lists everything.
- if (scev_initialized_p ())
- {
- scev_finalize ();
- scev_initialize ();
- }
-
- basic_block bb;
- int_range_max r;
- gimple_stmt_iterator gsi;
- FOR_EACH_BB_FN (bb, cfun)
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- {
- gimple *stmt = gsi_stmt (gsi);
-
- if (is_gimple_debug (stmt))
- continue;
-
- ranger.range_of_stmt (r, stmt);
- }
-}
-
-// Dump all that ranger knows for the current function.
-
-DEBUG_FUNCTION void
-dump_ranger (FILE *out)
-{
- gimple_ranger ranger;
- debug_seed_ranger (ranger);
- ranger.dump (out);
-}
-
-DEBUG_FUNCTION void
-debug_ranger ()
-{
- dump_ranger (stderr);
-}
-
-// Dump all that ranger knows on a path of BBs.
-//
-// Note that the blocks are in reverse order, thus the exit block is
-// path[0].
-
-DEBUG_FUNCTION void
-dump_ranger (FILE *dump_file, const vec<basic_block> &path)
-{
- if (path.length () == 0)
- {
- fprintf (dump_file, "empty\n");
- return;
- }
-
- gimple_ranger ranger;
- debug_seed_ranger (ranger);
-
- unsigned i = path.length ();
- do
- {
- i--;
- ranger.dump_bb (dump_file, path[i]);
- }
- while (i > 0);
-}
-
-DEBUG_FUNCTION void
-debug_ranger (const vec<basic_block> &path)
-{
- dump_ranger (stderr, path);
-}
-
-#include "gimple-range-tests.cc"