mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-21 19:35:36 -05:00
This patch adds a new key/value pair "cfgs={yes,no}" to diagnostics
sinks, "no" by default.
If set to "yes" for a SARIF sink, then GCC will add the internal state
of the CFG for all functions after each pertinent optimization pass in
graph form to theRun.graphs in the SARIF output.
If set to "yes" for an HTML sink, the generated HTML will contain SVG
displaying the graphs, adapted from code in graph.cc
Text sinks ignore it.
The SARIF output is thus a machine-readable serialization of (some of)
GCC's intermediate representation (as JSON), but it's much less than
GCC-XML used to provide. The precise form of the information is
documented as subject to change without notice.
Currently it shows both gimple statements and RTL instructions,
depending on the pass. My hope is that it should be possible to write a
"cfg-grep" tool that can read the SARIF and automatically identify
in which pass a particular piece of our IR appeared or disappeared,
for tracking down bugs in our optimization passes.
Implementation-wise:
* this uses the publish-subscribe mechanism from the earlier patch, by
having the diagnostics sink subscribe to pass_events::after_pass
messages from the pass_events_channel.
* the patch adds a new hook to cfghooks.h for dumping a basic block
into a SARIF property bag
gcc/ChangeLog:
* Makefile.in (OBJS): Add tree-diagnostic-cfg.o.
(OBJS-libcommon): Add custom-sarif-properties/cfg.o,
diagnostics/digraphs-to-dot.o, and
diagnostics/digraphs-to-dot-from-cfg.o.
* cfghooks.cc: Define INCLUDE_VECTOR. Add includes of
"diagnostics/sarif-sink.h" and "custom-sarif-properties/cfg.h".
(dump_bb_as_sarif_properties): New.
* cfghooks.h (diagnostics::sarif_builder): New forward decl.
(json::object): New forward decl.
(cfg_hooks::dump_bb_as_sarif_properties): New callback field.
(dump_bb_as_sarif_properties): New decl.
* cfgrtl.cc (rtl_cfg_hooks): Populate the new callback
field with rtl_dump_bb_as_sarif_properties.
(cfg_layout_rtl_cfg_hooks): Likewise.
* custom-sarif-properties/cfg.cc: New file.
* custom-sarif-properties/cfg.h: New file.
* diagnostics/digraphs-to-dot-from-cfg.cc: New file, partly
adapted from gcc/graph.cc.
* diagnostics/digraphs-to-dot.cc: New file.
* diagnostics/digraphs-to-dot.h: New file, based on material in...
* diagnostics/digraphs.cc: Include
"diagnostics/digraphs-to-dot.h".
(class conversion_to_dot): Rework and move to above.
(make_dot_graph_from_diagnostic_graph): Likewise.
(make_dot_node_from_digraph_node): Likewise.
(make_dot_edge_from_digraph_edge): Likewise.
(conversion_to_dot::get_dot_id_for_node): Likewise.
(conversion_to_dot::has_edges_p): Likewise.
(digraph::make_dot_graph): Use to_dot::converter::make and invoke
the result to make the dot graph.
* diagnostics/digraphs.h (digraph:get_all_nodes): New accessor.
* diagnostics/html-sink.cc
(html_builder::m_per_logical_loc_graphs): New field.
(html_builder::add_graph_for_logical_loc): New.
(html_sink::report_digraph_for_logical_location): New.
* diagnostics/sarif-sink.cc (sarif_array_of_unique::get_element):
New.
(sarif_builder::report_digraph_for_logical_location): New.
(sarif_sink::report_digraph_for_logical_location): New.
* diagnostics/sink.h: Include "diagnostics/logical-locations.h".
(sink::report_digraph_for_logical_location): New vfunc.
* diagnostics/text-sink.h
(text_sink::report_digraph_for_logical_location): New.
* doc/invoke.texi (fdiagnostics-add-output): Clarify wording.
Distinguish between scheme-specific vs GCC-specific keys, and add
"cfgs" as the first example of the latter.
* gimple-pretty-print.cc: Include "cfghooks.h", "json.h", and
"custom-sarif-properties/cfg.h".
(gimple_dump_bb_as_sarif_properties): New.
* gimple-pretty-print.h (diagnostics::sarif_builder): New forward
decl.
(json::object): Likewise.
(gimple_dump_bb_as_sarif_properties): New.
* graphviz.cc (get_compass_pt_from_string): New
* graphviz.h (get_compass_pt_from_string): New decl.
* libsarifreplay.cc (sarif_replayer::handle_graph_object): Fix
overlong line.
* opts-common.cc: Define INCLUDE_VECTOR.
* opts-diagnostic.cc: Define INCLUDE_LIST. Include
"diagnostics/sarif-sink.h", "tree-diagnostic-sink-extensions.h",
"opts-diagnostic.h", and "pub-sub.h".
(class gcc_extra_keys): New class.
(opt_spec_context::opt_spec_context): Add "client_keys" param and
pass to dc_spec_context.
(handle_gcc_specific_keys): New.
(try_to_make_sink): New.
(gcc_extension_factory::singleton): New.
(handle_OPT_fdiagnostics_add_output_): Rework to use
try_to_make_sink.
(handle_OPT_fdiagnostics_set_output_): Likewise.
* opts-diagnostic.h: Include "diagnostics/sink.h".
(class gcc_extension_factory): New.
* opts.cc: Define INCLUDE_LIST.
* print-rtl.cc: Include "dumpfile.h", "cfghooks.h", "json.h", and
"custom-sarif-properties/cfg.h".
(rtl_dump_bb_as_sarif_properties): New.
* print-rtl.h (diagnostics::sarif_builder): New forward decl.
(json::object): Likewise.
(rtl_dump_bb_as_sarif_properties): New decl.
* tree-cfg.cc (gimple_cfg_hooks): Use
gimple_dump_bb_as_sarif_properties for new callback field.
* tree-diagnostic-cfg.cc: New file, based on material in graph.cc.
* tree-diagnostic-sink-extensions.h: New file.
* tree-diagnostic.cc: Define INCLUDE_LIST. Include
"tree-diagnostic-sink-extensions.h".
(compiler_ext_factory): New.
(tree_diagnostics_defaults): Set gcc_extension_factory::singleton
to be compiler_ext_factory.
gcc/testsuite/ChangeLog:
* gcc.dg/diagnostic-cfgs-html.py: New test.
* gcc.dg/diagnostic-cfgs-sarif.py: New test.
* gcc.dg/diagnostic-cfgs.c: New test.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
1609 lines
44 KiB
C++
1609 lines
44 KiB
C++
/* Hooks for cfg representation specific functions.
|
|
Copyright (C) 2003-2026 Free Software Foundation, Inc.
|
|
Contributed by Sebastian Pop <s.pop@laposte.net>
|
|
|
|
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/>. */
|
|
|
|
#define INCLUDE_VECTOR
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "backend.h"
|
|
#include "rtl.h"
|
|
#include "cfghooks.h"
|
|
#include "timevar.h"
|
|
#include "pretty-print.h"
|
|
#include "diagnostic-core.h"
|
|
#include "dumpfile.h"
|
|
#include "cfganal.h"
|
|
#include "tree.h"
|
|
#include "tree-ssa.h"
|
|
#include "cfgloop.h"
|
|
#include "sreal.h"
|
|
#include "profile.h"
|
|
#include "diagnostics/sarif-sink.h"
|
|
#include "custom-sarif-properties/cfg.h"
|
|
|
|
/* Disable warnings about missing quoting in GCC diagnostics. */
|
|
#if __GNUC__ >= 10
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wformat-diag"
|
|
#endif
|
|
|
|
/* A pointer to one of the hooks containers. */
|
|
static struct cfg_hooks *cfg_hooks;
|
|
|
|
/* Initialization of functions specific to the rtl IR. */
|
|
void
|
|
rtl_register_cfg_hooks (void)
|
|
{
|
|
cfg_hooks = &rtl_cfg_hooks;
|
|
}
|
|
|
|
/* Initialization of functions specific to the rtl IR. */
|
|
void
|
|
cfg_layout_rtl_register_cfg_hooks (void)
|
|
{
|
|
cfg_hooks = &cfg_layout_rtl_cfg_hooks;
|
|
}
|
|
|
|
/* Initialization of functions specific to the tree IR. */
|
|
|
|
void
|
|
gimple_register_cfg_hooks (void)
|
|
{
|
|
cfg_hooks = &gimple_cfg_hooks;
|
|
}
|
|
|
|
struct cfg_hooks
|
|
get_cfg_hooks (void)
|
|
{
|
|
return *cfg_hooks;
|
|
}
|
|
|
|
void
|
|
set_cfg_hooks (struct cfg_hooks new_cfg_hooks)
|
|
{
|
|
*cfg_hooks = new_cfg_hooks;
|
|
}
|
|
|
|
/* Returns current ir type. */
|
|
|
|
enum ir_type
|
|
current_ir_type (void)
|
|
{
|
|
if (cfg_hooks == &gimple_cfg_hooks)
|
|
return IR_GIMPLE;
|
|
else if (cfg_hooks == &rtl_cfg_hooks)
|
|
return IR_RTL_CFGRTL;
|
|
else if (cfg_hooks == &cfg_layout_rtl_cfg_hooks)
|
|
return IR_RTL_CFGLAYOUT;
|
|
else
|
|
gcc_unreachable ();
|
|
}
|
|
|
|
/* Verify the CFG consistency.
|
|
|
|
Currently it does following: checks edge and basic block list correctness
|
|
and calls into IL dependent checking then. */
|
|
|
|
DEBUG_FUNCTION void
|
|
verify_flow_info (void)
|
|
{
|
|
size_t *edge_checksum;
|
|
bool err = false;
|
|
basic_block bb, last_bb_seen;
|
|
basic_block *last_visited;
|
|
|
|
timevar_push (TV_CFG_VERIFY);
|
|
last_visited = XCNEWVEC (basic_block, last_basic_block_for_fn (cfun));
|
|
edge_checksum = XCNEWVEC (size_t, last_basic_block_for_fn (cfun));
|
|
|
|
/* Check bb chain & numbers. */
|
|
last_bb_seen = ENTRY_BLOCK_PTR_FOR_FN (cfun);
|
|
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb, NULL, next_bb)
|
|
{
|
|
if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun)
|
|
&& bb != BASIC_BLOCK_FOR_FN (cfun, bb->index))
|
|
{
|
|
error ("bb %d on wrong place", bb->index);
|
|
err = true;
|
|
}
|
|
|
|
if (bb->prev_bb != last_bb_seen)
|
|
{
|
|
error ("prev_bb of %d should be %d, not %d",
|
|
bb->index, last_bb_seen->index, bb->prev_bb->index);
|
|
err = true;
|
|
}
|
|
|
|
last_bb_seen = bb;
|
|
}
|
|
|
|
/* Now check the basic blocks (boundaries etc.) */
|
|
FOR_EACH_BB_REVERSE_FN (bb, cfun)
|
|
{
|
|
int n_fallthru = 0;
|
|
edge e;
|
|
edge_iterator ei;
|
|
|
|
if (bb->loop_father != NULL && current_loops == NULL)
|
|
{
|
|
error ("verify_flow_info: Block %i has loop_father, but there are no loops",
|
|
bb->index);
|
|
err = true;
|
|
}
|
|
if (bb->loop_father == NULL && current_loops != NULL)
|
|
{
|
|
error ("verify_flow_info: Block %i lacks loop_father", bb->index);
|
|
err = true;
|
|
}
|
|
|
|
if (!bb->count.verify ())
|
|
{
|
|
error ("verify_flow_info: Wrong count of block %i", bb->index);
|
|
err = true;
|
|
}
|
|
/* FIXME: Graphite and SLJL and target code still tends to produce
|
|
edges with no probability. */
|
|
if (profile_status_for_fn (cfun) >= PROFILE_GUESSED
|
|
&& !bb->count.initialized_p () && !flag_graphite && 0)
|
|
{
|
|
error ("verify_flow_info: Missing count of block %i", bb->index);
|
|
err = true;
|
|
}
|
|
|
|
if (bb->flags & ~cfun->cfg->bb_flags_allocated)
|
|
{
|
|
error ("verify_flow_info: unallocated flag set on BB %d", bb->index);
|
|
err = true;
|
|
}
|
|
|
|
FOR_EACH_EDGE (e, ei, bb->succs)
|
|
{
|
|
if (last_visited [e->dest->index] == bb)
|
|
{
|
|
error ("verify_flow_info: Duplicate edge %i->%i",
|
|
e->src->index, e->dest->index);
|
|
err = true;
|
|
}
|
|
/* FIXME: Graphite and SLJL and target code still tends to produce
|
|
edges with no probability. */
|
|
if (profile_status_for_fn (cfun) >= PROFILE_GUESSED
|
|
&& !e->probability.initialized_p () && !flag_graphite && 0)
|
|
{
|
|
error ("Uninitialized probability of edge %i->%i", e->src->index,
|
|
e->dest->index);
|
|
err = true;
|
|
}
|
|
if (!e->probability.verify ())
|
|
{
|
|
error ("verify_flow_info: Wrong probability of edge %i->%i",
|
|
e->src->index, e->dest->index);
|
|
err = true;
|
|
}
|
|
|
|
last_visited [e->dest->index] = bb;
|
|
|
|
if (e->flags & EDGE_FALLTHRU)
|
|
n_fallthru++;
|
|
|
|
if (e->src != bb)
|
|
{
|
|
error ("verify_flow_info: Basic block %d succ edge is corrupted",
|
|
bb->index);
|
|
fprintf (stderr, "Predecessor: ");
|
|
dump_edge_info (stderr, e, TDF_DETAILS, 0);
|
|
fprintf (stderr, "\nSuccessor: ");
|
|
dump_edge_info (stderr, e, TDF_DETAILS, 1);
|
|
fprintf (stderr, "\n");
|
|
err = true;
|
|
}
|
|
|
|
if (e->flags & ~cfun->cfg->edge_flags_allocated)
|
|
{
|
|
error ("verify_flow_info: unallocated edge flag set on %d -> %d",
|
|
e->src->index, e->dest->index);
|
|
err = true;
|
|
}
|
|
|
|
edge_checksum[e->dest->index] += (size_t) e;
|
|
}
|
|
if (n_fallthru > 1)
|
|
{
|
|
error ("wrong amount of branch edges after unconditional jump %i", bb->index);
|
|
err = true;
|
|
}
|
|
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
{
|
|
if (e->dest != bb)
|
|
{
|
|
error ("basic block %d pred edge is corrupted", bb->index);
|
|
fputs ("Predecessor: ", stderr);
|
|
dump_edge_info (stderr, e, TDF_DETAILS, 0);
|
|
fputs ("\nSuccessor: ", stderr);
|
|
dump_edge_info (stderr, e, TDF_DETAILS, 1);
|
|
fputc ('\n', stderr);
|
|
err = true;
|
|
}
|
|
|
|
if (ei.index != e->dest_idx)
|
|
{
|
|
error ("basic block %d pred edge is corrupted", bb->index);
|
|
error ("its dest_idx should be %d, not %d",
|
|
ei.index, e->dest_idx);
|
|
fputs ("Predecessor: ", stderr);
|
|
dump_edge_info (stderr, e, TDF_DETAILS, 0);
|
|
fputs ("\nSuccessor: ", stderr);
|
|
dump_edge_info (stderr, e, TDF_DETAILS, 1);
|
|
fputc ('\n', stderr);
|
|
err = true;
|
|
}
|
|
|
|
edge_checksum[e->dest->index] -= (size_t) e;
|
|
}
|
|
}
|
|
|
|
/* Complete edge checksumming for ENTRY and EXIT. */
|
|
{
|
|
edge e;
|
|
edge_iterator ei;
|
|
|
|
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (cfun)->succs)
|
|
edge_checksum[e->dest->index] += (size_t) e;
|
|
|
|
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
|
|
edge_checksum[e->dest->index] -= (size_t) e;
|
|
}
|
|
|
|
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun), NULL, next_bb)
|
|
if (edge_checksum[bb->index])
|
|
{
|
|
error ("basic block %i edge lists are corrupted", bb->index);
|
|
err = true;
|
|
}
|
|
|
|
/* Clean up. */
|
|
free (last_visited);
|
|
free (edge_checksum);
|
|
|
|
if (cfg_hooks->verify_flow_info)
|
|
if (cfg_hooks->verify_flow_info ())
|
|
err = true;
|
|
|
|
if (err)
|
|
internal_error ("verify_flow_info failed");
|
|
timevar_pop (TV_CFG_VERIFY);
|
|
}
|
|
|
|
/* Print out one basic block BB to file OUTF. INDENT is printed at the
|
|
start of each new line. FLAGS are the TDF_* flags in dumpfile.h.
|
|
|
|
This function takes care of the purely graph related information.
|
|
The cfg hook for the active representation should dump
|
|
representation-specific information. */
|
|
|
|
void
|
|
dump_bb (FILE *outf, basic_block bb, int indent, dump_flags_t flags)
|
|
{
|
|
if (flags & TDF_BLOCKS)
|
|
dump_bb_info (outf, bb, indent, flags, true, false);
|
|
if (cfg_hooks->dump_bb)
|
|
cfg_hooks->dump_bb (outf, bb, indent, flags);
|
|
if (flags & TDF_BLOCKS)
|
|
dump_bb_info (outf, bb, indent, flags, false, true);
|
|
fputc ('\n', outf);
|
|
}
|
|
|
|
DEBUG_FUNCTION void
|
|
debug (basic_block_def &ref)
|
|
{
|
|
dump_bb (stderr, &ref, 0, TDF_NONE);
|
|
}
|
|
|
|
DEBUG_FUNCTION void
|
|
debug (basic_block_def *ptr)
|
|
{
|
|
if (ptr)
|
|
debug (*ptr);
|
|
else
|
|
fprintf (stderr, "<nil>\n");
|
|
}
|
|
|
|
static void
|
|
debug_slim (basic_block ptr)
|
|
{
|
|
fprintf (stderr, "<basic_block %p (%d)>", (void *) ptr, ptr->index);
|
|
}
|
|
|
|
DEFINE_DEBUG_VEC (basic_block_def *)
|
|
DEFINE_DEBUG_HASH_SET (basic_block_def *)
|
|
|
|
/* Dumps basic block BB to pretty-printer PP, for use as a label of
|
|
a DOT graph record-node. The implementation of this hook is
|
|
expected to write the label to the stream that is attached to PP.
|
|
Field separators between instructions are pipe characters printed
|
|
verbatim. Instructions should be written with some characters
|
|
escaped, using pp_write_text_as_dot_label_to_stream(). */
|
|
|
|
void
|
|
dump_bb_for_graph (pretty_printer *pp, basic_block bb)
|
|
{
|
|
if (!cfg_hooks->dump_bb_for_graph)
|
|
internal_error ("%s does not support dump_bb_for_graph",
|
|
cfg_hooks->name);
|
|
/* TODO: Add pretty printer for counter. */
|
|
if (bb->count.initialized_p ())
|
|
pp_printf (pp, "COUNT:" "%" PRId64, bb->count.to_gcov_type ());
|
|
pp_write_text_to_stream (pp);
|
|
if (!(dump_flags & TDF_SLIM))
|
|
cfg_hooks->dump_bb_for_graph (pp, bb);
|
|
}
|
|
|
|
void
|
|
dump_bb_as_sarif_properties (diagnostics::sarif_builder *builder,
|
|
json::object &output_bag,
|
|
basic_block bb)
|
|
{
|
|
if (!cfg_hooks->dump_bb_for_graph)
|
|
internal_error ("%s does not support dump_bb_as_sarif_properties",
|
|
cfg_hooks->name);
|
|
namespace bb_property_names = custom_sarif_properties::cfg::basic_block;
|
|
if (bb->index == ENTRY_BLOCK)
|
|
output_bag.set_string (bb_property_names::kind, "entry");
|
|
else if (bb->index == EXIT_BLOCK)
|
|
output_bag.set_string (bb_property_names::kind, "exit");
|
|
else if (BB_PARTITION (bb) == BB_HOT_PARTITION)
|
|
output_bag.set_string (bb_property_names::kind, "hot");
|
|
else if (BB_PARTITION (bb) == BB_COLD_PARTITION)
|
|
output_bag.set_string (bb_property_names::kind, "cold");
|
|
if (bb->count.initialized_p ())
|
|
{
|
|
pretty_printer pp;
|
|
pp_printf (&pp, "%" PRId64, bb->count.to_gcov_type ());
|
|
output_bag.set_string (bb_property_names::count,
|
|
pp_formatted_text (&pp));
|
|
}
|
|
cfg_hooks->dump_bb_as_sarif_properties (builder, output_bag, bb);
|
|
}
|
|
|
|
/* Dump the complete CFG to FILE. FLAGS are the TDF_* flags in dumpfile.h. */
|
|
void
|
|
dump_flow_info (FILE *file, dump_flags_t flags)
|
|
{
|
|
basic_block bb;
|
|
|
|
fprintf (file, "\n%d basic blocks, %d edges.\n", n_basic_blocks_for_fn (cfun),
|
|
n_edges_for_fn (cfun));
|
|
FOR_ALL_BB_FN (bb, cfun)
|
|
dump_bb (file, bb, 0, flags);
|
|
|
|
putc ('\n', file);
|
|
}
|
|
|
|
/* Like above, but dump to stderr. To be called from debuggers. */
|
|
void debug_flow_info (void);
|
|
DEBUG_FUNCTION void
|
|
debug_flow_info (void)
|
|
{
|
|
dump_flow_info (stderr, TDF_DETAILS);
|
|
}
|
|
|
|
/* Redirect edge E to the given basic block DEST and update underlying program
|
|
representation. Returns edge representing redirected branch (that may not
|
|
be equivalent to E in the case of duplicate edges being removed) or NULL
|
|
if edge is not easily redirectable for whatever reason. */
|
|
|
|
edge
|
|
redirect_edge_and_branch (edge e, basic_block dest)
|
|
{
|
|
edge ret;
|
|
|
|
if (!cfg_hooks->redirect_edge_and_branch)
|
|
internal_error ("%s does not support redirect_edge_and_branch",
|
|
cfg_hooks->name);
|
|
|
|
ret = cfg_hooks->redirect_edge_and_branch (e, dest);
|
|
|
|
/* If RET != E, then either the redirection failed, or the edge E
|
|
was removed since RET already lead to the same destination. */
|
|
if (current_loops != NULL && ret == e)
|
|
rescan_loop_exit (e, false, false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Returns true if it is possible to remove the edge E by redirecting it
|
|
to the destination of the other edge going from its source. */
|
|
|
|
bool
|
|
can_remove_branch_p (const_edge e)
|
|
{
|
|
if (!cfg_hooks->can_remove_branch_p)
|
|
internal_error ("%s does not support can_remove_branch_p",
|
|
cfg_hooks->name);
|
|
|
|
if (EDGE_COUNT (e->src->succs) != 2)
|
|
return false;
|
|
|
|
return cfg_hooks->can_remove_branch_p (e);
|
|
}
|
|
|
|
/* Removes E, by redirecting it to the destination of the other edge going
|
|
from its source. Can_remove_branch_p must be true for E, hence this
|
|
operation cannot fail. */
|
|
|
|
void
|
|
remove_branch (edge e)
|
|
{
|
|
edge other;
|
|
basic_block src = e->src;
|
|
int irr;
|
|
|
|
gcc_assert (EDGE_COUNT (e->src->succs) == 2);
|
|
|
|
other = EDGE_SUCC (src, EDGE_SUCC (src, 0) == e);
|
|
irr = other->flags & EDGE_IRREDUCIBLE_LOOP;
|
|
|
|
e = redirect_edge_and_branch (e, other->dest);
|
|
gcc_assert (e != NULL);
|
|
|
|
e->flags &= ~EDGE_IRREDUCIBLE_LOOP;
|
|
e->flags |= irr;
|
|
}
|
|
|
|
/* Removes edge E from cfg. Unlike remove_branch, it does not update IL. */
|
|
|
|
void
|
|
remove_edge (edge e)
|
|
{
|
|
if (current_loops != NULL)
|
|
{
|
|
rescan_loop_exit (e, false, true);
|
|
|
|
/* Removal of an edge inside an irreducible region or which leads
|
|
to an irreducible region can turn the region into a natural loop.
|
|
In that case, ask for the loop structure fixups.
|
|
|
|
FIXME: Note that LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS is not always
|
|
set, so always ask for fixups when removing an edge in that case. */
|
|
if (!loops_state_satisfies_p (LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS)
|
|
|| (e->flags & EDGE_IRREDUCIBLE_LOOP)
|
|
|| (e->dest->flags & BB_IRREDUCIBLE_LOOP))
|
|
loops_state_set (LOOPS_NEED_FIXUP);
|
|
}
|
|
|
|
/* This is probably not needed, but it doesn't hurt. */
|
|
/* FIXME: This should be called via a remove_edge hook. */
|
|
if (current_ir_type () == IR_GIMPLE)
|
|
redirect_edge_var_map_clear (e);
|
|
|
|
remove_edge_raw (e);
|
|
}
|
|
|
|
/* Like redirect_edge_succ but avoid possible duplicate edge. */
|
|
|
|
edge
|
|
redirect_edge_succ_nodup (edge e, basic_block new_succ)
|
|
{
|
|
edge s;
|
|
|
|
s = find_edge (e->src, new_succ);
|
|
if (s && s != e)
|
|
{
|
|
s->flags |= e->flags;
|
|
s->probability += e->probability;
|
|
/* FIXME: This should be called via a hook and only for IR_GIMPLE. */
|
|
redirect_edge_var_map_dup (s, e);
|
|
remove_edge (e);
|
|
e = s;
|
|
}
|
|
else
|
|
redirect_edge_succ (e, new_succ);
|
|
|
|
return e;
|
|
}
|
|
|
|
/* Redirect the edge E to basic block DEST even if it requires creating
|
|
of a new basic block; then it returns the newly created basic block.
|
|
Aborts when redirection is impossible. */
|
|
|
|
basic_block
|
|
redirect_edge_and_branch_force (edge e, basic_block dest)
|
|
{
|
|
basic_block ret, src = e->src;
|
|
|
|
if (!cfg_hooks->redirect_edge_and_branch_force)
|
|
internal_error ("%s does not support redirect_edge_and_branch_force",
|
|
cfg_hooks->name);
|
|
|
|
if (current_loops != NULL)
|
|
rescan_loop_exit (e, false, true);
|
|
|
|
ret = cfg_hooks->redirect_edge_and_branch_force (e, dest);
|
|
|
|
if (ret != NULL && dom_info_available_p (CDI_DOMINATORS))
|
|
set_immediate_dominator (CDI_DOMINATORS, ret, src);
|
|
|
|
if (current_loops != NULL)
|
|
{
|
|
if (ret != NULL)
|
|
{
|
|
class loop *loop
|
|
= find_common_loop (single_pred (ret)->loop_father,
|
|
single_succ (ret)->loop_father);
|
|
add_bb_to_loop (ret, loop);
|
|
}
|
|
else if (find_edge (src, dest) == e)
|
|
rescan_loop_exit (e, true, false);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Splits basic block BB after the specified instruction I (but at least after
|
|
the labels). If I is NULL, splits just after labels. The newly created edge
|
|
is returned. The new basic block is created just after the old one. */
|
|
|
|
static edge
|
|
split_block_1 (basic_block bb, void *i)
|
|
{
|
|
basic_block new_bb;
|
|
edge res;
|
|
|
|
if (!cfg_hooks->split_block)
|
|
internal_error ("%s does not support split_block", cfg_hooks->name);
|
|
|
|
new_bb = cfg_hooks->split_block (bb, i);
|
|
if (!new_bb)
|
|
return NULL;
|
|
|
|
new_bb->count = bb->count;
|
|
|
|
if (dom_info_available_p (CDI_DOMINATORS))
|
|
{
|
|
redirect_immediate_dominators (CDI_DOMINATORS, bb, new_bb);
|
|
set_immediate_dominator (CDI_DOMINATORS, new_bb, bb);
|
|
}
|
|
|
|
if (current_loops != NULL)
|
|
{
|
|
edge_iterator ei;
|
|
edge e;
|
|
add_bb_to_loop (new_bb, bb->loop_father);
|
|
/* Identify all loops bb may have been the latch of and adjust them. */
|
|
FOR_EACH_EDGE (e, ei, new_bb->succs)
|
|
if (e->dest->loop_father->latch == bb)
|
|
e->dest->loop_father->latch = new_bb;
|
|
}
|
|
|
|
res = make_single_succ_edge (bb, new_bb, EDGE_FALLTHRU);
|
|
|
|
if (bb->flags & BB_IRREDUCIBLE_LOOP)
|
|
{
|
|
new_bb->flags |= BB_IRREDUCIBLE_LOOP;
|
|
res->flags |= EDGE_IRREDUCIBLE_LOOP;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
edge
|
|
split_block (basic_block bb, gimple *i)
|
|
{
|
|
return split_block_1 (bb, i);
|
|
}
|
|
|
|
edge
|
|
split_block (basic_block bb, rtx i)
|
|
{
|
|
return split_block_1 (bb, i);
|
|
}
|
|
|
|
/* Splits block BB just after labels. The newly created edge is returned. */
|
|
|
|
edge
|
|
split_block_after_labels (basic_block bb)
|
|
{
|
|
return split_block_1 (bb, NULL);
|
|
}
|
|
|
|
/* Moves block BB immediately after block AFTER. Returns false if the
|
|
movement was impossible. */
|
|
|
|
bool
|
|
move_block_after (basic_block bb, basic_block after)
|
|
{
|
|
bool ret;
|
|
|
|
if (!cfg_hooks->move_block_after)
|
|
internal_error ("%s does not support move_block_after", cfg_hooks->name);
|
|
|
|
ret = cfg_hooks->move_block_after (bb, after);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Deletes the basic block BB. */
|
|
|
|
void
|
|
delete_basic_block (basic_block bb)
|
|
{
|
|
if (!cfg_hooks->delete_basic_block)
|
|
internal_error ("%s does not support delete_basic_block", cfg_hooks->name);
|
|
|
|
cfg_hooks->delete_basic_block (bb);
|
|
|
|
if (current_loops != NULL)
|
|
{
|
|
class loop *loop = bb->loop_father;
|
|
|
|
/* If we remove the header or the latch of a loop, mark the loop for
|
|
removal. */
|
|
if (loop->latch == bb
|
|
|| loop->header == bb)
|
|
mark_loop_for_removal (loop);
|
|
|
|
remove_bb_from_loops (bb);
|
|
}
|
|
|
|
/* Remove the edges into and out of this block. Note that there may
|
|
indeed be edges in, if we are removing an unreachable loop. */
|
|
while (EDGE_COUNT (bb->preds) != 0)
|
|
remove_edge (EDGE_PRED (bb, 0));
|
|
while (EDGE_COUNT (bb->succs) != 0)
|
|
remove_edge (EDGE_SUCC (bb, 0));
|
|
|
|
if (dom_info_available_p (CDI_DOMINATORS))
|
|
delete_from_dominance_info (CDI_DOMINATORS, bb);
|
|
if (dom_info_available_p (CDI_POST_DOMINATORS))
|
|
delete_from_dominance_info (CDI_POST_DOMINATORS, bb);
|
|
|
|
/* Remove the basic block from the array. */
|
|
expunge_block (bb);
|
|
}
|
|
|
|
/* Splits edge E and returns the newly created basic block. */
|
|
|
|
basic_block
|
|
split_edge (edge e)
|
|
{
|
|
basic_block ret;
|
|
profile_count count = e->count ();
|
|
edge f;
|
|
bool irr = (e->flags & EDGE_IRREDUCIBLE_LOOP) != 0;
|
|
bool back = (e->flags & EDGE_DFS_BACK) != 0;
|
|
class loop *loop;
|
|
basic_block src = e->src, dest = e->dest;
|
|
|
|
if (!cfg_hooks->split_edge)
|
|
internal_error ("%s does not support split_edge", cfg_hooks->name);
|
|
|
|
if (current_loops != NULL)
|
|
rescan_loop_exit (e, false, true);
|
|
|
|
ret = cfg_hooks->split_edge (e);
|
|
ret->count = count;
|
|
single_succ_edge (ret)->probability = profile_probability::always ();
|
|
|
|
if (irr)
|
|
{
|
|
ret->flags |= BB_IRREDUCIBLE_LOOP;
|
|
single_pred_edge (ret)->flags |= EDGE_IRREDUCIBLE_LOOP;
|
|
single_succ_edge (ret)->flags |= EDGE_IRREDUCIBLE_LOOP;
|
|
}
|
|
if (back)
|
|
{
|
|
single_pred_edge (ret)->flags &= ~EDGE_DFS_BACK;
|
|
single_succ_edge (ret)->flags |= EDGE_DFS_BACK;
|
|
}
|
|
|
|
if (dom_info_available_p (CDI_DOMINATORS))
|
|
set_immediate_dominator (CDI_DOMINATORS, ret, single_pred (ret));
|
|
|
|
if (dom_info_state (CDI_DOMINATORS) >= DOM_NO_FAST_QUERY)
|
|
{
|
|
/* There are two cases:
|
|
|
|
If the immediate dominator of e->dest is not e->src, it
|
|
remains unchanged.
|
|
|
|
If immediate dominator of e->dest is e->src, it may become
|
|
ret, provided that all other predecessors of e->dest are
|
|
dominated by e->dest. */
|
|
|
|
if (get_immediate_dominator (CDI_DOMINATORS, single_succ (ret))
|
|
== single_pred (ret))
|
|
{
|
|
edge_iterator ei;
|
|
FOR_EACH_EDGE (f, ei, single_succ (ret)->preds)
|
|
{
|
|
if (f == single_succ_edge (ret))
|
|
continue;
|
|
|
|
if (!dominated_by_p (CDI_DOMINATORS, f->src,
|
|
single_succ (ret)))
|
|
break;
|
|
}
|
|
|
|
if (!f)
|
|
set_immediate_dominator (CDI_DOMINATORS, single_succ (ret), ret);
|
|
}
|
|
}
|
|
|
|
if (current_loops != NULL)
|
|
{
|
|
loop = find_common_loop (src->loop_father, dest->loop_father);
|
|
add_bb_to_loop (ret, loop);
|
|
|
|
/* If we split the latch edge of loop adjust the latch block. */
|
|
if (loop->latch == src
|
|
&& loop->header == dest)
|
|
loop->latch = ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Creates a new basic block just after the basic block AFTER.
|
|
HEAD and END are the first and the last statement belonging
|
|
to the block. If both are NULL, an empty block is created. */
|
|
|
|
static basic_block
|
|
create_basic_block_1 (void *head, void *end, basic_block after)
|
|
{
|
|
basic_block ret;
|
|
|
|
if (!cfg_hooks->create_basic_block)
|
|
internal_error ("%s does not support create_basic_block", cfg_hooks->name);
|
|
|
|
ret = cfg_hooks->create_basic_block (head, end, after);
|
|
|
|
if (dom_info_available_p (CDI_DOMINATORS))
|
|
add_to_dominance_info (CDI_DOMINATORS, ret);
|
|
if (dom_info_available_p (CDI_POST_DOMINATORS))
|
|
add_to_dominance_info (CDI_POST_DOMINATORS, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
basic_block
|
|
create_basic_block (gimple_seq seq, basic_block after)
|
|
{
|
|
return create_basic_block_1 (seq, NULL, after);
|
|
}
|
|
|
|
basic_block
|
|
create_basic_block (rtx head, rtx end, basic_block after)
|
|
{
|
|
return create_basic_block_1 (head, end, after);
|
|
}
|
|
|
|
|
|
/* Creates an empty basic block just after basic block AFTER. */
|
|
|
|
basic_block
|
|
create_empty_bb (basic_block after)
|
|
{
|
|
return create_basic_block_1 (NULL, NULL, after);
|
|
}
|
|
|
|
/* Checks whether we may merge blocks BB1 and BB2. */
|
|
|
|
bool
|
|
can_merge_blocks_p (basic_block bb1, basic_block bb2)
|
|
{
|
|
bool ret;
|
|
|
|
if (!cfg_hooks->can_merge_blocks_p)
|
|
internal_error ("%s does not support can_merge_blocks_p", cfg_hooks->name);
|
|
|
|
ret = cfg_hooks->can_merge_blocks_p (bb1, bb2);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
predict_edge (edge e, enum br_predictor predictor, int probability)
|
|
{
|
|
if (!cfg_hooks->predict_edge)
|
|
internal_error ("%s does not support predict_edge", cfg_hooks->name);
|
|
|
|
cfg_hooks->predict_edge (e, predictor, probability);
|
|
}
|
|
|
|
bool
|
|
predicted_by_p (const_basic_block bb, enum br_predictor predictor)
|
|
{
|
|
if (!cfg_hooks->predict_edge)
|
|
internal_error ("%s does not support predicted_by_p", cfg_hooks->name);
|
|
|
|
return cfg_hooks->predicted_by_p (bb, predictor);
|
|
}
|
|
|
|
/* Merges basic block B into basic block A. */
|
|
|
|
void
|
|
merge_blocks (basic_block a, basic_block b)
|
|
{
|
|
edge e;
|
|
edge_iterator ei;
|
|
|
|
if (!cfg_hooks->merge_blocks)
|
|
internal_error ("%s does not support merge_blocks", cfg_hooks->name);
|
|
|
|
/* Pick the more reliable count. If both qualities agrees, pick the larger
|
|
one since turning mistakely hot code to cold is more harmful. */
|
|
if (!a->count.initialized_p ())
|
|
a->count = b->count;
|
|
else if (a->count.quality () < b->count.quality ())
|
|
a->count = b->count;
|
|
else if (a->count.quality () == b->count.quality ())
|
|
a->count = profile_count::max_prefer_initialized (a->count, b->count);
|
|
|
|
cfg_hooks->merge_blocks (a, b);
|
|
|
|
if (current_loops != NULL)
|
|
{
|
|
/* If the block we merge into is a loop header do nothing unless ... */
|
|
if (a->loop_father->header == a)
|
|
{
|
|
/* ... we merge two loop headers, in which case we kill
|
|
the inner loop. */
|
|
if (b->loop_father->header == b)
|
|
mark_loop_for_removal (b->loop_father);
|
|
}
|
|
/* If we merge a loop header into its predecessor, update the loop
|
|
structure. */
|
|
else if (b->loop_father->header == b)
|
|
{
|
|
remove_bb_from_loops (a);
|
|
add_bb_to_loop (a, b->loop_father);
|
|
a->loop_father->header = a;
|
|
}
|
|
/* If we merge a loop latch into its predecessor, update the loop
|
|
structure. */
|
|
if (b->loop_father->latch
|
|
&& b->loop_father->latch == b)
|
|
b->loop_father->latch = a;
|
|
remove_bb_from_loops (b);
|
|
}
|
|
|
|
/* Normally there should only be one successor of A and that is B, but
|
|
partway though the merge of blocks for conditional_execution we'll
|
|
be merging a TEST block with THEN and ELSE successors. Free the
|
|
whole lot of them and hope the caller knows what they're doing. */
|
|
|
|
while (EDGE_COUNT (a->succs) != 0)
|
|
remove_edge (EDGE_SUCC (a, 0));
|
|
|
|
/* Adjust the edges out of B for the new owner. */
|
|
FOR_EACH_EDGE (e, ei, b->succs)
|
|
{
|
|
e->src = a;
|
|
if (current_loops != NULL)
|
|
{
|
|
/* If b was a latch, a now is. */
|
|
if (e->dest->loop_father->latch == b)
|
|
e->dest->loop_father->latch = a;
|
|
rescan_loop_exit (e, true, false);
|
|
}
|
|
}
|
|
a->succs = b->succs;
|
|
a->flags |= b->flags;
|
|
|
|
/* B hasn't quite yet ceased to exist. Attempt to prevent mishap. */
|
|
b->preds = b->succs = NULL;
|
|
|
|
if (dom_info_available_p (CDI_DOMINATORS))
|
|
redirect_immediate_dominators (CDI_DOMINATORS, b, a);
|
|
|
|
if (dom_info_available_p (CDI_DOMINATORS))
|
|
delete_from_dominance_info (CDI_DOMINATORS, b);
|
|
if (dom_info_available_p (CDI_POST_DOMINATORS))
|
|
delete_from_dominance_info (CDI_POST_DOMINATORS, b);
|
|
|
|
expunge_block (b);
|
|
}
|
|
|
|
/* Split BB into entry part and the rest (the rest is the newly created block).
|
|
Redirect those edges for that REDIRECT_EDGE_P returns true to the entry
|
|
part. Returns the edge connecting the entry part to the rest. */
|
|
|
|
edge
|
|
make_forwarder_block (basic_block bb, bool (*redirect_edge_p) (edge),
|
|
void (*new_bb_cbk) (basic_block))
|
|
{
|
|
edge e, fallthru;
|
|
edge_iterator ei;
|
|
basic_block dummy, jump;
|
|
class loop *loop, *ploop, *cloop;
|
|
|
|
if (!cfg_hooks->make_forwarder_block)
|
|
internal_error ("%s does not support make_forwarder_block",
|
|
cfg_hooks->name);
|
|
|
|
fallthru = split_block_after_labels (bb);
|
|
dummy = fallthru->src;
|
|
dummy->count = profile_count::zero ();
|
|
bb = fallthru->dest;
|
|
|
|
/* Redirect back edges we want to keep. */
|
|
for (ei = ei_start (dummy->preds); (e = ei_safe_edge (ei)); )
|
|
{
|
|
basic_block e_src;
|
|
|
|
if (redirect_edge_p (e))
|
|
{
|
|
dummy->count += e->count ();
|
|
ei_next (&ei);
|
|
continue;
|
|
}
|
|
|
|
e_src = e->src;
|
|
jump = redirect_edge_and_branch_force (e, bb);
|
|
if (jump != NULL)
|
|
{
|
|
/* If we redirected the loop latch edge, the JUMP block now acts like
|
|
the new latch of the loop. */
|
|
if (current_loops != NULL
|
|
&& dummy->loop_father != NULL
|
|
&& dummy->loop_father->header == dummy
|
|
&& dummy->loop_father->latch == e_src)
|
|
dummy->loop_father->latch = jump;
|
|
|
|
if (new_bb_cbk != NULL)
|
|
new_bb_cbk (jump);
|
|
}
|
|
}
|
|
|
|
if (dom_info_available_p (CDI_DOMINATORS))
|
|
{
|
|
vec<basic_block> doms_to_fix;
|
|
doms_to_fix.create (2);
|
|
doms_to_fix.quick_push (dummy);
|
|
doms_to_fix.quick_push (bb);
|
|
iterate_fix_dominators (CDI_DOMINATORS, doms_to_fix, false);
|
|
doms_to_fix.release ();
|
|
}
|
|
|
|
if (current_loops != NULL)
|
|
{
|
|
/* If we do not split a loop header, then both blocks belong to the
|
|
same loop. In case we split loop header and do not redirect the
|
|
latch edge to DUMMY, then DUMMY belongs to the outer loop, and
|
|
BB becomes the new header. If latch is not recorded for the loop,
|
|
we leave this updating on the caller (this may only happen during
|
|
loop analysis). */
|
|
loop = dummy->loop_father;
|
|
if (loop->header == dummy
|
|
&& loop->latch != NULL
|
|
&& find_edge (loop->latch, dummy) == NULL)
|
|
{
|
|
remove_bb_from_loops (dummy);
|
|
loop->header = bb;
|
|
|
|
cloop = loop;
|
|
FOR_EACH_EDGE (e, ei, dummy->preds)
|
|
{
|
|
cloop = find_common_loop (cloop, e->src->loop_father);
|
|
}
|
|
add_bb_to_loop (dummy, cloop);
|
|
}
|
|
|
|
/* In case we split loop latch, update it. */
|
|
for (ploop = loop; ploop; ploop = loop_outer (ploop))
|
|
if (ploop->latch == dummy)
|
|
ploop->latch = bb;
|
|
}
|
|
|
|
cfg_hooks->make_forwarder_block (fallthru);
|
|
|
|
return fallthru;
|
|
}
|
|
|
|
/* Try to make the edge fallthru. */
|
|
|
|
void
|
|
tidy_fallthru_edge (edge e)
|
|
{
|
|
if (cfg_hooks->tidy_fallthru_edge)
|
|
cfg_hooks->tidy_fallthru_edge (e);
|
|
}
|
|
|
|
/* Fix up edges that now fall through, or rather should now fall through
|
|
but previously required a jump around now deleted blocks. Simplify
|
|
the search by only examining blocks numerically adjacent, since this
|
|
is how they were created.
|
|
|
|
??? This routine is currently RTL specific. */
|
|
|
|
void
|
|
tidy_fallthru_edges (void)
|
|
{
|
|
basic_block b, c;
|
|
|
|
if (!cfg_hooks->tidy_fallthru_edge)
|
|
return;
|
|
|
|
if (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun))
|
|
return;
|
|
|
|
FOR_BB_BETWEEN (b, ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb,
|
|
EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb, next_bb)
|
|
{
|
|
edge s;
|
|
|
|
c = b->next_bb;
|
|
|
|
/* We care about simple conditional or unconditional jumps with
|
|
a single successor.
|
|
|
|
If we had a conditional branch to the next instruction when
|
|
CFG was built, then there will only be one out edge for the
|
|
block which ended with the conditional branch (since we do
|
|
not create duplicate edges).
|
|
|
|
Furthermore, the edge will be marked as a fallthru because we
|
|
merge the flags for the duplicate edges. So we do not want to
|
|
check that the edge is not a FALLTHRU edge. */
|
|
|
|
if (single_succ_p (b))
|
|
{
|
|
s = single_succ_edge (b);
|
|
if (! (s->flags & EDGE_COMPLEX)
|
|
&& s->dest == c
|
|
&& !(JUMP_P (BB_END (b)) && CROSSING_JUMP_P (BB_END (b))))
|
|
tidy_fallthru_edge (s);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Edge E is assumed to be fallthru edge. Emit needed jump instruction
|
|
(and possibly create new basic block) to make edge non-fallthru.
|
|
Return newly created BB or NULL if none. */
|
|
|
|
basic_block
|
|
force_nonfallthru (edge e)
|
|
{
|
|
basic_block ret, src = e->src;
|
|
|
|
if (!cfg_hooks->force_nonfallthru)
|
|
internal_error ("%s does not support force_nonfallthru",
|
|
cfg_hooks->name);
|
|
|
|
ret = cfg_hooks->force_nonfallthru (e);
|
|
if (ret != NULL)
|
|
{
|
|
if (dom_info_available_p (CDI_DOMINATORS))
|
|
set_immediate_dominator (CDI_DOMINATORS, ret, src);
|
|
|
|
if (current_loops != NULL)
|
|
{
|
|
basic_block pred = single_pred (ret);
|
|
basic_block succ = single_succ (ret);
|
|
class loop *loop
|
|
= find_common_loop (pred->loop_father, succ->loop_father);
|
|
rescan_loop_exit (e, false, true);
|
|
add_bb_to_loop (ret, loop);
|
|
|
|
/* If we split the latch edge of loop adjust the latch block. */
|
|
if (loop->latch == pred
|
|
&& loop->header == succ)
|
|
loop->latch = ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Returns true if we can duplicate basic block BB. */
|
|
|
|
bool
|
|
can_duplicate_block_p (const_basic_block bb)
|
|
{
|
|
if (!cfg_hooks->can_duplicate_block_p)
|
|
internal_error ("%s does not support can_duplicate_block_p",
|
|
cfg_hooks->name);
|
|
|
|
if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun) || bb == ENTRY_BLOCK_PTR_FOR_FN (cfun))
|
|
return false;
|
|
|
|
return cfg_hooks->can_duplicate_block_p (bb);
|
|
}
|
|
|
|
/* Duplicate basic block BB, place it after AFTER (if non-null) and redirect
|
|
edge E to it (if non-null). Return the new basic block.
|
|
|
|
If BB contains a returns_twice call, the caller is responsible for recreating
|
|
incoming abnormal edges corresponding to the "second return" for the copy.
|
|
gimple_can_duplicate_bb_p rejects such blocks, while RTL likes to live
|
|
dangerously.
|
|
|
|
If BB has incoming abnormal edges for some other reason, their destinations
|
|
should be tied to label(s) of the original BB and not the copy. */
|
|
|
|
basic_block
|
|
duplicate_block (basic_block bb, edge e, basic_block after, copy_bb_data *id)
|
|
{
|
|
edge s, n;
|
|
basic_block new_bb;
|
|
profile_count new_count = e ? e->count (): profile_count::uninitialized ();
|
|
edge_iterator ei;
|
|
|
|
if (!cfg_hooks->duplicate_block)
|
|
internal_error ("%s does not support duplicate_block",
|
|
cfg_hooks->name);
|
|
|
|
if (bb->count < new_count)
|
|
new_count = bb->count;
|
|
|
|
gcc_checking_assert (can_duplicate_block_p (bb));
|
|
|
|
new_bb = cfg_hooks->duplicate_block (bb, id);
|
|
if (after)
|
|
move_block_after (new_bb, after);
|
|
|
|
new_bb->flags = (bb->flags & ~BB_DUPLICATED);
|
|
FOR_EACH_EDGE (s, ei, bb->succs)
|
|
{
|
|
/* Since we are creating edges from a new block to successors
|
|
of another block (which therefore are known to be disjoint), there
|
|
is no need to actually check for duplicated edges. */
|
|
n = unchecked_make_edge (new_bb, s->dest, s->flags);
|
|
n->probability = s->probability;
|
|
n->aux = s->aux;
|
|
}
|
|
|
|
if (e)
|
|
{
|
|
new_bb->count = new_count;
|
|
bb->count -= new_count;
|
|
|
|
redirect_edge_and_branch_force (e, new_bb);
|
|
}
|
|
else
|
|
new_bb->count = bb->count;
|
|
|
|
set_bb_original (new_bb, bb);
|
|
set_bb_copy (bb, new_bb);
|
|
|
|
/* Add the new block to the copy of the loop of BB, or directly to the loop
|
|
of BB if the loop is not being copied. */
|
|
if (current_loops != NULL)
|
|
{
|
|
class loop *cloop = bb->loop_father;
|
|
class loop *copy = get_loop_copy (cloop);
|
|
/* If we copied the loop header block but not the loop
|
|
we have created a loop with multiple entries. Ditch the loop,
|
|
add the new block to the outer loop and arrange for a fixup. */
|
|
if (!copy
|
|
&& cloop->header == bb)
|
|
{
|
|
add_bb_to_loop (new_bb, loop_outer (cloop));
|
|
mark_loop_for_removal (cloop);
|
|
}
|
|
else
|
|
{
|
|
add_bb_to_loop (new_bb, copy ? copy : cloop);
|
|
/* If we copied the loop latch block but not the loop, adjust
|
|
loop state. */
|
|
if (!copy
|
|
&& cloop->latch == bb)
|
|
{
|
|
cloop->latch = NULL;
|
|
loops_state_set (LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
|
|
}
|
|
}
|
|
}
|
|
|
|
return new_bb;
|
|
}
|
|
|
|
/* Return 1 if BB ends with a call, possibly followed by some
|
|
instructions that must stay with the call, 0 otherwise. */
|
|
|
|
bool
|
|
block_ends_with_call_p (basic_block bb)
|
|
{
|
|
if (!cfg_hooks->block_ends_with_call_p)
|
|
internal_error ("%s does not support block_ends_with_call_p", cfg_hooks->name);
|
|
|
|
return (cfg_hooks->block_ends_with_call_p) (bb);
|
|
}
|
|
|
|
/* Return 1 if BB ends with a conditional branch, 0 otherwise. */
|
|
|
|
bool
|
|
block_ends_with_condjump_p (const_basic_block bb)
|
|
{
|
|
if (!cfg_hooks->block_ends_with_condjump_p)
|
|
internal_error ("%s does not support block_ends_with_condjump_p",
|
|
cfg_hooks->name);
|
|
|
|
return (cfg_hooks->block_ends_with_condjump_p) (bb);
|
|
}
|
|
|
|
/* Add fake edges to the function exit for any non constant and non noreturn
|
|
calls, volatile inline assembly in the bitmap of blocks specified by
|
|
BLOCKS or to the whole CFG if BLOCKS is zero. Return the number of blocks
|
|
that were split.
|
|
|
|
The goal is to expose cases in which entering a basic block does not imply
|
|
that all subsequent instructions must be executed. */
|
|
|
|
int
|
|
flow_call_edges_add (sbitmap blocks)
|
|
{
|
|
if (!cfg_hooks->flow_call_edges_add)
|
|
internal_error ("%s does not support flow_call_edges_add",
|
|
cfg_hooks->name);
|
|
|
|
return (cfg_hooks->flow_call_edges_add) (blocks);
|
|
}
|
|
|
|
/* This function is called immediately after edge E is added to the
|
|
edge vector E->dest->preds. */
|
|
|
|
void
|
|
execute_on_growing_pred (edge e)
|
|
{
|
|
if (! (e->dest->flags & BB_DUPLICATED)
|
|
&& cfg_hooks->execute_on_growing_pred)
|
|
cfg_hooks->execute_on_growing_pred (e);
|
|
}
|
|
|
|
/* This function is called immediately before edge E is removed from
|
|
the edge vector E->dest->preds. */
|
|
|
|
void
|
|
execute_on_shrinking_pred (edge e)
|
|
{
|
|
if (! (e->dest->flags & BB_DUPLICATED)
|
|
&& cfg_hooks->execute_on_shrinking_pred)
|
|
cfg_hooks->execute_on_shrinking_pred (e);
|
|
}
|
|
|
|
/* This is used inside loop versioning when we want to insert
|
|
stmts/insns on the edges, which have a different behavior
|
|
in tree's and in RTL, so we made a CFG hook. */
|
|
void
|
|
lv_flush_pending_stmts (edge e)
|
|
{
|
|
if (cfg_hooks->flush_pending_stmts)
|
|
cfg_hooks->flush_pending_stmts (e);
|
|
}
|
|
|
|
/* Loop versioning uses the duplicate_loop_body_to_header_edge to create
|
|
a new version of the loop basic-blocks, the parameters here are
|
|
exactly the same as in duplicate_loop_body_to_header_edge or
|
|
tree_duplicate_loop_body_to_header_edge; while in tree-ssa there is
|
|
additional work to maintain ssa information that's why there is
|
|
a need to call the tree_duplicate_loop_body_to_header_edge rather
|
|
than duplicate_loop_body_to_header_edge when we are in tree mode. */
|
|
bool
|
|
cfg_hook_duplicate_loop_body_to_header_edge (class loop *loop, edge e,
|
|
unsigned int ndupl,
|
|
sbitmap wont_exit, edge orig,
|
|
vec<edge> *to_remove, int flags)
|
|
{
|
|
gcc_assert (cfg_hooks->cfg_hook_duplicate_loop_body_to_header_edge);
|
|
return cfg_hooks->cfg_hook_duplicate_loop_body_to_header_edge (
|
|
loop, e, ndupl, wont_exit, orig, to_remove, flags);
|
|
}
|
|
|
|
/* Conditional jumps are represented differently in trees and RTL,
|
|
this hook takes a basic block that is known to have a cond jump
|
|
at its end and extracts the taken and not taken edges out of it
|
|
and store it in E1 and E2 respectively. */
|
|
void
|
|
extract_cond_bb_edges (basic_block b, edge *e1, edge *e2)
|
|
{
|
|
gcc_assert (cfg_hooks->extract_cond_bb_edges);
|
|
cfg_hooks->extract_cond_bb_edges (b, e1, e2);
|
|
}
|
|
|
|
/* Responsible for updating the ssa info (PHI nodes) on the
|
|
new condition basic block that guards the versioned loop. */
|
|
void
|
|
lv_adjust_loop_header_phi (basic_block first, basic_block second,
|
|
basic_block new_block, edge e)
|
|
{
|
|
if (cfg_hooks->lv_adjust_loop_header_phi)
|
|
cfg_hooks->lv_adjust_loop_header_phi (first, second, new_block, e);
|
|
}
|
|
|
|
/* Conditions in trees and RTL are different so we need
|
|
a different handling when we add the condition to the
|
|
versioning code. */
|
|
void
|
|
lv_add_condition_to_bb (basic_block first, basic_block second,
|
|
basic_block new_block, void *cond)
|
|
{
|
|
gcc_assert (cfg_hooks->lv_add_condition_to_bb);
|
|
cfg_hooks->lv_add_condition_to_bb (first, second, new_block, cond);
|
|
}
|
|
|
|
/* Checks whether all N blocks in BBS array can be copied. */
|
|
bool
|
|
can_copy_bbs_p (basic_block *bbs, unsigned n)
|
|
{
|
|
unsigned i;
|
|
edge e;
|
|
int ret = true;
|
|
|
|
for (i = 0; i < n; i++)
|
|
bbs[i]->flags |= BB_DUPLICATED;
|
|
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
/* In case we should redirect abnormal edge during duplication, fail. */
|
|
edge_iterator ei;
|
|
FOR_EACH_EDGE (e, ei, bbs[i]->succs)
|
|
if ((e->flags & EDGE_ABNORMAL)
|
|
&& (e->dest->flags & BB_DUPLICATED))
|
|
{
|
|
ret = false;
|
|
goto end;
|
|
}
|
|
|
|
if (!can_duplicate_block_p (bbs[i]))
|
|
{
|
|
ret = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
end:
|
|
for (i = 0; i < n; i++)
|
|
bbs[i]->flags &= ~BB_DUPLICATED;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Duplicates N basic blocks stored in array BBS. Newly created basic blocks
|
|
are placed into array NEW_BBS in the same order. Edges from basic blocks
|
|
in BBS are also duplicated and copies of those that lead into BBS are
|
|
redirected to appropriate newly created block. The function assigns bbs
|
|
into loops (copy of basic block bb is assigned to bb->loop_father->copy
|
|
loop, so this must be set up correctly in advance)
|
|
|
|
If UPDATE_DOMINANCE is true then this function updates dominators locally
|
|
(LOOPS structure that contains the information about dominators is passed
|
|
to enable this), otherwise it does not update the dominator information
|
|
and it assumed that the caller will do this, perhaps by destroying and
|
|
recreating it instead of trying to do an incremental update like this
|
|
function does when update_dominance is true.
|
|
|
|
BASE is the superloop to that basic block belongs; if its header or latch
|
|
is copied, we do not set the new blocks as header or latch.
|
|
|
|
Created copies of N_EDGES edges in array EDGES are stored in array NEW_EDGES,
|
|
also in the same order.
|
|
|
|
Newly created basic blocks are put after the basic block AFTER in the
|
|
instruction stream, and the order of the blocks in BBS array is preserved. */
|
|
|
|
void
|
|
copy_bbs (basic_block *bbs, unsigned n, basic_block *new_bbs,
|
|
edge *edges, unsigned num_edges, edge *new_edges,
|
|
class loop *base, basic_block after, bool update_dominance)
|
|
{
|
|
unsigned i, j;
|
|
basic_block bb, new_bb, dom_bb;
|
|
edge e;
|
|
copy_bb_data id;
|
|
|
|
/* Mark the blocks to be copied. This is used by edge creation hooks
|
|
to decide whether to reallocate PHI nodes capacity to avoid reallocating
|
|
PHIs in the set of source BBs. */
|
|
for (i = 0; i < n; i++)
|
|
bbs[i]->flags |= BB_DUPLICATED;
|
|
|
|
/* Duplicate bbs, update dominators, assign bbs to loops. */
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
/* Duplicate. */
|
|
bb = bbs[i];
|
|
new_bb = new_bbs[i] = duplicate_block (bb, NULL, after, &id);
|
|
after = new_bb;
|
|
if (bb->loop_father)
|
|
{
|
|
/* Possibly set loop header. */
|
|
if (bb->loop_father->header == bb && bb->loop_father != base)
|
|
new_bb->loop_father->header = new_bb;
|
|
/* Or latch. */
|
|
if (bb->loop_father->latch == bb && bb->loop_father != base)
|
|
new_bb->loop_father->latch = new_bb;
|
|
}
|
|
}
|
|
|
|
/* Set dominators. */
|
|
if (update_dominance)
|
|
{
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
bb = bbs[i];
|
|
new_bb = new_bbs[i];
|
|
|
|
dom_bb = get_immediate_dominator (CDI_DOMINATORS, bb);
|
|
if (dom_bb->flags & BB_DUPLICATED)
|
|
{
|
|
dom_bb = get_bb_copy (dom_bb);
|
|
set_immediate_dominator (CDI_DOMINATORS, new_bb, dom_bb);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Redirect edges. */
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
edge_iterator ei;
|
|
new_bb = new_bbs[i];
|
|
bb = bbs[i];
|
|
|
|
FOR_EACH_EDGE (e, ei, new_bb->succs)
|
|
{
|
|
if (!(e->dest->flags & BB_DUPLICATED))
|
|
continue;
|
|
redirect_edge_and_branch_force (e, get_bb_copy (e->dest));
|
|
}
|
|
}
|
|
for (j = 0; j < num_edges; j++)
|
|
{
|
|
if (!edges[j])
|
|
new_edges[j] = NULL;
|
|
else
|
|
{
|
|
basic_block src = edges[j]->src;
|
|
basic_block dest = edges[j]->dest;
|
|
if (src->flags & BB_DUPLICATED)
|
|
src = get_bb_copy (src);
|
|
if (dest->flags & BB_DUPLICATED)
|
|
dest = get_bb_copy (dest);
|
|
new_edges[j] = find_edge (src, dest);
|
|
}
|
|
}
|
|
|
|
/* Clear information about duplicates. */
|
|
for (i = 0; i < n; i++)
|
|
bbs[i]->flags &= ~BB_DUPLICATED;
|
|
}
|
|
|
|
/* Return true if BB contains only labels or non-executable
|
|
instructions */
|
|
bool
|
|
empty_block_p (basic_block bb)
|
|
{
|
|
gcc_assert (cfg_hooks->empty_block_p);
|
|
return cfg_hooks->empty_block_p (bb);
|
|
}
|
|
|
|
/* Split a basic block if it ends with a conditional branch and if
|
|
the other part of the block is not empty. */
|
|
basic_block
|
|
split_block_before_cond_jump (basic_block bb)
|
|
{
|
|
gcc_assert (cfg_hooks->split_block_before_cond_jump);
|
|
return cfg_hooks->split_block_before_cond_jump (bb);
|
|
}
|
|
|
|
/* Work-horse for passes.cc:check_profile_consistency.
|
|
Do book-keeping of the CFG for the profile consistency checker.
|
|
Store the counting in RECORD. */
|
|
|
|
void
|
|
profile_record_check_consistency (profile_record *record)
|
|
{
|
|
basic_block bb;
|
|
edge_iterator ei;
|
|
edge e;
|
|
|
|
FOR_ALL_BB_FN (bb, cfun)
|
|
{
|
|
if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun)
|
|
&& profile_status_for_fn (cfun) != PROFILE_ABSENT
|
|
&& EDGE_COUNT (bb->succs))
|
|
{
|
|
sreal sum = 0;
|
|
bool found = false;
|
|
FOR_EACH_EDGE (e, ei, bb->succs)
|
|
{
|
|
if (!(e->flags & (EDGE_EH | EDGE_FAKE)))
|
|
found = true;
|
|
if (e->probability.initialized_p ())
|
|
sum += e->probability.to_sreal ();
|
|
}
|
|
double dsum = sum.to_double ();
|
|
if (found && (dsum < 0.9 || dsum > 1.1)
|
|
&& !(bb->count == profile_count::zero ()))
|
|
{
|
|
record->num_mismatched_prob_out++;
|
|
dsum = dsum > 1 ? dsum - 1 : 1 - dsum;
|
|
if (profile_info)
|
|
{
|
|
if (ENTRY_BLOCK_PTR_FOR_FN
|
|
(cfun)->count.ipa ().initialized_p ()
|
|
&& ENTRY_BLOCK_PTR_FOR_FN
|
|
(cfun)->count.ipa ().nonzero_p ()
|
|
&& bb->count.ipa ().initialized_p ())
|
|
record->dyn_mismatched_prob_out
|
|
+= dsum * bb->count.ipa ().to_gcov_type ();
|
|
}
|
|
else if (bb->count.initialized_p ())
|
|
record->dyn_mismatched_prob_out
|
|
+= dsum * bb->count.to_sreal_scale
|
|
(ENTRY_BLOCK_PTR_FOR_FN (cfun)->count).to_double ();
|
|
}
|
|
}
|
|
if (bb != ENTRY_BLOCK_PTR_FOR_FN (cfun)
|
|
&& profile_status_for_fn (cfun) != PROFILE_ABSENT)
|
|
{
|
|
profile_count lsum = profile_count::zero ();
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
lsum += e->count ();
|
|
if (lsum.differs_from_p (bb->count))
|
|
{
|
|
record->num_mismatched_count_in++;
|
|
profile_count max;
|
|
if (lsum < bb->count)
|
|
max = bb->count;
|
|
else
|
|
max = lsum;
|
|
if (profile_info)
|
|
{
|
|
if (ENTRY_BLOCK_PTR_FOR_FN
|
|
(cfun)->count.ipa ().initialized_p ()
|
|
&& ENTRY_BLOCK_PTR_FOR_FN
|
|
(cfun)->count.ipa ().nonzero_p ()
|
|
&& max.ipa ().initialized_p ())
|
|
record->dyn_mismatched_count_in
|
|
+= max.ipa ().to_gcov_type ();
|
|
}
|
|
else if (bb->count.initialized_p ())
|
|
record->dyn_mismatched_prob_out
|
|
+= max.to_sreal_scale
|
|
(ENTRY_BLOCK_PTR_FOR_FN (cfun)->count).to_double ();
|
|
}
|
|
}
|
|
if (bb == ENTRY_BLOCK_PTR_FOR_FN (cfun)
|
|
|| bb == EXIT_BLOCK_PTR_FOR_FN (cfun))
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Work-horse for passes.cc:acount_profile.
|
|
Do book-keeping of the CFG for the profile accounting.
|
|
Store the counting in RECORD. */
|
|
|
|
void
|
|
profile_record_account_profile (profile_record *record)
|
|
{
|
|
basic_block bb;
|
|
|
|
FOR_ALL_BB_FN (bb, cfun)
|
|
{
|
|
gcc_assert (cfg_hooks->account_profile_record);
|
|
cfg_hooks->account_profile_record (bb, record);
|
|
}
|
|
}
|
|
|
|
#if __GNUC__ >= 10
|
|
# pragma GCC diagnostic pop
|
|
#endif
|