Files
gcc-reflection/gcc/gdbhooks.py
David Malcolm 0b786d961d analyzer: reimplement supergraph to eliminate function_point and stmt_finder
GCC's static analyzer code has become hard to debug and extend.

I've realized that the core data structures within it for tracking
positions in the user's code are clunky and make things more difficult
than they need to be.

The analyzer has a data structure called the "supergraph" which unifies
all CFGs and the callgraph into a single directed graph expressing
control flow and function calls in the user's code.  The core job of the
analyzer is to walk paths in the supergraph to build a directed graph
called the exploded graph, which combines control flow and data flow,
and uncovers problems as it does so (e.g. double-free bugs).

Previously, the nodes in the supergraph closely matched basic blocks in
the gimple CFG representation in the hope that this would help the
analyzer scale better, using a class function_point to refer to places
in the code, such as *within* a basic block/supernode.  This approach
needed lots of awkward special cases and workarounds to deal with state
changes that happen mid-node, which complicated the implementation and
make debugging it hard.

This patch reimplements the analyzer's supergraph:

* eliminate class function_point in favor of a very fine-grained
supergraph, where each node in the graph represents a location in the
user's program, and each edge in the graph represents an operation (with
no-op edges for showing changing locations).  The debug option
"-fanalyzer-fine-grained" becomes redundant.

* eliminate the class hierarchy inheriting from class superedge in
favor of having each superedge optionally own an "operation", to better
express the state transitions along edges (composition rather than
inheritance), and splitting up the more complicated cases into
multiple operations/edges (making debugging easier and reasoning about
state transitions clearer).

* perform various post-processing "passes" to the supergraph after it's
initially constructed but before performing the analysis, such as
simplifying the graph, improving source location information, etc

* eliminate class stmt_finder (which was always something of a hack) in
favor of improving user source locations in the supergraph, using
class event_loc_info more consistently, and a new class
pending_location::fixup_for_epath for the most awkward cases (leaks)

* precompute and cache various properties in operations, such as for
switch edges and for phi edges, rather than performing work each time we
visit an edge.

Advantages:

* The implementation is much simpler, easier to understand and debug,
and has much clearer separation of responsibilities.

* Locations for diagnostics are somewhat improved (due to being more
consistent about using the goto_locus field of CFG edges when
constructing the supergraph, and fixing up missing location data from
gimple stmts).

* The analyzer now detects a missing "return" from a non-void-returning
function (albeit as a read of uninitialized "<return-value>"), which
found many lurking true +ves in the test suite.  I can fix the wording
of this case as a follow-up.

Disadvantages:

* The supergraph is much larger than before (one node per gimple stmt,
rather than per basic block) - but the optimizer that runs after the
supergraph is built simplifies it somewhat (and I have various ideas for
future simplifications which I hope will help the analyzer scale).

* all edges in the supergraph are intraprocedural, making "supergraph"
a misnomer.

Other notes:

* I tried to maintain the behavior of -fanalyzer as closely as possible,
but there are changes to the testsuite output.  These mostly are places
where the exploration of the exploded graph leads to nodes not being
merged as well as the previous implementation on a particular test case,
leading to the analysis hitting a termination limit and bailing out.
So I expect the analyzer's behavior to change somewhat.  I had to add
xfails in various places - but was able to remove xfails in others.

* the testsuite was running with -fanalyzer-call-summaries enabled,
which is not the default for users.  The new implementation uncovered
numerous pre-existing bugs in -fanalyzer-call-summaries, so the patch
disables this within the testsuite, matching the default for users.
Fixing those bugs can be done separately from the patch.

* the only performance data I have so far is with a debug rather than
release build.  "wall" time spent in the analyzer shows a slight
improvement overall, but with one new outlier in the integration
testsuite that now takes over an hour (specifically,
qemu-7.2.0/build/target_hexagon_decode.c) but I'd like to go ahead with
pushing this, and treat that specific slowdown as a bug.

I posted an incomplete version of this before the close of stage 1 here:
  https://gcc.gnu.org/pipermail/gcc-patches/2025-November/700883.html

Although the patch is a very large change to -fanalyzer, the changes are
confined to that component (apart from a trivial addition of
INCLUDE_DEQUE/#include <deque> to system.h), so I want to apply this
patch now in stage 3: it's a big quality-of-life improvement when
debugging -fanalyzer.

gcc/ChangeLog:
	PR analyzer/122003
	* Makefile.in (ANALYZER_OBJS): Add analyzer/ops.o,
	analyzer/supergraph-fixup-locations.o,
	analyzer/supergraph-simplify.o, and analyzer/supergraph-sorting.o.
	* digraph.h (dnode::add_in_edge): New.
	(dnode::remove_in_edge): New.
	(dnode::add_out_edge): New.
	(dnode::remove_out_edge): New.
	(dnode::m_preds): Make public.
	(dnode::m_succs): Likewise.
	(dnode::find_edge_idx): New.
	(dedge::edge_t): New typedef.
	(dedge::m_src): Make non-const.
	(dedge::m_dest): Likewise.
	(dedge::set_dest): New.
	(digraph::add_any_extra_stmts): New.
	(digraph<GraphTraits>::dump_dot_to_pp): Call it.
	* doc/analyzer.texi: Update for rewrite of supergraph.
	* doc/invoke.texi (fanalyzer-fine-grained): Make this as a no-op
	preserved for backwards compatibility.
	(fanalyzer-simplify-supergraph): Document new option.
	(fdump-analyzer-supergraph): Update for changes to output.
	* gdbhooks.py (AnaSupernodePrinter.to_string): Update for renaming
	of supernode::m_index to supernode::m_id.
	* system.h: Include <deque> if INCLUDE_DEQUE was defined.

gcc/analyzer/ChangeLog:
	PR analyzer/122003
	* analyzer-logging.h (class log_nesting_level): New.
	(log_nesting_level::log_nesting_level): New.
	(log_nesting_level::~log_nesting_level): New.
	* analyzer.cc (is_cxa_end_catch_p): New.
	* analyzer.opt (fdump-analyzer-callgraph): Make this as a no-op
	preserved for backwards compatibility.
	(fanalyzer-fine-grained): Likewise.
	(fanalyzer-simplify-supergraph): New.
	* bounds-checking.cc (strip_types): Update for changes to
	widening_svalue.
	* call-details.h: Include "pending-diagnostic.h".
	* call-info.cc (custom_edge_info::get_dot_attrs): New.
	(call_info::add_events_to_path): Add pending_diagnostic & param.
	Fix indentation.
	* call-info.h (call_info::add_events_to_path): Add
	pending_diagnostic & param.
	* call-string.cc (call_string::element_t::operator==): Reimplement.
	(call_string::element_t::cmp): New.
	(call_string::element_t::get_caller_function): Likewise.
	(call_string::element_t::get_callee_function): Likewise.
	(call_string::element_t::get_call_snode_in_caller): New.
	(call_string::element_t::get_return_snode_in_caller): New.
	(call_string::element_t::get_call_stmt): New.
	(call_string::print): Update for new implementation.
	(call_string::to_json): Likewise.
	(call_string::push_call): Likewise.
	(call_string::count_occurrences_of_function): Likewise.
	(call_string::cmp): Likewise.
	(call_string::get_callee_node): Delete.
	(call_string::get_caller_node): Convert into...
	(call_string::get_return_node_in_caller): ...this new function.
	(call_string::validate): Update for new implementation.
	(call_string::recursive_log): Likewise.
	* call-string.h (class call_superedge): Delete forward decl.
	(class return_superedge): Likewise.
	(class call_and_return_op): New forward decl.
	(struct call_string::element_t): Reimplement using
	call_and_return_op, rather than relying on interprocedural edges
	in the supergraph.
	(class call_string): Likewise.
	* call-summary.cc (call_summary::get_fndecl): Reimplement.
	(call_summary_replay::convert_svalue_from_summary_1): Update for
	changes to widening_svalue.
	* checker-event.cc (event_kind_to_string): Update for renamings
	of event_kind::{call_edge -> call_} and
	event_kind::{return_edge -> return_}.
	(region_creation_event_debug::print_desc): Update for change to
	event_loc_info.
	(state_change_event::state_change_event): Pass in event_loc_info
	rather than stack_depth, and pass it to checker_event ctor.
	(superedge_event::get_callgraph_superedge): Delete.
	(superedge_event::should_filter_p): Reimplement in terms of
	get_any_cfg_edge.
	(superedge_event::get_call_and_return_op): New.
	(superedge_event::superedge_event): Drop m_eedge and
	m_critical_state.  Add assertion that the superedge is non-null.
	(cfg_edge_event::get_cfg_superedge): Delete.
	(cfg_edge_event::cfg_edge_event): Add "op" param, and remove
	assertion refering to kinds of superedge.
	(cfg_edge_event::get_meaning): Reimplement without
	cfg_superedge.
	(cfg_edge_event::get_cfg_edge): New.
	(start_cfg_edge_event::print_desc): Use m_op.  Update for renaming
	of superedge::m_index to superedge::m_id.
	(start_cfg_edge_event::maybe_describe_condition): Reimplement in
	ops.cc as gcond_edge_op::maybe_describe_condition.
	(start_cfg_edge_event::should_print_expr_p): Reimplement in ops.cc
	as gcond_edge_op::should_print_expr_p.
	(call_event::call_event): Update for renaming of event_kind.  Drop
	assertion about superedge kind.
	(call_event::print_desc): Update for consolidation of m_var and
	m_critical_state into a struct.
	(return_event::return_event): Inherit directly from checker_event.
	Drop assertion referring to kinds of superedge.  Initialize m_edge
	and m_call_and_return_op.
	(return_event::print_desc): Update for change to m_critical_state.
	* checker-event.h (enum class event_kind): Rename call_edge to
	call_, and return_edge to return_.
	(state_change_event::state_change_event): Update for changes to
	location-handling in base class ctor.
	(state_change_event::record_critical_state): Drop this, moving
	it to special-cases in the subclasses that need it.
	(state_change_event::get_callgraph_superedge): Delete.
	(superedge_event::get_call_and_return_op): New vfunc decl.
	(superedge_event::m_var, superedge_event::m_critical_state): Drop
	these fields from this class, combining them into a new struct
	and moving the fields to the interprocedural event subclasses
	where they make sense.
	(cfg_edge_event::get_cfg_superedge): Delete.
	(cfg_edge_event::get_cfg_edge): Add.
	(cfg_edge_event::cfg_edge_event): Update for changes to location
	handling in base class ctor.  Add "op".
	(cfg_edge_event::m_op): New field.
	(start_cfg_edge_event::start_cfg_edge_event): Update for changes
	to base class ctor.
	(start_cfg_edge_event::maybe_describe_condition): Drop.
	(end_cfg_edge_event::end_cfg_edge_event): Update for changes to
	base class ctor.
	(catch_cfg_edge_event::catch_cfg_edge_event): Likewise.
	(struct critical_state): New struct.
	(call_event::record_critical_state): New decl.
	(call_event::m_critical_state): New field.
	(class return_event): Inherit from checker_event, rather than
	superedge_event.
	(return_event::get_call_and_return_op): New.
	(return_event::record_critical_state): New.
	(return_event::m_call_and_return_op): New field.
	(return_event::m_critical_state): New field.
	* common.h: Define INCLUDE_SET.
	(class cfg_superedge): Drop forward decl.
	(class switch_cfg_superedge): Likewise.
	(class eh_dispatch_cfg_superedge): Likewise.
	(class eh_dispatch_try_cfg_superedge): Likewise.
	(class eh_dispatch_allowed_cfg_superedge): Likewise.
	(class callgraph_superedge): Likewise.
	(class call_superedge): Likewise.
	(class return_superedge): Likewise.
	(class stmt_finder): Likewise.
	(class function_point): Likewise.
	(class feasibility_state): New forward decl.
	(class uncertainty_t): Likewise.
	(useful_location_p): New.
	(known_function::check_any_preconditions): New.
	(custom_edge_info::get_dot_attrs): New decl.
	(custom_edge_info::add_events_to_path): Add param
	"pending_diagnostic &pd".
	(is_cxa_end_catch_p): New decl.
	* constraint-manager.cc
	(bounded_ranges_manager::get_or_create_ranges_for_switch): Delete.
	(bounded_ranges_manager::create_ranges_for_switch): Delete.
	* constraint-manager.h
	(bounded_ranges_manager::get_or_create_ranges_for_switch): Delete
	decl.
	(bounded_ranges_manager::create_ranges_for_switch): Likewise.
	(bounded_ranges_manager::make_case_label_ranges): Make public for
	use by ops code.
	(bounded_ranges_manager::edge_cache_t): Delete.
	(bounded_ranges_manager::m_edge_cache): Delete.
	* diagnostic-manager.cc (pending_location::pending_location): New
	ctor implementations.
	(pending_location::to_json): New.
	(epath_finder::get_best_epath): Rename param to "target_enode".
	Drop param "target_stmt".  Update for renaming of
	supernode::m_index to m_id.
	(epath_finder::explore_feasible_paths): Drop param "target_stmt".
	(process_worklist_item): Likewise.
	(saved_diagnostic::saved_diagnostic): Pass in param "ploc" by
	rvalue reference and store it in m_ploc.  Drop m_stmt_finder and
	other fields made redundant by m_ploc.
	(saved_diagnostic::get_supernode): New.
	(saved_diagnostic::operator==): Update for changes to
	location-tracking.
	(saved_diagnostic::to_json): Update.
	(saved_diagnostic::dump_as_dot_node): Drop m_stmt.
	(saved_diagnostic::calc_best_epath): Update for change to
	location-tracking.
	(saved_diagnostic::supercedes_p): Likewise.
	(saved_diagnostic::maybe_add_sarif_properties): Likewise.
	(get_emission_location): Delete.
	(diagnostic_manager::add_diagnostic): Pass "ploc" by rvalue
	reference, moving it to the saved_diagnostic.  Update early
	rejection check, and call fixup_location before-hand.
	(class dedupe_key): Drop m_stmt field, and update for changes
	to saved_diagnostic.
	(dedupe_winners::add): Call get_best_epath here, and call
	fixer_for_epath on it.
	(diagnostic_manager::emit_saved_diagnostics): Update for
	changes to saved_diagnostic and supernode.
	(diagnostic_manager::emit_saved_diagnostic): Likewise.
	Use the pending_location from the saved_diagnostic for the
	location of the final event, and for the primary location of the
	diagnostic itself.
	(diagnostic_manager::build_emission_path): Use useful_location_p.
	(state_change_event_creator::on_global_state_change): Update for
	changes to location-tracking.
	(state_change_event_creator::on_state_change): Likewise.
	(struct null_assignment_sm_context): Reimplement within ops.cc.
	(diagnostic_manager::add_events_for_eedge): Reimplement.
	(diagnostic_manager::add_events_for_superedge): Delete in favor of
	control_flow_op::add_any_events_for_eedge.
	(diagnostic_manager::prune_for_sm_diagnostic): Update call/return
	for event_kind renamings, and to use call_and_return_op rathern
	than callgraph_superedge.
	(diagnostic_manager::consolidate_conditions): Port from
	cfg_superedge to get_cfg_edge.
	* diagnostic-manager.h: Include "analyzer/supergraph.h" and
	"analyzer/event-loc-info.h".
	(struct pending_location): Move decl earlier in file.  Replace
	the existing specified ctors with 3 new ones.  Add comments.
	(class pending_location::fixer_for_epath): New.
	(pending_location::get_location): New.
	(pending_location::to_json): New decl.
	(pending_location::m_snode): Drop redundant field.
	(pending_location::m_event_loc_info): New field, replacing m_stmt
	and m_loc.
	(pending_location::m_finder): Replace with...
	(pending_location::m_fixer_for_epath): ...this new field.
	(make_ploc_fixer_for_epath_for_leak_diagnostic): New decl.
	(saved_diagnostic::saved_diagnostic): Pass in param "ploc" by
	rvalue reference and store it in m_ploc.
	(saved_diagnostic::get_supernode): New.
	(saved_diagnostic::m_ploc): New field, replacing m_enode,
	m_snode, m_stmt, m_stmt_finder, and m_loc.
	(diagnostic_manager::add_diagnostic): Pass ploc as rvalue
	reference.
	(diagnostic_manager::add_events_for_superedge): Delete decl.
	* engine.cc: Include "gimple-predict.h" and
	"analyzer/impl-sm-context.h".
	(impl_region_model_context::impl_region_model_context): Drop
	stmt_finder.
	(impl_region_model_context::warn): Convert to...
	(impl_region_model_context::warn_at): ...this.
	(class impl_sm_context): Move to impl-sm-context.h.
	(impl_region_model_context::get_state_map_by_name): Drop
	m_stmt_finder.
	(class leak_stmt_finder): Reimplement as...
	(class leak_ploc_fixer_for_epath): ...this.
	(make_ploc_fixer_for_epath_for_leak_diagnostic): New.
	(returning_from_function_p): Update for supergraph changes.
	(impl_region_model_context::on_state_leak): Port from
	leak_stmt_finder to leak_ploc_fixer_for_epath.
	(impl_region_model_context::on_condition): Update for
	location-handling changes.
	(impl_region_model_context::on_bounded_ranges): Likewise.
	(impl_region_model_context::on_phi): Likewise.
	(impl_region_model_context::get_pending_location_for_diag): New.
	(exploded_node::status_to_str): Add status::special.
	(exploded_node::get_processed_stmt): Delete.
	(exploded_node::dump_dot): Elide state if we have a single
	predecessor  and the state hasn't changed.
	(exploded_node::dump_processed_stmts): Delete.
	(exploded_node::on_stmt): Delete, reimplementing in ops.cc as
	gimple_stmt_op::execute_on_state, call_and_return_op::execute, and
	operation::handle_on_stmt_for_state_machines.
	(exploded_node::on_stmt_pre): Delete, reimplementing in ops.cc as
	call_and_return_op::make.
	(exploded_node::on_stmt_post): Delete.
	(class call_summary_edge_info): Move to ops.cc.
	(exploded_node::replay_call_summaries): Delete.
	(exploded_node::replay_call_summary): Delete.
	(exploded_node::on_edge): Delete.
	(exploded_node::on_longjmp): Eliminate ambiguous "setjmp_point"
	and "next_point" in favor of "point_before_setjmp" and
	"point_after_setjmp".
	(exploded_graph::unwind_from_exception): Update for changes to
	program_point.
	(exploded_node::on_throw): Convert "after_throw_point" to a param.
	(exploded_node::on_resx): Delete.
	(exploded_node::detect_leaks): Update for renaming of
	supernode::return_p to supernode::exit_p, and drop stmt param of
	impl_region_model_context ctor.
	(dynamic_call_info_t::update_model): Delete.
	(dynamic_call_info_t::add_events_to_path): Delete.
	(interprocedural_call::print): New.
	(interprocedural_call::get_dot_attrs): New.
	(interprocedural_call::update_state): New.
	(interprocedural_call::update_model): New.
	(interprocedural_call::add_events_to_path): New.
	(interprocedural_return::print): New.
	(interprocedural_return::get_dot_attrs): New.
	(interprocedural_return::update_state): New.
	(interprocedural_return::update_model): New.
	(interprocedural_return::add_events_to_path): New.
	(rewind_info_t::add_events_to_path): Add pending_diagnostic &
	param.
	(exploded_edge::dump_dot_label): Drop superedge kinds.  Show
	op vs no-op.  Flush before printing any superedge label, and
	escape that label.
	(exploded_edge::maybe_get_stmt): New.
	(exploded_edge::maybe_get_op): New.
	(stats::stats): Update for change to m_num_nodes;
	(stats::log): Likewise.
	(stats::dump): Likewise.
	(stats::get_total_enodes): Likewise.
	(strongly_connected_components::strongly_connected_components):
	Update for changes to supergraph.
	(strongly_connected_components::dump): Show the stack.  Update for
	changes to supernode.
	(strongly_connected_components::to_json): Update for changes to
	supergraph.
	(strongly_connected_components::strong_connect): Rename "index" to
	"id".  Drop superedge kinds.
	(worklist::key_t::cmp): Compare BB indexes before snode ids.
	Drop function_point.
	(exploded_graph::exploded_graph): Update stats initialization.
	(tainted_args_function_info::update_model): Reimplement.
	(tainted_args_function_info::add_events_to_path): Add param.
	(exploded_graph::get_or_create_node): Check for recursion limit
	here, rather than in program_point::on_edge and
	exploded_graph::maybe_create_dynamic_call.  Only merge state
	for points with state_merge_at_p.  Update stats tracking for
	changes to supergraph.  Fix wording of log of state.
	(exploded_graph::get_or_create_per_call_string_data): Update for
	supergraph changes.
	(tainted_args_call_info::update_model): Reimplement.
	(tainted_args_call_info::add_events_to_path): Add param.
	(exploded_graph::process_worklist): Drop assertions that nodes
	have no successors, due to some cases during unwinding exceptions.
	Update call to maybe_process_run_of_before_supernode_enodes to
	call to maybe_process_run_of_enodes, and only at points for
	which state_merge_at_p.  Reimplement "too complex" check.
	(exploded_graph::maybe_process_run_of_before_supernode_enodes):
	Convert to...
	(exploded_graph::maybe_process_run_of_enodes): ...this.  Only
	consider nodes with a single successor in the supergraph and for
	which that superedge supports_bulk_merge_p.  Port state updates to
	using operation::update_state_for_bulk_merger.
	(stmt_requires_new_enode_p): Delete.
	(state_change_requires_new_enode_p): Delete.
	(exploded_graph::maybe_create_dynamic_call): Delete.
	(class impl_path_context): Reimplement in ops.cc.
	(class jump_through_null): Move to region-model.cc.
	(exploded_graph::process_node): Use location_t from supernode,
	rather than trying to have a stmt associated with a supernode.
	Drop switch on program_point kind, instead using the operation, if any,
	from the superedge.
	(exploded_graph::get_or_create_function_stats): Update computation
	of num_supernodes for the function.
	(exploded_graph::print_bar_charts): Update for supergraph changes.
	(exploded_graph::dump_stats): Likewise.
	(exploded_graph::dump_states_for_supernode): Delete.
	(exploded_graph::to_json): Update comment.
	(exploded_path::find_stmt_backwards): Update for supergraph
	reimplementation.
	(exploded_path::feasible_p): Drop "last_stmt".
	(feasibility_state::maybe_update_for_edge): Move most of
	implementation to ops and custom_edge_infos.
	(feasibility_state::update_for_stmt): Delete.
	(supernode_cluster::dump_dot): Update for supernode changes.
	(supernode_cluster::cmp_ptr_ptr): Likewise.
	(exploded_graph::dump_exploded_nodes): Update for
	location-handling changes, and for changes to supergraph
	representation.
	(class viz_callgraph_node): Delete
	(class viz_callgraph_edge): Delete.
	(class viz_callgraph): Delete.
	(class viz_callgraph_cluster): Delete.
	(struct viz_callgraph_traits): Delete.
	(dump_callgraph): Delete.
	(exploded_graph_annotator::exploded_graph_annotator): Update for
	supernode::m_index becoming supernode:m_id.
	(exploded_graph_annotator::add_node_annotations): Reimplement to
	show enodes within the node for the supernode.
	(exploded_graph_annotator::print_enode_port): New.
	(exploded_graph_annotator::print_enode): Add port.
	(exploded_graph_annotator::print_saved_diagnostic): Drop stmt.
	(exploded_graph_annotator::m_enodes_per_snodes): Convert to...
	(exploded_graph_annotator::m_enodes_per_snode_id): ...this, using
	std::vector.
	(maybe_dump_supergraph): New.
	(impl_run_checkers): Create region_model_manager before supergraph
	and pass it to supergraph ctor.  Dump the original form of the
	supergraph, then call fixup_locations, simplify, and sort_nodes on
	the supergraph, dumping it at each stage.  Drop dump_callgraph.
	Replace dump to "NAME.supergraph-eg.dot" with dump to
	"NAME.supergraph.N.eg.dot".
	* event-loc-info.h (event_loc_info::event_loc_info): Add ctors taking
	const exploded_node * and const program_point &.
	* exploded-graph.h: Add include of "analyzer/region-model.h".
	(impl_region_model_context::impl_region_model_context): Add default
	for "stmt" param. Drop "stmt_finder" param.
	(impl_region_model_context::warn): Convert to...
	(impl_region_model_context::warn_at): ...this.
	(impl_region_model_context::get_pending_location_for_diag): New.
	(impl_region_model_context::m_stmt_finder): Drop.
	(struct exploded_node::on_stmt_flags): Drop.
	(exploded_node::on_stmt): Drop.
	(exploded_node::on_stmt_pre): Drop.
	(exploded_node::on_stmt_post): Drop.
	(exploded_node::replay_call_summaries): Drop.
	(exploded_node::replay_call_summary): Drop.
	(exploded_node::on_edge): Drop.
	(exploded_node::on_throw): Add "after_throw_point" param.
	(exploded_node::on_resx): Drop.
	(exploded_node::get_location): New.
	(exploded_node::get_stmt): Drop.
	(exploded_node::get_processed_stmt): Drop.
	(exploded_node::maybe_get_stmt): New decl.
	(exploded_node::maybe_get_op): New decl.
	(class dynamic_call_info_t): Delete.
	(class interprocedural_call): New.
	(class interprocedural_return): New.
	(rewind_info_t::add_events_to_path): Add pending_diagnostic &
	param.
	(rewind_info_t::get_setjmp_point): Replace with...
	(rewind_info_t::get_point_before_setjmp): ...this...
	(rewind_info_t::get_point_after_setjmp): ...and this.
	(stats::m_num_nodes): Convert from an array to a plain int.
	(class strongly_connected_components): Convert from index to id
	throughout.
	(exploded_graph::maybe_process_run_of_before_supernode_enodes):
	Replace with...
	(exploded_graph::maybe_process_run_of_enodes): ...this.
	(exploded_graph::maybe_create_dynamic_call): Delete.
	(exploded_graph::save_diagnostic): Drop stmt_finder param.
	(exploded_graph::dump_states_for_supernode): Drop.
	(exploded_graph::m_PK_AFTER_SUPERNODE_per_snode): Drop.
	(class feasibility_problem): Drop "m_last_stmt".
	(feasibility_state::update_for_stmt): Drop.
	(feasibility_state::get_model): Add non-const accessor.
	(feasibility_state::get_snodes_visited): New accessor.
	(class stmt_finder): Drop.
	* feasible-graph.cc (feasible_node::dump_dot): Drop call to
	dump_processed_stmts.
	(feasible_node::get_state_at_stmt): Drop.
	* impl-sm-context.h: New file, adapted from material in engine.cc.
	* infinite-loop.cc
	(perpetual_start_cfg_edge_event::perpetual_start_cfg_edge_event):
	Add "op" param.
	(perpetual_start_cfg_edge_event::print_desc): Use m_op to describe
	condition.
	(looping_back_event::looping_back_event): Add "op" param.
	(infinite_loop_diagnostic::maybe_add_custom_events_for_superedge):
	Convert to...
	(infinite_loop_diagnostic::maybe_add_custom_events_for_eedge):
	...this.
	(infinite_loop_diagnostic::add_final_event): Port from
	cfg_superedge to get_any_cfg_edge and operations.  Update for
	location-handling changes.
	(get_in_edge_back_edge): Port from cfg_superedge to
	get_any_cfg_edge.
	(starts_infinite_loop_p): Update for location-handling changes.
	(exploded_graph::detect_infinite_loops): Remove redundant params.
	* infinite-recursion.cc
	(infinite_recursion_diagnostic::add_final_event): Update for
	location-handling changes.
	(infinite_recursion_diagnostic::check_valid_fpath_p): Drop gimple
	param.
	(infinite_recursion_diagnostic::fedge_uses_conjured_svalue_p):
	Port from cfg_superedge to operations.
	(is_entrypoint_p): Update for supergraph changes.
	(exploded_graph::detect_infinite_recursion): Update for
	location-handling changes.
	* kf-lang-cp.cc (kf_operator_new::impl_call_pre): Split out
	code to handle placement-new into...
	(kf_operator_new::check_any_preconditions): ...this...
	(kf_operator_new::get_sized_region_for_placement_new): ...and
	this.
	* ops.cc: New file, albeit with material adatpted from old
	implementation.
	* ops.h: Likewise.
	* pending-diagnostic.cc (pending_diagnostic::add_call_event): Add
	gcall param.  Update for changes to location-handling.
	* pending-diagnostic.h
	(pending_diagnostic::maybe_add_custom_events_for_superedge):
	Convert to...
	(pending_diagnostic::maybe_add_custom_events_for_eedge): ...this.
	(pending_diagnostic::add_call_event): Add "call_stmt" param.
	(pending_diagnostic::check_valid_fpath_p): Drop stmt param.
	* program-point.cc (point_kind_to_string): Delete.
	(function_point::function_point): Delete.
	(function_point::print): Delete.
	(function_point::hash): Delete.
	(function_point::get_function): Delete.
	(function_point::get_stmt): Delete.
	(function_point::get_location): Delete.
	(function_point::final_stmt_p): Delete.
	(function_point::from_function_entry): Delete.
	(function_point::before_supernode): Delete.
	(function_point::print_source_line): Convert to...
	(program_point::print_source_line): ...this.
	(program_point::print): Reimplement.
	(program_point::to_json): Likewise.
	(program_point::push_to_call_stack): Delete.
	(program_point::hash): Reimplement.
	(program_point::get_function_at_depth): Likewise.
	(program_point::on_edge): Delete.
	(function_point::cmp_within_supernode_1): Delete.
	(function_point::cmp_within_supernode): Delete.
	(function_point::cmp): Delete.
	(function_point::cmp_ptr): Delete.
	(function_point::next_stmt): Delete.
	(function_point::get_next): Delete.
	(program_point::origin): Update.
	(program_point::from_function_entry): Update.
	(program_point::get_next): Delete.
	(selftest::test_function_point_equality): Delete.
	(selftest::test_function_point_ordering): Delete.
	(selftest::test_program_point_equality): Update for changes to
	program_point.
	(selftest::analyzer_program_point_cc_tests): Don't call deleted
	function_point tests.
	* program-point.h: Include "analyzer/supergraph.h".
	(class exploded_graph): Drop forward decl.
	(enum point_kind): Drop.
	(point_kind_to_string): Drop decl.
	(class function_point): Delete.
	(program_point::program_point): Take a const supernode *
	rather than a const function_point &.
	(program_point::print_source_line): New decl.
	(program_point::operator==): Update.
	(program_point::get_function_point): Drop.
	(program_point::get_supernode): Reimplement.
	(program_point::get_function): Reimplement.
	(program_point::get_fndecl): Reimplement.
	(program_point::get_stmt): Drop.
	(program_point::get_location): Reimplement.
	(program_point::get_kind): Drop.
	(program_point::get_from_edge): Drop.
	(program_point::get_stmt_idx): Drop.
	(program_point::get_stack_depth): Update.
	(program_point::state_merge_at_p): New.
	(program_point::before_supernode): Drop.
	(program_point::before_stmt): Drop.
	(program_point::after_supernode): Drop.
	(program_point::empty): Drop.
	(program_point::deleted): Drop.
	(program_point::on_edge): Drop.
	(program_point::push_to_call_stack): Drop.
	(program_point::next_stmt): Drop.
	(program_point::get_next): Drop.
	(program_point::m_function_point): Replace with...
	(program_point::m_snode): ...this.
	* program-state.cc (program_state::on_edge): Delete.
	(program_state::push_call): Delete.
	(program_state::returning_call): Delete.
	(program_state::prune_for_point): Port from function_point to
	supernode.  Drop stmt param to impl_region_model_context ctor.
	(selftest::test_sm_state_map): Update for engine borrowing rather
	owning the region_model_manager.
	(selftest::test_program_state_1): Likewise.
	(selftest::test_program_state_2): Likewise.
	(selftest::test_program_state_merging): Likewise.
	(selftest::test_program_state_merging_2): Likewise.
	* program-state.h (program_state::push_call): Delete decl.
	(program_state::returning_call): Delete decl.
	(program_state::on_edge): Delete decl.
	* region-model-manager.cc (region_model_manager::maybe_fold_unaryop):
	Only fold constants if we have a type.
	(region_model_manager::get_or_create_widening_svalue): Port from
	function_point to supernode.
	* region-model-manager.h
	(region_model_manager::get_or_create_widening_svalue): Likewise.
	* region-model.cc
	(poisoned_value_diagnostic::check_valid_fpath_p): Drop code for
	handling function_points within an snode.
	(exception_thrown_from_unrecognized_call::add_events_to_path): Add
	pending_diagnostic param.
	(class jump_through_null): Move here from engine.cc.
	(region_model::on_call_pre): Check for jump through null here,
	rather than in exploded_graph::process_node.
	(region_model::on_setjmp): Add superedge param and pass it to
	setjmp_record ctor.
	(region_model::handle_phi): Delete, in favor of
	phis_for_edge_op::update_state in ops.cc.
	(region_model::update_for_phis): Likewise.
	(region_model::maybe_update_for_edge): Delete.
	(region_model::update_for_call_superedge): Delete.
	(region_model::update_for_return_superedge): Delete.
	(region_model::apply_constraints_for_gcond): Reimplement in ops.cc as
	gcond_edge_op::apply_constraints.
	(has_nondefault_case_for_value_p): Move to ops.cc.
	(has_nondefault_cases_for_all_enum_values_p): Move to ops.cc
	(region_model::apply_constraints_for_gswitch): Reimplement in
	ops.cc as switch_case_op::apply_constraints.
	(class rejected_eh_dispatch): Move to ops.cc.
	(exception_matches_type_p): Move to ops.cc.
	(matches_any_exception_type_p): Move to ops.cc.
	(region_model::apply_constraints_for_eh_dispatch): Reimplement in
	ops.cc as eh_dispatch_edge_op::apply_constraints.
	(region_model::apply_constraints_for_eh_dispatch_try): Reimplement
	in ops.cc as eh_dispatch_try_edge_op::apply_eh_constraints.
	(region_model::apply_constraints_for_eh_dispatch_allowed):
	Reimplement in ops.cc as
	eh_dispatch_allowed_edge_op::apply_eh_constraints.
	(region_model::apply_constraints_for_ggoto): Reimplement in ops.cc
	as ggoto_edge_op::apply_constraints.
	(caller_context::warn): Replace with...
	(caller_context::get_pending_location_for_diag): ...this.
	(region_model::get_or_create_region_for_heap_alloc): Fix
	indentation.
	(region_model_context::warn): New, replacing vfunc with
	shared code that calls get_pending_location_for_diag and warn_at
	vfuncs.
	(engine::engine): Borrow m_mgr rather than own it.
	(seldtest::test_state_merging): Update test for ptrs to different
	base regions becoming unmergeable.
	(selftest::test_widening_constraints): Port from function_point to
	supernode.
	* region-model.h: Include "analyzer/diagnostic-manager.h".
	(region_model::on_setjmp): Add superedge param.
	(region_model::void update_for_phis): Drop decl.
	(region_model::handle_phi): Drop decl.
	(region_model::maybe_update_for_edge): Drop decl.
	(region_model::apply_constraints_for_eh_dispatch_try): Drop decl.
	(region_model::apply_constraints_for_eh_dispatch_allowed): Drop
	decl.
	(region_model::update_for_call_superedge): Drop decl.
	(region_model::update_for_return_superedge): Drop decl.
	(region_model::apply_constraints_for_gcond): Drop decl.
	(region_model::apply_constraints_for_gswitch): Drop decl.
	(region_model::apply_constraints_for_ggoto): Drop decl.
	(region_model::apply_constraints_for_eh_dispatch): Drop decl.
	(region_model_context::warn): Convert from vfunc to func.
	(region_model_context::get_pending_location_for_diag): New vfunc.
	(region_model_context::warn_at): New vfunc.
	(class noop_region_model_context): Update for changes to
	region_model_context.
	(class region_model_context_decorator): Likewise.
	(class annotating_context): Likewise.
	(struct model_merger): Port from function_point to supernode.
	(class engine): Borrow m_mgr rather than own it.
	(class test_region_model_context): Update for changes to
	region_model_context.
	* region.cc (frame_region::get_region_for_local): Update for
	change to supergraph.
	* sm-fd.cc: Drop redundant params throughout.  Pass stmt
	rather than node to the various on_ calls.
	* sm-file.cc: Drop redundant params throughout.
	(register_known_file_functions): Register "*_unlocked" versions of
	functions that I'd missed.
	* sm-malloc.cc: Drop redundant params throughout.
	(deref_before_check::loop_header_p): Reimplement cfg_superedge
	check.
	(malloc_state_machine::on_stmt): Move attribute-handling to...
	(malloc_state_machine::check_call_preconditions): ...this new
	function.
	(maybe_complain_about_deref_before_check): Use
	sm_ctxt.get_emission_location when checking for inlining.
	* sm-pattern-test.cc: Drop redundant params throughout.
	* sm-sensitive.cc: Likewise.
	* sm-signal.cc: Likewise.
	* sm-taint.cc: Likewise.
	* sm.cc: Fix unused param warnings.
	* sm.h: Include "analyzer/analyzer-logging.h".  Drop redundant
	params throughout.
	(state_machine::check_call_preconditions): New vfunc.
	(sm_context::get_state): Drop "stmt" args.
	(sm_context::set_next_state): Likewise.
	(sm_context::on_transition): Drop "stmt" and "node" args.
	(sm_context::warn): Likewise.
	(sm_context::get_emission_location): New vfunc.
	* state-purge.cc: Define INCLUDE_SET.
	(class gimple_op_visitor): Replace function_point and function
	with superedge.
	(state_purge_map::state_purge_map): Iterate through ops on edges,
	rather than on stmts in supernodes.
	(state_purge_map::on_duplicated_node): New.
	(state_purge_map::get_or_create_data_for_decl): Use supernode
	rather than function_point.
	(state_purge_per_ssa_name::state_purge_per_ssa_name): Likewise.
	(state_purge_per_ssa_name::needed_at_point_p): Replace with...
	(state_purge_per_ssa_name::needed_at_supernode_p): ...this.
	(state_purge_per_ssa_name::before_use_stmt): Delete.
	(state_purge_per_ssa_name::add_to_worklist): Use supernode rather
	than function_point.
	(name_used_by_phis_p): Delete.
	(state_purge_per_ssa_name::process_point): Replace with...
	(state_purge_per_ssa_name::process_supernode): ...this.
	(state_purge_per_ssa_name::on_duplicated_node): New.
	(state_purge_per_decl::state_purge_per_decl): Use supernode rather
	than function_point.
	(state_purge_per_decl::add_needed_at): Likewise.
	(state_purge_per_decl::add_pointed_to_at): Likewise.
	(state_purge_per_decl::process_worklists): Likewise.
	(state_purge_per_decl::add_to_worklist): Likewise.
	(state_purge_per_decl::process_point_backwards): Replace with...
	(state_purge_per_decl::process_supernode_backwards): ...this.
	(state_purge_per_decl::process_point_forwards): Replace with...
	(state_purge_per_decl::process_supernode_forwards): ...this.
	(state_purge_per_decl::needed_at_point_p): Replace with...
	(state_purge_per_decl::needed_at_supernode_p): ...this.
	(state_purge_per_decl::on_duplicated_node): New.
	(print_vec_of_names): Drop "within_table" param.
	(state_purge_annotator::add_stmt_annotations): Drop.
	(state_purge_annotator::add_node_annotations): Reimplement.
	* state-purge.h: Convert throughout from function_point to
	supernode.
	(state_purge_map::on_duplicated_node): New decl.
	(state_purge_per_ssa_name::on_duplicated_node): Likewise.
	(state_purge_per_decl::on_duplicated_node): Likewise.
	* store.cc (needs_loop_replay_fixup_p): New.
	(store::loop_replay_fixup): Use it rather than checking for
	SK_WIDENING.
	* supergraph-fixup-locations.cc: New file.
	* supergraph-manipulation.h: New file.
	* supergraph-simplify.cc: New file.
	* supergraph-sorting.cc: New file.
	* supergraph.cc: Define INCLUDE_DEQUE.  Drop include of
	"tree-dfa.h".  Include "diagnostics/file-cache.h"
	and "analyzer/exploded-graph.h".
	(supergraph_call_edge): Delete.
	(control_flow_stmt_p): New.
	(supergraph::supergraph): Add "mgr" param.  Initialize
	m_next_snode_id.  Reimplement.
	(supergraph::populate_for_basic_block): New.
	(supergraph::dump_dot_to_pp): Add auto_cfun sentinel.
	Split up nodes using loop information from the original CFG,
	then by basic block.  Call the node_annotator's
	add_extra_objects vfunc.
	(supergraph::dump_dot_to_gv_for_loop): New.
	(supergraph::dump_dot_to_gv_for_bb): New, based on code in
	dump_dot_to_pp.
	(supergraph::add_node): Drop "returning_call" and "phi_nodes"
	params. Add logger param and logging.  Use m_next_snode_id to
	allow for node deletion.
	(supergraph::add_cfg_edge): Delete.
	(supergraph::add_call_superedge): Delete.
	(supergraph::add_return_superedge): Delete.
	(supergraph::delete_nodes): New.
	(supergraph::add_sedges_for_cfg_edge): New.
	(supernode::dump_dot): Drop output cluster, moving
	add_node_annotations to within the dot node.  Show any SCC id.
	Show m_preserve_p and m_state_merger_node.  Update for renaming of
	supernode::return_p to supernode::exit_p.  Highlight nodes without
	source location information.  Show m_loc and m_stmt_loc.  Show
	source lines, with color for highlight.
	(supernode::dump_dot_id): Update.
	(supernode::to_json): Update.
	(supernode::get_start_location): Delete.
	(supernode::get_end_location): Delete.
	(supernode::get_stmt_index): Delete.
	(supernode::get_label): Delete.
	(edge_kind_to_string): Delete.
	(superedge::dump): Update for supernode::m_index becoming m_id.
	(superedge::dump_dot): Drop ltail/lhead attrs.  Flush after
	dumping the label.
	(superedge::to_json): Reimplement.
	(superedge::get_any_cfg_edge): Delete.
	(superedge::get_any_callgraph_edge): Delete.
	(superedge::preserve_p): New.
	(superedge::supports_bulk_merge_p): New.
	(cfg_superedge::dump_label_to_pp): Delete.
	(superedge::dump_label_to_pp): New.
	(cfg_superedge::get_phi_arg_idx): Delete.
	(cfg_superedge::get_phi_arg): Delete.
	(switch_cfg_superedge::switch_cfg_superedge): Delete.
	(switch_cfg_superedge::dump_label_to_pp): Delete.
	(switch_cfg_superedge::implicitly_created_default_p): Delete.
	(get_catch): Move to ops.cc.
	(eh_dispatch_cfg_superedge::make): Delete in favor of
	eh_dispatch_edge_op::make.
	(eh_dispatch_cfg_superedge::eh_dispatch_cfg_superedge): Delete.
	(eh_dispatch_cfg_superedge::get_eh_status): Delete.
	(eh_dispatch_try_cfg_superedge::dump_label_to_pp): Delete.
	(eh_dispatch_try_cfg_superedge::apply_constraints): Delete.
	(eh_dispatch_allowed_cfg_superedge::eh_dispatch_allowed_cfg_superedge):
	Delete.
	(eh_dispatch_allowed_cfg_superedge::dump_label_to_pp): Delete.
	(eh_dispatch_allowed_cfg_superedge::apply_constraints): Delete.
	(callgraph_superedge::dump_label_to_pp): Delete.
	(callgraph_superedge::get_callee_function): Delete.
	(callgraph_superedge::get_caller_function): Delete
	(callgraph_superedge::get_callee_decl): Delete
	(callgraph_superedge::get_call_stmt): Delete
	(callgraph_superedge::get_caller_decl): Delete
	(callgraph_superedge::get_arg_for_parm): Delete in favor of
	call_and_return_op::get_arg_for_parm in ops.cc.
	(callgraph_superedge::get_parm_for_arg): Delete in favor of
	call_and_return_op::get_parm_for_arg in ops.cc.
	(callgraph_superedge::map_expr_from_caller_to_callee): Delete in
	favor of call_and_return_op::map_expr_from_caller_to_callee in
	ops.cc.
	(callgraph_superedge::map_expr_from_callee_to_caller): Delete in
	favor of call_and_return_op::map_expr_from_callee_to_caller in
	ops.cc.
	* supergraph.h: Include "cfgloop.h" and "analyzer/ops.h".
	(enum edge_kind): Delete.
	(struct supergraph_traits::dump_args_t): Add m_eg.
	(class supergraph): Rewrite leading comment.
	(supergraph::supergraph): Add "mgr" param.
	(supergraph::get_node_for_function_entry): Reimplement.
	(supergraph::get_node_for_function_exit): Reimplement.
	(supergraph::get_node_for_block): Convert to...
	(supergraph::get_initial_node_for_block): ...this.
	(supergraph::get_caller_next_node): Delete.
	(supergraph::get_edge_for_call): Delete.
	(supergraph::get_edge_for_return): Delete.
	(supergraph::get_intraprocedural_edge_for_call): Delete.
	(supergraph::get_edge_for_cfg_edge): Delete.
	(supergraph::get_supernode_for_stmt): Delete.
	(supergraph::get_final_node_for_block): New.
	(supergraph::get_supernode_for_stmt): New.
	(supergraph::get_superedge_for_phis): New.
	(supergraph::get_node_by_index): Delete.
	(supergraph::add_node): Drop "returning_call" and "phi_nodes"
	params.  Add logger param.
	(supergraph::add_cfg_edge): Delete.
	(supergraph::add_call_superedge): Delete.
	(supergraph::add_return_superedge): Delete.
	(supergraph::log_stats): New decl.
	(supergraph::delete_nodes): New decl.
	(supergraph::fixup_locations): New decl.
	(supergraph::simplify): New decl.
	(supergraph::sort_nodes): New decl.
	(supergraph::populate_for_basic_block): New decl.
	(supergraph::add_sedges_for_cfg_edge): New decl.
	(supergraph::dump_dot_to_gv_for_loop): New decl.
	(supergraph::dump_dot_to_gv_for_bb): New decl.
	(supergraph::reorder_nodes_and_ids): New decl.
	(supergraph::bb_to_node_t): Make private.
	(supergraph::m_bb_to_initial_node): Make private.
	(supergraph::m_bb_to_final_node): Make private.
	(supergraph::cgraph_edge_to_node_t): Delete typedef.
	(supergraph::m_cgraph_edge_to_caller_prev_node): Delete.
	(supergraph::m_cgraph_edge_to_caller_next_node): Delete.
	(supergraph::cfg_edge_to_cfg_superedge_t): Delete typedef.
	(supergraph::m_cfg_edge_to_cfg_superedge): Delete.
	(supergraph::cgraph_edge_to_call_superedge_t): Delete typedef.
	(supergraph::m_cgraph_edge_to_call_superedge): Delete
	(supergraph::cgraph_edge_to_return_superedge_t): Delete typedef.
	(supergraph::m_cgraph_edge_to_return_superedge): Delete.
	(supergraph::cgraph_edge_to_intraproc_superedge_t): Delete
	typedef.
	(supergraph::m_cgraph_edge_to_intraproc_superedge): Delete.
	(supergraph::stmt_to_node_t): Delete typedef.
	(supergraph::m_stmt_to_node_t): Replace with...
	(supergraph::m_node_for_stmt): ...this.
	(supergraph::m_edges_for_phis): New field.
	(supergraph::m_next_snode_id): New field.
	(supergraph::m_snode_by_id): New field.
	(supernode::supernode): Drop "returning_call" and "phi_nodes"
	params.  Convert "index" to "id".  Update for changes to fields.
	(supernode::return_p): Rename for clarity to...
	(supernode::exit_p): ...this.
	(supernode::get_start_location): Delete.
	(supernode::get_end_location): Delete.
	(supernode::start_phis): Delete.
	(supernode::get_returning_call): Delete.
	(supernode::print): New.
	(supernode::get_last_stmt): Delete.
	(supernode::get_final_call): Delete.
	(supernode::get_stmt_index): Delete.
	(supernode::get_location): New.
	(supernode::get_label): Convert to trivial accessor.
	(supernode::preserve_p): New.
	(supernode::m_returning_call): Drop field.
	(supernode::m_phi_nodes): Drop field.
	(supernode::m_stmts): Drop field.
	(supernode::m_index): Replace with...
	(supernode::m_id): ...this.
	(supernode::m_loc): New field.
	(supernode::m_stmt_loc): New field.
	(supernode::m_original_id): New field.
	(supernode::m_label): New field.
	(supernode::m_preserve_p): New field.
	(supernode::m_state_merger_node): New field.
	(class superedge): Update leading comment.
	(superedge::superedge): Make public rather than protected.  Drop
	"kind" param.  Add "op" and "cfg_edge" params.  Assert that edge
	is intraprocedural.
	(superedge::m_kind): Drop field.
	(superedge::m_op): New field.
	(superedge::m_cfg_edge): New field.
	(superedge::dump_label_to_pp): Make non-virtual.
	(superedge::get_op): New.
	(superedge::set_op): New.
	(superedge::get_kind): Drop.
	(superedge::get_dest_snode): New accessor.
	(superedge::dyn_cast_cfg_superedge): Delete.
	(superedge::dyn_cast_switch_cfg_superedge): Delete
	(superedge::dyn_cast_eh_dispatch_cfg_superedge): Delete
	(superedge::dyn_cast_eh_dispatch_try_cfg_superedge): Delete
	(superedge::dyn_cast_eh_dispatch_allowed_cfg_superedge): Delete
	(superedge::dyn_cast_callgraph_superedge): Delete
	(superedge::dyn_cast_callgraph_superedge): Delete
	(superedge::dyn_cast_call_superedge): Delete
	(superedge::dyn_cast_call_superedge): Delete
	(superedge::dyn_cast_return_superedge): Delete
	(superedge::dyn_cast_return_superedge): Delete
	(superedge::get_any_cfg_edge): Convert to trivial accessor.
	(superedge::get_any_callgraph_edge): Drop.
	(superedge::preserve_p): New.
	(superedge::supports_bulk_merge_p): New.
	(class callgraph_superedge): Drop.
	(is_a_helper <const callgraph_superedge *>::test): Drop.
	(class call_superedge): Drop.
	(is_a_helper <const call_superedge *>::test): Drop.
	(class return_superedge): Drop.
	(is_a_helper <const return_superedge *>::test): Drop.
	(class cfg_superedge): Drop.
	(class switch_cfg_superedge): Drop.
	(is_a_helper <const switch_cfg_superedge *>::test): Drop.
	(class eh_dispatch_cfg_superedge): Drop.
	(is_a_helper <const eh_dispatch_cfg_superedge *>::test): Drop.
	(class eh_dispatch_try_cfg_superedge): Drop.
	(is_a_helper <const eh_dispatch_try_cfg_superedge *>::test): Drop.
	(class eh_dispatch_allowed_cfg_superedge): Drop.
	(is_a_helper <const eh_dispatch_allowed_cfg_superedge *>::test):
	Drop.
	(dot_annotator::~dot_annotator): Use "= default;".
	(dot_annotator::add_node_annotations): Drop return value and
	"within_table" param.
	(dot_annotator::add_stmt_annotations): Drop.
	(dot_annotator::add_after_node_annotations): Drop.
	(dot_annotator::add_extra_objects): New.
	(supergraph_call_edge): Delete decl.
	(get_ultimate_function_for_cgraph_edge): Delete decl.
	* svalue.cc (svalue::can_merge_p): Reject attempts to merge
	pointers that point to different base regions, except for the case
	where both are string literals.  Update for point change in
	widening_svalue.
	(svalue::cmp_ptr): Update for point change to widening_svalue.
	(widening_svalue::dump_to_pp): Likewise.
	(widening_svalue::print_dump_widget_label): Likewise.
	* svalue.h (struct setjmp_record): Add m_sedge.
	(class widening_svalue): Replace function_point m_point with
	const supernode *m_snode throughout.
	* varargs.cc (va_list_state_machine::on_stmt): Drop redundant
	param.
	(va_list_state_machine::on_va_start): Likewise.  Update for change
	to get_state.
	(va_list_state_machine::check_for_ended_va_list): Likewise.
	(va_list_state_machine::on_va_copy): Likewise.
	(va_list_state_machine::on_va_arg): Likewise.
	(va_list_state_machine::on_va_end): Likewise.
	(va_arg_diagnostic::add_call_event): Update for changes to
	location-tracking.

gcc/testsuite/ChangeLog:
	PR analyzer/122003
	* c-c++-common/analyzer/allocation-size-multiline-1.c: Update for split
	of region creation events.
	* c-c++-common/analyzer/bzip2-arg-parse-1.c: Drop test for enode
	merging.  Add -Wno-analyzer-too-complex.
	* c-c++-common/analyzer/coreutils-cksum-pr108664.c: Add
	-Wno-analyzer-symbol-too-complex.  Add dg-bogus for false +ve seen
	during patch development.
	* c-c++-common/analyzer/coreutils-group_number.c: New test.
	* c-c++-common/analyzer/data-model-20.c: Mark warnings as xfail.
	* c-c++-common/analyzer/deref-before-check-qemu-qtest_rsp_args.c:
	Add xfails.
	* c-c++-common/analyzer/dot-output.c: Update for changes to dumps.
	* c-c++-common/analyzer/fd-symbolic-socket.c: Update for
	improvements to locations of leaks.
	* c-c++-common/analyzer/fibonacci.c: Update regex.
	* c-c++-common/analyzer/flex-with-call-summaries.c: Add xfail.
	* c-c++-common/analyzer/flex-without-call-summaries.c: Add
	-Wno-analyzer-symbol-too-complex.  Add xfail.
	* c-c++-common/analyzer/infinite-recursion-5.c: Disable cases that
	now explode the analysis.
	* c-c++-common/analyzer/infinite-recursion-pr108524-2.c: Remove
	xfail.
	* c-c++-common/analyzer/invalid-shift-1.c: Remove xfails with c++26.
	* c-c++-common/analyzer/ipa-callbacks-1.c: New test.
	* c-c++-common/analyzer/loop-4.c: Expect incorrect UNKNOWN within
	loop.  Update expected number of enodes.
	* c-c++-common/analyzer/loop-n-down-to-1-by-1.c: Expect incorrect
	UNKNOWN within loop.
	* c-c++-common/analyzer/loop.c: Drop xfail.
	* c-c++-common/analyzer/out-of-bounds-coreutils.c: Expect infinite
	loop warning.
	* c-c++-common/analyzer/paths-4.c: Update expected number of
	enodes.
	* c-c++-common/analyzer/pr94362-1.c: Drop -Wno-analyzer-too-complex.
	* c-c++-common/analyzer/pr94851-2.c: Add xfail.
	* c-c++-common/analyzer/pr96650-1-notrans.c: Add
	-Wno-analyzer-too-complex.
	* c-c++-common/analyzer/pr98628.c: Likewise.
	* c-c++-common/analyzer/pr99774-1.c: Likewise.
	* c-c++-common/analyzer/pragma-2.c: Expect double-free warning.
	* c-c++-common/analyzer/realloc-1.c: Move expected location of
	leak from trailing "}" to realloc call.
	* c-c++-common/analyzer/sock-1.c: Add -fno-exceptions.
	* c-c++-common/analyzer/sprintf-2.c: Add __attribute__ nonnull to
	decl.
	* c-c++-common/analyzer/sprintf-concat.c: Move expected location
	of leak of p from sprintf to trailing "}".
	* c-c++-common/analyzer/stdarg-sentinel-1.c: Drop
	-Wno-analyzer-too-complex.
	* c-c++-common/analyzer/strncpy-1.c: Add __attribute__ nonnull to
	decl.
	* c-c++-common/analyzer/strstr-1.c: Likewise.
	* g++.dg/analyzer/analyzer.exp: Drop -fanalyzer-call-summaries.
	* g++.dg/analyzer/fanalyzer-show-events-in-system-headers-default.C:
	Update expected messages.
	* g++.dg/analyzer/fanalyzer-show-events-in-system-headers-no.C:
	Likewise.
	* g++.dg/analyzer/fanalyzer-show-events-in-system-headers.C: Likewise.
	* g++.dg/analyzer/pr94028.C: Move expected location of leak
	warning to where return value of f is discarded within m.
	* g++.dg/analyzer/pr96641.C: Expect infinite recursion warning.
	* gcc.dg/analyzer/CWE-131-examples.c: Add
	-Wno-analyzer-too-complex.
	* gcc.dg/analyzer/abs-1.c (test_2): Fix return type.
	* gcc.dg/analyzer/analyzer-decls.h: Reformat.  Add
	__attribute__ ((nothrow)) to all functions.
	* gcc.dg/analyzer/analyzer.exp: Drop -fanalyzer-call-summaries.
	* gcc.dg/analyzer/boxed-malloc-1.c: Fix return types.
	* gcc.dg/analyzer/call-summaries-2.c: Likewise.
	* gcc.dg/analyzer/combined-conditionals-1.c: Likewise.
	* gcc.dg/analyzer/compound-assignment-2.c: Expect warning about
	missing return.
	* gcc.dg/analyzer/compound-assignment-3.c: Likewise.
	* gcc.dg/analyzer/conditionals-3.c: Fix return type.
	* gcc.dg/analyzer/data-model-1.c: Likewise.
	* gcc.dg/analyzer/data-model-15.c: Likewise.
	* gcc.dg/analyzer/data-model-17.c: Likewise.
	* gcc.dg/analyzer/data-model-20a.c: Remove xfail from bogus leak.
	* gcc.dg/analyzer/data-model-7.c: Fix return type.
	* gcc.dg/analyzer/doom-d_main-IdentifyVersion.c: Add xfail to some
	of the leak msgs.
	* gcc.dg/analyzer/doom-s_sound-pr108867.c: Add xfail.
	* gcc.dg/analyzer/edges-1.c: Update for improvements to location
	of leak.
	* gcc.dg/analyzer/error-1.c: Fix return type.
	* gcc.dg/analyzer/explode-1.c: Drop xfail.  Expect uninit and
	double-free warnings.
	* gcc.dg/analyzer/explode-2.c: Add xfail.
	* gcc.dg/analyzer/explode-3.c: Drop xfail.  Expect uninit and
	double-free warnings.
	* gcc.dg/analyzer/fd-datagram-socket.c: Move expected location of
	leaks to closing "}"s.
	* gcc.dg/analyzer/fd-glibc-byte-stream-connection-server.c: Add
	false +ve leak message, due to not considering that program is
	about to exit.
	* gcc.dg/analyzer/fd-stream-socket.c: Move expected location of
	leaks to closing "}"s.
	* gcc.dg/analyzer/malloc-1.c: Fix return types.
	* gcc.dg/analyzer/malloc-many-paths-2.c: Likewise.
	* gcc.dg/analyzer/malloc-paths-10.c: Likewise.
	* gcc.dg/analyzer/malloc-vs-local-4.c: Likewise.
	* gcc.dg/analyzer/memset-CVE-2017-18549-1.c: Likewise.
	* gcc.dg/analyzer/null-deref-pr102671-1.c: Enable
	-fanalyzer-call-summaries.
	* gcc.dg/analyzer/null-deref-pr102671-2.c: Remove xfail.
	* gcc.dg/analyzer/pr101143.c: Fix return type.
	* gcc.dg/analyzer/pr101837.c: Fix return type.  Add warning about
	missing return.
	* gcc.dg/analyzer/pr101983-not-main.c: Fix return type.
	* gcc.dg/analyzer/pr103892.c: Enable -fanalyzer-call-summaries.
	* gcc.dg/analyzer/pr104224.c: Add xfails.
	* gcc.dg/analyzer/pr104434-nonconst.c: Likewise.
	* gcc.dg/analyzer/pr93032-mztools-signed-char.c: Increase
	exploration limits by a factor of 5.
	* gcc.dg/analyzer/pr93032-mztools-unsigned-char.c: Likewise.
	* gcc.dg/analyzer/pr93355-localealias-feasibility-2.c: Fix return type.
	* gcc.dg/analyzer/pr93355-localealias.c: Add xfail.  Add expected
	leak true +ve and uninit false +ve.
	* gcc.dg/analyzer/pr94579.c: Add warning about missing return.
	* gcc.dg/analyzer/pr98599-a.c: Add missing return stmts.
	* gcc.dg/analyzer/pr99771-1.c: Fix expected locations of leaks.
	* gcc.dg/analyzer/pr99774-2.c: Likewise.
	* gcc.dg/analyzer/sensitive-1.c: Fix return types.
	* gcc.dg/analyzer/state-diagram-1-sarif.py: Update.
	* gcc.dg/analyzer/stdarg-1.c
	(__analyzer_test_not_enough_args_2_middle): Add test coverage for
	wording of call event with variadic args.
	* gcc.dg/analyzer/strcmp-1.c: Fix return types.
	* gcc.dg/analyzer/strcpy-1.c: Likewise.
	* gcc.dg/analyzer/switch-enum-taint-1.c: Add warning about missing
	return.
	* gcc.dg/analyzer/switch.c: Fix return types.
	* gcc.dg/analyzer/taint-assert.c: Likewise.
	* gcc.dg/analyzer/taint-write-offset-1.c: Likewise.
	* gcc.dg/analyzer/torture/analyzer-torture.exp: Drop
	-fanalyzer-call-summaries.
	* gcc.dg/analyzer/torture/boxed-ptr-1.c: Fix return type.
	* gcc.dg/analyzer/torture/fold-ptr-arith-pr105784.c: Add
	-Wno-analyzer-too-complex.
	* gcc.dg/analyzer/torture/loop-inc-ptr-1.c: Skip at -O3 to avoid
	changes to enode count.
	* gcc.dg/analyzer/torture/pr102225.c: Consolidate on one line to
	avoid caring about precise location of leak warning.
	* gcc.dg/analyzer/torture/pr93379.c: Skip on -fno-fat-lto-objects.
	Add warning about uninit.
	* gcc.dg/analyzer/torture/stdarg-4.c: Replace UNKNOWN with
	symbolic sum of params.
	* gcc.dg/analyzer/untracked-1.c: Fix return type.
	* gcc.dg/analyzer/use-after-free.c: Likewise.
	* gcc.dg/analyzer/zlib-3.c: Add xfails.
	* gcc.dg/plugin/analyzer_cpython_plugin.cc
	(class refcnt_stmt_finder): Eliminate.
	(check_refcnt): ...in favor of a call to
	make_ploc_fixer_for_epath_for_leak_diagnostic.
	* gcc.dg/plugin/analyzer_gil_plugin.cc: Update for
	location-handling changes.
	* gcc.dg/plugin/infoleak-CVE-2011-1078-1.c: Add missing
	"return 0;".
	* gcc.dg/plugin/infoleak-CVE-2011-1078-2.c: Fix return types.
	* gcc.dg/plugin/infoleak-CVE-2017-18549-1.c: Likewise.
	* gdc.dg/analyzer/analyzer.exp: Drop -fanalyzer-call-summaries.
	* gfortran.dg/analyzer/analyzer.exp: Likewise.
	* gfortran.dg/analyzer/uninit-pr63311.f90: Add
	-Wno-analyzer-too-complex.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
2025-12-12 12:23:34 -05:00

939 lines
32 KiB
Python

# Python hooks for gdb for debugging GCC
# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# Contributed by David Malcolm <dmalcolm@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/>.
"""
Enabling the debugging hooks
----------------------------
gcc/configure (from configure.ac) generates a .gdbinit within the "gcc"
subdirectory of the build directory, and when run by gdb, this imports
gcc/gdbhooks.py from the source directory, injecting useful Python code
into gdb.
You may see a message from gdb of the form:
"path-to-build/gcc/.gdbinit" auto-loading has been declined by your `auto-load safe-path'
as a protection against untrustworthy python scripts. See
http://sourceware.org/gdb/onlinedocs/gdb/Auto_002dloading-safe-path.html
The fix is to mark the paths of the build/gcc directory as trustworthy.
An easy way to do so is by adding the following to your ~/.gdbinit script:
add-auto-load-safe-path /absolute/path/to/build/gcc
for the build directories for your various checkouts of gcc.
If it's working, you should see the message:
Successfully loaded GDB hooks for GCC
as gdb starts up.
During development, I've been manually invoking the code in this way, as a
precanned way of printing a variety of different kinds of value:
gdb \
-ex "break expand_gimple_stmt" \
-ex "run" \
-ex "bt" \
--args \
./cc1 foo.c -O3
Examples of output using the pretty-printers
--------------------------------------------
Pointer values are generally shown in the form:
<type address extra_info>
For example, an opt_pass* might appear as:
(gdb) p pass
$2 = <opt_pass* 0x188b600 "expand"(170)>
The name of the pass is given ("expand"), together with the
static_pass_number.
Note that you can dereference the pointer in the normal way:
(gdb) p *pass
$4 = {type = RTL_PASS, name = 0x120a312 "expand",
[etc, ...snipped...]
and you can suppress pretty-printers using /r (for "raw"):
(gdb) p /r pass
$3 = (opt_pass *) 0x188b600
Basic blocks are shown with their index in parentheses, apart from the
CFG's entry and exit blocks, which are given as "ENTRY" and "EXIT":
(gdb) p bb
$9 = <basic_block 0x7ffff041f1a0 (2)>
(gdb) p cfun->cfg->x_entry_block_ptr
$10 = <basic_block 0x7ffff041f0d0 (ENTRY)>
(gdb) p cfun->cfg->x_exit_block_ptr
$11 = <basic_block 0x7ffff041f138 (EXIT)>
CFG edges are shown with the src and dest blocks given in parentheses:
(gdb) p e
$1 = <edge 0x7ffff043f118 (ENTRY -> 6)>
Tree nodes are printed using Python code that emulates print_node_brief,
running in gdb, rather than in the inferior:
(gdb) p cfun->decl
$1 = <function_decl 0x7ffff0420b00 foo>
For usability, the type is printed first (e.g. "function_decl"), rather
than just "tree".
RTL expressions use a kludge: they are pretty-printed by injecting
calls into print-rtl.c into the inferior:
Value returned is $1 = (note 9 8 10 [bb 3] NOTE_INSN_BASIC_BLOCK)
(gdb) p $1
$2 = (note 9 8 10 [bb 3] NOTE_INSN_BASIC_BLOCK)
(gdb) p /r $1
$3 = (rtx_def *) 0x7ffff043e140
This won't work for coredumps, and probably in other circumstances, but
it's a quick way of getting lots of debuggability quickly.
Callgraph nodes are printed with the name of the function decl, if
available:
(gdb) frame 5
#5 0x00000000006c288a in expand_function (node=<cgraph_node* 0x7ffff0312720 "foo"/12345>) at ../../src/gcc/cgraphunit.c:1594
1594 execute_pass_list (g->get_passes ()->all_passes);
(gdb) p node
$1 = <cgraph_node* 0x7ffff0312720 "foo"/12345>
Similarly for symtab_node and varpool_node classes.
Cgraph edges are printed with the name of caller and callee:
(gdb) p this->callees
$4 = <cgraph_edge* 0x7fffe25aa000 (<cgraph_node * 0x7fffe62b22e0 "_GLOBAL__sub_I__ZN5Pooma5pinfoE"/19660> -> <cgraph_node * 0x7fffe620f730 "__static_initialization_and_destruction_1"/19575>)>
IPA reference follow very similar format:
(gdb) Value returned is $5 = <ipa_ref* 0x7fffefcb80c8 (<symtab_node * 0x7ffff562f000 "__dt_base "/875> -> <symtab_node * 0x7fffe795f000 "_ZTVN6Smarts8RunnableE"/16056>:IPA_REF_ADDR)>
vec<> pointers are printed as the address followed by the elements in
braces. Here's a length 2 vec:
(gdb) p bb->preds
$18 = 0x7ffff0428b68 = {<edge 0x7ffff044d380 (3 -> 5)>, <edge 0x7ffff044d3b8 (4 -> 5)>}
and here's a length 1 vec:
(gdb) p bb->succs
$19 = 0x7ffff0428bb8 = {<edge 0x7ffff044d3f0 (5 -> EXIT)>}
You cannot yet use array notation [] to access the elements within the
vector: attempting to do so instead gives you the vec itself (for vec[0]),
or a (probably) invalid cast to vec<> for the memory after the vec (for
vec[1] onwards).
Instead (for now) you must access the payload directly:
(gdb) p ((edge_def**)(bb->preds+1))[0]
$20 = <edge 0x7ffff044d380 (3 -> 5)>
(gdb) p ((edge_def**)(bb->preds+1))[1]
$21 = <edge 0x7ffff044d3b8 (4 -> 5)>
"""
import os.path
import re
import sys
import tempfile
import gdb
import gdb.printing
import gdb.types
# Convert "enum tree_code" (tree.def and tree.h) to a dict:
tree_code_dict = gdb.types.make_enum_dict(gdb.lookup_type('enum tree_code'))
# ...and look up specific values for use later:
IDENTIFIER_NODE = tree_code_dict['IDENTIFIER_NODE']
TYPE_DECL = tree_code_dict['TYPE_DECL']
SSA_NAME = tree_code_dict['SSA_NAME']
# Similarly for "enum tree_code_class" (tree.h):
tree_code_class_dict = gdb.types.make_enum_dict(gdb.lookup_type('enum tree_code_class'))
tcc_type = tree_code_class_dict['tcc_type']
tcc_declaration = tree_code_class_dict['tcc_declaration']
# Python3 has int() with arbitrary precision (bignum). Python2 int() is 32-bit
# on 32-bit hosts but remote targets may have 64-bit pointers there; Python2
# long() is always 64-bit but Python3 no longer has anything named long.
def intptr(gdbval):
return long(gdbval) if sys.version_info.major == 2 else int(gdbval)
class Tree:
"""
Wrapper around a gdb.Value for a tree, with various methods
corresponding to macros in gcc/tree.h
"""
def __init__(self, gdbval):
self.gdbval = gdbval
def is_nonnull(self):
return intptr(self.gdbval)
def TREE_CODE(self):
"""
Get gdb.Value corresponding to TREE_CODE (self)
as per:
#define TREE_CODE(NODE) ((enum tree_code) (NODE)->base.code)
"""
return self.gdbval['base']['code']
def DECL_NAME(self):
"""
Get Tree instance corresponding to DECL_NAME (self)
"""
return Tree(self.gdbval['decl_minimal']['name'])
def TYPE_NAME(self):
"""
Get Tree instance corresponding to result of TYPE_NAME (self)
"""
return Tree(self.gdbval['type_common']['name'])
def IDENTIFIER_POINTER(self):
"""
Get str correspoinding to result of IDENTIFIER_NODE (self)
"""
return self.gdbval['identifier']['id']['str'].string()
class TreePrinter:
"Prints a tree"
def __init__ (self, gdbval):
self.gdbval = gdbval
self.node = Tree(gdbval)
def to_string (self):
# like gcc/print-tree.c:print_node_brief
# #define TREE_CODE(NODE) ((enum tree_code) (NODE)->base.code)
# tree_code_name[(int) TREE_CODE (node)])
if intptr(self.gdbval) == 0:
return '<tree 0x0>'
val_TREE_CODE = self.node.TREE_CODE()
# constexpr inline enum tree_code_class tree_code_type[] = { ... };
# #define TREE_CODE_CLASS(CODE) tree_code_type[(int) (CODE)]
# or
# template <int N>
# struct tree_code_type_tmpl {
# static constexpr enum tree_code_class tree_code_type[] = { ... };
# }; };
# #define TREE_CODE_CLASS(CODE) \
# tree_code_type_tmpl <0>::tree_code_type[(int) (CODE)]
if val_TREE_CODE == 0xa5a5:
return '<ggc_freed 0x%x>' % intptr(self.gdbval)
try:
val_tree_code_type = gdb.parse_and_eval('tree_code_type')
except:
val_tree_code_type = gdb.parse_and_eval('tree_code_type_tmpl<0>::tree_code_type')
val_tclass = val_tree_code_type[val_TREE_CODE]
val_tree_code_name = gdb.parse_and_eval('tree_code_name')
val_code_name = val_tree_code_name[intptr(val_TREE_CODE)]
#print(val_code_name.string())
try:
result = '<%s 0x%x' % (val_code_name.string(), intptr(self.gdbval))
except:
return '<tree 0x%x>' % intptr(self.gdbval)
if intptr(val_tclass) == tcc_declaration:
tree_DECL_NAME = self.node.DECL_NAME()
if tree_DECL_NAME.is_nonnull():
result += ' %s' % tree_DECL_NAME.IDENTIFIER_POINTER()
else:
pass # TODO: labels etc
elif intptr(val_tclass) == tcc_type:
tree_TYPE_NAME = Tree(self.gdbval['type_common']['name'])
if tree_TYPE_NAME.is_nonnull():
if tree_TYPE_NAME.TREE_CODE() == IDENTIFIER_NODE:
result += ' %s' % tree_TYPE_NAME.IDENTIFIER_POINTER()
elif tree_TYPE_NAME.TREE_CODE() == TYPE_DECL:
if tree_TYPE_NAME.DECL_NAME().is_nonnull():
result += ' %s' % tree_TYPE_NAME.DECL_NAME().IDENTIFIER_POINTER()
if self.node.TREE_CODE() == IDENTIFIER_NODE:
result += ' %s' % self.node.IDENTIFIER_POINTER()
elif self.node.TREE_CODE() == SSA_NAME:
result += ' %u' % self.gdbval['base']['u']['version']
# etc
result += '>'
return result
######################################################################
# Callgraph pretty-printers
######################################################################
class SymtabNodePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
t = str(self.gdbval.type)
result = '<%s 0x%x' % (t, intptr(self.gdbval))
if intptr(self.gdbval):
# symtab_node::name calls lang_hooks.decl_printable_name
# default implementation (lhd_decl_printable_name) is:
# return IDENTIFIER_POINTER (DECL_NAME (decl));
tree_decl = Tree(self.gdbval['decl'])
result += ' "%s"/%d' % (tree_decl.DECL_NAME().IDENTIFIER_POINTER(), self.gdbval['order'])
result += '>'
return result
class CgraphEdgePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
result = '<cgraph_edge* 0x%x' % intptr(self.gdbval)
if intptr(self.gdbval):
src = SymtabNodePrinter(self.gdbval['caller']).to_string()
dest = SymtabNodePrinter(self.gdbval['callee']).to_string()
result += ' (%s -> %s)' % (src, dest)
result += '>'
return result
class IpaReferencePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
result = '<ipa_ref* 0x%x' % intptr(self.gdbval)
if intptr(self.gdbval):
src = SymtabNodePrinter(self.gdbval['referring']).to_string()
dest = SymtabNodePrinter(self.gdbval['referred']).to_string()
result += ' (%s -> %s:%s)' % (src, dest, str(self.gdbval['use']))
result += '>'
return result
######################################################################
# Dwarf DIE pretty-printers
######################################################################
class DWDieRefPrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
if intptr(self.gdbval) == 0:
return '<dw_die_ref 0x0>'
result = '<dw_die_ref 0x%x' % intptr(self.gdbval)
result += ' %s' % self.gdbval['die_tag']
if intptr(self.gdbval['die_parent']) != 0:
result += ' <parent=0x%x %s>' % (intptr(self.gdbval['die_parent']),
self.gdbval['die_parent']['die_tag'])
result += '>'
return result
######################################################################
class GimplePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
if intptr(self.gdbval) == 0:
return '<gimple 0x0>'
val_gimple_code = self.gdbval['code']
val_gimple_code_name = gdb.parse_and_eval('gimple_code_name')
val_code_name = val_gimple_code_name[intptr(val_gimple_code)]
result = '<%s 0x%x' % (val_code_name.string(),
intptr(self.gdbval))
result += '>'
return result
######################################################################
# CFG pretty-printers
######################################################################
def bb_index_to_str(index):
if index == 0:
return 'ENTRY'
elif index == 1:
return 'EXIT'
else:
return '%i' % index
class BasicBlockPrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
result = '<basic_block 0x%x' % intptr(self.gdbval)
if intptr(self.gdbval):
result += ' (%s)' % bb_index_to_str(intptr(self.gdbval['index']))
result += '>'
return result
class CfgEdgePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
result = '<edge 0x%x' % intptr(self.gdbval)
if intptr(self.gdbval):
src = bb_index_to_str(intptr(self.gdbval['src']['index']))
dest = bb_index_to_str(intptr(self.gdbval['dest']['index']))
result += ' (%s -> %s)' % (src, dest)
result += '>'
return result
######################################################################
# Pretty-printers for -fanalyzer (namespace ana)
######################################################################
class AnaSupernodePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
result = '<ana::supernode 0x%x' % intptr(self.gdbval)
if intptr(self.gdbval):
result += ' (SN %i)' % intptr(self.gdbval['m_id'])
result += '>'
return result
class AnaExplodedNodePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
result = '<ana::exploded_node 0x%x' % intptr(self.gdbval)
if intptr(self.gdbval):
result += ' (EN %i)' % intptr(self.gdbval['m_index'])
result += '>'
return result
######################################################################
class Rtx:
def __init__(self, gdbval):
self.gdbval = gdbval
def GET_CODE(self):
return self.gdbval['code']
def GET_RTX_LENGTH(code):
val_rtx_length = gdb.parse_and_eval('rtx_length')
return intptr(val_rtx_length[code])
def GET_RTX_NAME(code):
val_rtx_name = gdb.parse_and_eval('rtx_name')
return val_rtx_name[code].string()
def GET_RTX_FORMAT(code):
val_rtx_format = gdb.parse_and_eval('rtx_format')
return val_rtx_format[code].string()
class RtxPrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
self.rtx = Rtx(gdbval)
def to_string (self):
"""
For now, a cheap kludge: invoke the inferior's print
function to get a string to use the user, and return an empty
string for gdb
"""
# We use print_inline_rtx to avoid a trailing newline
gdb.execute('call print_inline_rtx (stderr, (const_rtx) %s, 0)'
% intptr(self.gdbval))
return ''
# or by hand; based on gcc/print-rtl.c:print_rtx
result = ('<rtx_def 0x%x'
% (intptr(self.gdbval)))
code = self.rtx.GET_CODE()
result += ' (%s' % GET_RTX_NAME(code)
format_ = GET_RTX_FORMAT(code)
for i in range(GET_RTX_LENGTH(code)):
print(format_[i])
result += ')>'
return result
######################################################################
class PassPrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
result = '<opt_pass* 0x%x' % intptr(self.gdbval)
if intptr(self.gdbval):
result += (' "%s"(%i)'
% (self.gdbval['name'].string(),
intptr(self.gdbval['static_pass_number'])))
result += '>'
return result
######################################################################
VEC_KIND_EMBED = 0
VEC_KIND_PTR = 1
"""
Given a vec or pointer to vec, return its layout (embedded or space
efficient).
"""
def get_vec_kind(val):
typ = val.type
if typ.code == gdb.TYPE_CODE_PTR:
typ = typ.target()
kind = typ.template_argument(2).name
if kind == "vl_embed":
return VEC_KIND_EMBED
elif kind == "vl_ptr":
return VEC_KIND_PTR
else:
assert False, f"unexpected vec kind {kind}"
def strip_ref(gdbval):
if gdbval.type.code == gdb.TYPE_CODE_REF:
return gdbval.referenced_value ()
return gdbval
class VecPrinter:
# -ex "up" -ex "p bb->preds"
def __init__(self, gdbval):
self.gdbval = gdbval
def display_hint (self):
return 'array'
def to_string (self):
# A trivial implementation; prettyprinting the contents is done
# by gdb calling the "children" method below.
return '0x%x' % intptr(strip_ref(self.gdbval))
def children (self):
val = strip_ref(self.gdbval)
if intptr(val) != 0 and get_vec_kind(val) == VEC_KIND_PTR:
val = val['m_vec']
if intptr(val) == 0:
return
assert get_vec_kind(val) == VEC_KIND_EMBED
m_vecpfx = val['m_vecpfx']
m_num = m_vecpfx['m_num']
typ = val.type
if typ.code == gdb.TYPE_CODE_PTR:
typ = typ.target()
else:
val = val.address
typ_T = typ.template_argument(0) # the type T
vecdata = (val + 1).cast(typ_T.pointer())
for i in range(m_num):
yield ('[%d]' % i, vecdata[i])
######################################################################
class MachineModePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
name = str(self.gdbval['m_mode'])
return name[2:] if name.startswith('E_') else name
######################################################################
class OptMachineModePrinter:
def __init__(self, gdbval):
self.gdbval = gdbval
def to_string (self):
name = str(self.gdbval['m_mode'])
if name == 'E_VOIDmode':
return '<None>'
return name[2:] if name.startswith('E_') else name
######################################################################
# TODO:
# * hashtab
# * location_t
class GdbSubprinter(gdb.printing.SubPrettyPrinter):
def __init__(self, name, class_):
super(GdbSubprinter, self).__init__(name)
self.class_ = class_
def handles_type(self, str_type):
raise NotImplementedError
class GdbSubprinterTypeList(GdbSubprinter):
"""
A GdbSubprinter that handles a specific set of types
"""
def __init__(self, str_types, name, class_):
super(GdbSubprinterTypeList, self).__init__(name, class_)
self.str_types = frozenset(str_types)
def handles_type(self, str_type):
return str_type in self.str_types
class GdbSubprinterRegex(GdbSubprinter):
"""
A GdbSubprinter that handles types that match a regex
"""
def __init__(self, regex, name, class_):
super(GdbSubprinterRegex, self).__init__(name, class_)
self.regex = re.compile(regex)
def handles_type(self, str_type):
return self.regex.match(str_type)
class GdbPrettyPrinters(gdb.printing.PrettyPrinter):
def __init__(self, name):
super(GdbPrettyPrinters, self).__init__(name, [])
def add_printer_for_types(self, types, name, class_):
self.subprinters.append(GdbSubprinterTypeList(types, name, class_))
def add_printer_for_regex(self, regex, name, class_):
self.subprinters.append(GdbSubprinterRegex(regex, name, class_))
def __call__(self, gdbval):
type_ = gdbval.type.unqualified()
str_type = str(type_)
for printer in self.subprinters:
if printer.enabled and printer.handles_type(str_type):
return printer.class_(gdbval)
# Couldn't find a pretty printer (or it was disabled):
return None
def build_pretty_printer():
pp = GdbPrettyPrinters('gcc')
pp.add_printer_for_types(['tree', 'const_tree'],
'tree', TreePrinter)
pp.add_printer_for_types(['cgraph_node *', 'varpool_node *', 'symtab_node *'],
'symtab_node', SymtabNodePrinter)
pp.add_printer_for_types(['cgraph_edge *'],
'cgraph_edge', CgraphEdgePrinter)
pp.add_printer_for_types(['ipa_ref *'],
'ipa_ref', IpaReferencePrinter)
pp.add_printer_for_types(['dw_die_ref'],
'dw_die_ref', DWDieRefPrinter)
pp.add_printer_for_types(['gimple', 'gimple *',
# Keep this in the same order as gimple.def:
'gimple_cond', 'const_gimple_cond',
'gimple_statement_cond *',
'gimple_debug', 'const_gimple_debug',
'gimple_statement_debug *',
'gimple_label', 'const_gimple_label',
'gimple_statement_label *',
'gimple_switch', 'const_gimple_switch',
'gimple_statement_switch *',
'gimple_assign', 'const_gimple_assign',
'gimple_statement_assign *',
'gimple_bind', 'const_gimple_bind',
'gimple_statement_bind *',
'gimple_phi', 'const_gimple_phi',
'gimple_statement_phi *'],
'gimple',
GimplePrinter)
pp.add_printer_for_types(['basic_block', 'basic_block_def *'],
'basic_block',
BasicBlockPrinter)
pp.add_printer_for_types(['ana::supernode *', 'const ana::supernode *'],
'ana::supernode',
AnaSupernodePrinter)
pp.add_printer_for_types(['ana::exploded_node *',
'dedge<ana::eg_traits>::node_t *'],
'ana::exploded_node',
AnaExplodedNodePrinter)
pp.add_printer_for_types(['edge', 'edge_def *'],
'edge',
CfgEdgePrinter)
pp.add_printer_for_types(['rtx_def *'], 'rtx_def', RtxPrinter)
pp.add_printer_for_types(['opt_pass *'], 'opt_pass', PassPrinter)
pp.add_printer_for_regex(r'vec<(\S+), (\S+), (\S+)> \*',
'vec',
VecPrinter)
pp.add_printer_for_regex(r'opt_mode<(\S+)>',
'opt_mode', OptMachineModePrinter)
pp.add_printer_for_types(['opt_scalar_int_mode',
'opt_scalar_float_mode',
'opt_scalar_mode'],
'opt_mode', OptMachineModePrinter)
pp.add_printer_for_regex(r'pod_mode<(\S+)>',
'pod_mode', MachineModePrinter)
pp.add_printer_for_types(['scalar_int_mode_pod',
'scalar_mode_pod'],
'pod_mode', MachineModePrinter)
for mode in ('scalar_mode', 'scalar_int_mode', 'scalar_float_mode',
'complex_mode'):
pp.add_printer_for_types([mode], mode, MachineModePrinter)
return pp
gdb.printing.register_pretty_printer(
gdb.current_objfile(),
build_pretty_printer(),
replace=True)
def find_gcc_source_dir():
# Use location of global "g" to locate the source tree
sym_g = gdb.lookup_global_symbol('g')
path = sym_g.symtab.filename # e.g. '../../src/gcc/context.h'
srcdir = os.path.split(path)[0] # e.g. '../../src/gcc'
return srcdir
class PassNames:
"""Parse passes.def, gathering a list of pass class names"""
def __init__(self):
srcdir = find_gcc_source_dir()
self.names = []
with open(os.path.join(srcdir, 'passes.def')) as f:
for line in f:
m = re.match(r'\s*NEXT_PASS \(([^,]+).*\);', line)
if m:
self.names.append(m.group(1))
class BreakOnPass(gdb.Command):
"""
A custom command for putting breakpoints on the execute hook of passes.
This is largely a workaround for issues with tab-completion in gdb when
setting breakpoints on methods on classes within anonymous namespaces.
Example of use: putting a breakpoint on "final"
(gdb) break-on-pass
Press <TAB>; it autocompletes to "pass_":
(gdb) break-on-pass pass_
Press <TAB>:
Display all 219 possibilities? (y or n)
Press "n"; then type "f":
(gdb) break-on-pass pass_f
Press <TAB> to autocomplete to pass classnames beginning with "pass_f":
pass_fast_rtl_dce pass_fold_builtins
pass_feedback_split_functions pass_forwprop
pass_final pass_fre
pass_fixup_cfg pass_free_cfg
Type "in<TAB>" to complete to "pass_final":
(gdb) break-on-pass pass_final
...and hit <RETURN>:
Breakpoint 6 at 0x8396ba: file ../../src/gcc/final.c, line 4526.
...and we have a breakpoint set; continue execution:
(gdb) cont
Continuing.
Breakpoint 6, (anonymous namespace)::pass_final::execute (this=0x17fb990) at ../../src/gcc/final.c:4526
4526 virtual unsigned int execute (function *) { return rest_of_handle_final (); }
"""
def __init__(self):
gdb.Command.__init__(self, 'break-on-pass', gdb.COMMAND_BREAKPOINTS)
self.pass_names = None
def complete(self, text, word):
# Lazily load pass names:
if not self.pass_names:
self.pass_names = PassNames()
return [name
for name in sorted(self.pass_names.names)
if name.startswith(text)]
def invoke(self, arg, from_tty):
sym = '(anonymous namespace)::%s::execute' % arg
breakpoint = gdb.Breakpoint(sym)
BreakOnPass()
class DumpFn(gdb.Command):
"""
A custom command to dump a gimple/rtl function to file. By default, it
dumps the current function using 0 as dump_flags, but the function and flags
can also be specified. If /f <file> are passed as the first two arguments,
the dump is written to that file. Otherwise, a temporary file is created
and opened in the text editor specified in the EDITOR environment variable.
Examples of use:
(gdb) dump-fn
(gdb) dump-fn /f foo.1.txt
(gdb) dump-fn cfun->decl
(gdb) dump-fn /f foo.1.txt cfun->decl
(gdb) dump-fn cfun->decl 0
(gdb) dump-fn cfun->decl dump_flags
"""
def __init__(self):
gdb.Command.__init__(self, 'dump-fn', gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
# Parse args, check number of args
args = gdb.string_to_argv(arg)
if len(args) >= 1 and args[0] == "/f":
if len(args) == 1:
print ("Missing file argument")
return
filename = args[1]
editor_mode = False
base_arg = 2
else:
editor = os.getenv("EDITOR", "")
if editor == "":
print ("EDITOR environment variable not defined")
return
editor_mode = True
base_arg = 0
if len(args) - base_arg > 2:
print ("Too many arguments")
return
# Set func
if len(args) - base_arg >= 1:
funcname = args[base_arg]
printfuncname = "function %s" % funcname
else:
funcname = "cfun ? cfun->decl : current_function_decl"
printfuncname = "current function"
func = gdb.parse_and_eval(funcname)
if func == 0:
print ("Could not find %s" % printfuncname)
return
func = "(tree)%u" % func
# Set flags
if len(args) - base_arg >= 2:
flags = gdb.parse_and_eval(args[base_arg + 1])
else:
flags = 0
# Get tempory file, if necessary
if editor_mode:
f = tempfile.NamedTemporaryFile(delete=False, suffix=".txt")
filename = f.name
f.close()
# Open file
fp = gdb.parse_and_eval("(FILE *) fopen (\"%s\", \"w\")" % filename)
if fp == 0:
print ("Could not open file: %s" % filename)
return
# Dump function to file
_ = gdb.parse_and_eval("dump_function_to_file (%s, %s, %u)" %
(func, fp, flags))
# Close file
ret = gdb.parse_and_eval("(int) fclose (%s)" % fp)
if ret != 0:
print ("Could not close file: %s" % filename)
return
# Open file in editor, if necessary
if editor_mode:
os.system("( %s \"%s\"; rm \"%s\" ) &" %
(editor, filename, filename))
DumpFn()
class GCCDotCmd(gdb.Parameter):
"""
This parameter controls the command used to render dot files within
GCC's dot-fn command. It will be invoked as gcc-dot-cmd <dot-file>.
"""
def __init__(self):
super(GCCDotCmd, self).__init__('gcc-dot-cmd',
gdb.COMMAND_NONE, gdb.PARAM_STRING)
self.value = "dot -Tx11"
gcc_dot_cmd = GCCDotCmd()
class DotFn(gdb.Command):
"""
A custom command to show a gimple/rtl function control flow graph.
By default, it show the current function, but the function can also be
specified.
Examples of use:
(gdb) dot-fn
(gdb) dot-fn cfun
(gdb) dot-fn cfun 0
(gdb) dot-fn cfun dump_flags
"""
def __init__(self):
gdb.Command.__init__(self, 'dot-fn', gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
# Parse args, check number of args
args = gdb.string_to_argv(arg)
if len(args) > 2:
print("Too many arguments")
return
# Set func
if len(args) >= 1:
funcname = args[0]
printfuncname = "function %s" % funcname
else:
funcname = "cfun"
printfuncname = "current function"
func = gdb.parse_and_eval(funcname)
if func == 0:
print("Could not find %s" % printfuncname)
return
func = "(struct function *)%s" % func
# Set flags
if len(args) >= 2:
flags = gdb.parse_and_eval(args[1])
else:
flags = 0
# Get temp file
f = tempfile.NamedTemporaryFile(delete=False)
filename = f.name
# Close and reopen temp file to get C FILE*
f.close()
fp = gdb.parse_and_eval("(FILE *) fopen (\"%s\", \"w\")" % filename)
if fp == 0:
print("Cannot open temp file")
return
# Write graph to temp file
_ = gdb.parse_and_eval("start_graph_dump (%s, \"<debug>\")" % fp)
_ = gdb.parse_and_eval("print_graph_cfg (%s, %s, %u)"
% (fp, func, flags))
_ = gdb.parse_and_eval("end_graph_dump (%s)" % fp)
# Close temp file
ret = gdb.parse_and_eval("(int) fclose (%s)" % fp)
if ret != 0:
print("Could not close temp file: %s" % filename)
return
# Show graph in temp file
dot_cmd = gcc_dot_cmd.value
os.system("( %s \"%s\"; rm \"%s\" ) &" % (dot_cmd, filename, filename))
DotFn()
# Try and invoke the user-defined command "on-gcc-hooks-load". Doing
# this allows users to customize the GCC extensions once they've been
# loaded by defining the hook in their .gdbinit.
try:
gdb.execute('on-gcc-hooks-load')
except gdb.error:
pass
print('Successfully loaded GDB hooks for GCC')