mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
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>
890 lines
26 KiB
C++
890 lines
26 KiB
C++
/* Classes for working with summaries of function calls.
|
|
Copyright (C) 2022 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/>. */
|
|
|
|
#include "analyzer/common.h"
|
|
|
|
#include "analyzer/region-model.h"
|
|
#include "analyzer/call-summary.h"
|
|
#include "analyzer/exploded-graph.h"
|
|
|
|
#if ENABLE_ANALYZER
|
|
|
|
namespace ana {
|
|
|
|
/* class call_summary. */
|
|
|
|
const program_state &
|
|
call_summary::get_state () const
|
|
{
|
|
return m_enode->get_state ();
|
|
}
|
|
|
|
tree
|
|
call_summary::get_fndecl () const
|
|
{
|
|
return m_enode->get_function ()->decl;
|
|
}
|
|
|
|
label_text
|
|
call_summary::get_desc () const
|
|
{
|
|
pretty_printer pp;
|
|
pp_format_decoder (&pp) = default_tree_printer;
|
|
|
|
get_user_facing_desc (&pp);
|
|
if (flag_analyzer_verbose_edges)
|
|
pp_printf (&pp, " (call summary; EN: %i)", m_enode->m_index);
|
|
|
|
return label_text::take (xstrdup (pp_formatted_text (&pp)));
|
|
}
|
|
|
|
/* Generate a user-facing description of this call summary.c
|
|
This has various heuristics for distinguishing between different
|
|
summaries.
|
|
This will help with debugging, too. */
|
|
|
|
void
|
|
call_summary::get_user_facing_desc (pretty_printer *pp) const
|
|
{
|
|
tree fndecl = get_fndecl ();
|
|
|
|
/* If there are multiple summaries, try to use the return value to
|
|
distinguish between them. */
|
|
if (m_per_fn_data->m_summaries.length () > 1)
|
|
{
|
|
if (tree result = DECL_RESULT (fndecl))
|
|
{
|
|
const region *result_reg
|
|
= get_state ().m_region_model->get_lvalue (result, nullptr);
|
|
const svalue *result_sval
|
|
= get_state ().m_region_model->get_store_value (result_reg, nullptr);
|
|
switch (result_sval->get_kind ())
|
|
{
|
|
default:
|
|
break;
|
|
case SK_REGION:
|
|
{
|
|
const region_svalue *region_sval
|
|
= as_a <const region_svalue *> (result_sval);
|
|
const region *pointee_reg = region_sval->get_pointee ();
|
|
switch (pointee_reg->get_kind ())
|
|
{
|
|
default:
|
|
break;
|
|
case RK_HEAP_ALLOCATED:
|
|
pp_printf (pp,
|
|
"when %qE returns pointer"
|
|
" to heap-allocated buffer",
|
|
fndecl);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case SK_CONSTANT:
|
|
{
|
|
const constant_svalue *constant_sval
|
|
= as_a <const constant_svalue *> (result_sval);
|
|
tree cst = constant_sval->get_constant ();
|
|
if (POINTER_TYPE_P (TREE_TYPE (result))
|
|
&& zerop (cst))
|
|
pp_printf (pp, "when %qE returns NULL", fndecl);
|
|
else
|
|
pp_printf (pp, "when %qE returns %qE", fndecl, cst);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Fallback. */
|
|
pp_printf (pp, "when %qE returns", fndecl);
|
|
}
|
|
|
|
/* Dump a multiline representation of this object to PP. */
|
|
|
|
void
|
|
call_summary::dump_to_pp (const extrinsic_state &ext_state,
|
|
pretty_printer *pp,
|
|
bool simple) const
|
|
{
|
|
label_text desc = get_desc ();
|
|
pp_printf (pp, "desc: %qs", desc.get ());
|
|
pp_newline (pp);
|
|
|
|
get_state ().dump_to_pp (ext_state, simple, true, pp);
|
|
}
|
|
|
|
/* Dump a multiline representation of this object to FILE. */
|
|
|
|
void
|
|
call_summary::dump (const extrinsic_state &ext_state,
|
|
FILE *fp,
|
|
bool simple) const
|
|
{
|
|
tree_dump_pretty_printer pp (fp);
|
|
dump_to_pp (ext_state, &pp, simple);
|
|
}
|
|
|
|
/* Dump a multiline representation of this object to stderr. */
|
|
|
|
DEBUG_FUNCTION void
|
|
call_summary::dump (const extrinsic_state &ext_state, bool simple) const
|
|
{
|
|
dump (ext_state, stderr, simple);
|
|
}
|
|
|
|
/* class call_summary_replay. */
|
|
|
|
/* call_summary_replay's ctor.
|
|
Populate the cache with params for the summary based on
|
|
arguments at the caller. */
|
|
|
|
call_summary_replay::call_summary_replay (const call_details &cd,
|
|
const function &called_fn,
|
|
call_summary &summary,
|
|
const extrinsic_state &ext_state)
|
|
: m_cd (cd),
|
|
m_summary (summary),
|
|
m_ext_state (ext_state)
|
|
{
|
|
region_model_manager *mgr = cd.get_manager ();
|
|
|
|
// populate params based on args
|
|
tree fndecl = called_fn.decl;
|
|
|
|
/* Get a frame_region for use with respect to the summary.
|
|
This will be a top-level frame, since that's what's in
|
|
the summary. */
|
|
const frame_region *summary_frame
|
|
= mgr->get_frame_region (nullptr, called_fn);
|
|
|
|
unsigned idx = 0;
|
|
for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
|
|
iter_parm = DECL_CHAIN (iter_parm), ++idx)
|
|
{
|
|
/* If there's a mismatching declaration, the call stmt might
|
|
not have enough args. Handle this case by leaving the
|
|
rest of the params as uninitialized. */
|
|
if (idx >= cd.num_args ())
|
|
break;
|
|
const svalue *caller_arg_sval = cd.get_arg_svalue (idx);
|
|
tree parm_lval = iter_parm;
|
|
if (tree parm_default_ssa = get_ssa_default_def (called_fn, iter_parm))
|
|
parm_lval = parm_default_ssa;
|
|
const region *summary_parm_reg
|
|
= summary_frame->get_region_for_local (mgr, parm_lval, cd.get_ctxt ());
|
|
const svalue *summary_initial_parm_reg
|
|
= mgr->get_or_create_initial_value (summary_parm_reg);
|
|
add_svalue_mapping (summary_initial_parm_reg, caller_arg_sval);
|
|
}
|
|
|
|
/* Handle any variadic args. */
|
|
unsigned va_arg_idx = 0;
|
|
for (; idx < cd.num_args (); idx++, va_arg_idx++)
|
|
{
|
|
const svalue *caller_arg_sval = cd.get_arg_svalue (idx);
|
|
const region *summary_var_arg_reg
|
|
= mgr->get_var_arg_region (summary_frame, va_arg_idx);
|
|
const svalue *summary_initial_var_arg_reg
|
|
= mgr->get_or_create_initial_value (summary_var_arg_reg);
|
|
add_svalue_mapping (summary_initial_var_arg_reg, caller_arg_sval);
|
|
}
|
|
}
|
|
|
|
/* Try to convert SUMMARY_SVAL in the summary to a corresponding svalue
|
|
in the caller, caching the result.
|
|
|
|
Return nullptr if the conversion is not possible. */
|
|
|
|
const svalue *
|
|
call_summary_replay::convert_svalue_from_summary (const svalue *summary_sval)
|
|
{
|
|
gcc_assert (summary_sval);
|
|
|
|
if (const svalue **slot
|
|
= m_map_svalue_from_summary_to_caller.get (summary_sval))
|
|
return *slot;
|
|
|
|
const svalue *caller_sval = convert_svalue_from_summary_1 (summary_sval);
|
|
|
|
if (caller_sval)
|
|
if (summary_sval->get_type () && caller_sval->get_type ())
|
|
gcc_assert (types_compatible_p (summary_sval->get_type (),
|
|
caller_sval->get_type ()));
|
|
|
|
/* Add to cache. */
|
|
add_svalue_mapping (summary_sval, caller_sval);
|
|
|
|
return caller_sval;
|
|
}
|
|
|
|
/* Implementation of call_summary_replay::convert_svalue_from_summary. */
|
|
|
|
const svalue *
|
|
call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval)
|
|
{
|
|
gcc_assert (summary_sval);
|
|
|
|
switch (summary_sval->get_kind ())
|
|
{
|
|
default:
|
|
gcc_unreachable ();
|
|
case SK_REGION:
|
|
{
|
|
const region_svalue *region_summary_sval
|
|
= as_a <const region_svalue *> (summary_sval);
|
|
const region *summary_reg = region_summary_sval->get_pointee ();
|
|
const region *caller_reg = convert_region_from_summary (summary_reg);
|
|
if (!caller_reg)
|
|
return nullptr;
|
|
region_model_manager *mgr = get_manager ();
|
|
const svalue *caller_ptr
|
|
= mgr->get_ptr_svalue (summary_sval->get_type (),
|
|
caller_reg);
|
|
return caller_ptr;
|
|
}
|
|
break;
|
|
|
|
case SK_CONSTANT:
|
|
case SK_PLACEHOLDER:
|
|
case SK_POISONED:
|
|
case SK_UNKNOWN:
|
|
return summary_sval;
|
|
|
|
case SK_SETJMP:
|
|
return nullptr; // TODO
|
|
|
|
case SK_INITIAL:
|
|
{
|
|
const initial_svalue *initial_summary_sval
|
|
= as_a <const initial_svalue *> (summary_sval);
|
|
/* Params should already be in the cache, courtesy of the ctor. */
|
|
gcc_assert (!initial_summary_sval->initial_value_of_param_p ());
|
|
|
|
/* Initial value of region within the summary is the value of the
|
|
region at the point of the call. */
|
|
const region *summary_reg = initial_summary_sval->get_region ();
|
|
const region *caller_reg = convert_region_from_summary (summary_reg);
|
|
if (!caller_reg)
|
|
return nullptr;
|
|
const svalue *caller_sval
|
|
= m_cd.get_model ()->get_store_value (caller_reg, m_cd.get_ctxt ());
|
|
return caller_sval;
|
|
}
|
|
break;
|
|
case SK_UNARYOP:
|
|
{
|
|
const unaryop_svalue *unaryop_summary_sval
|
|
= as_a <const unaryop_svalue *> (summary_sval);
|
|
const svalue *summary_arg = unaryop_summary_sval->get_arg ();
|
|
const svalue *caller_arg = convert_svalue_from_summary (summary_arg);
|
|
if (!caller_arg)
|
|
return nullptr;
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_unaryop (summary_sval->get_type (),
|
|
unaryop_summary_sval->get_op (),
|
|
caller_arg);
|
|
}
|
|
break;
|
|
case SK_BINOP:
|
|
{
|
|
const binop_svalue *binop_summary_sval
|
|
= as_a <const binop_svalue *> (summary_sval);
|
|
const svalue *summary_arg0 = binop_summary_sval->get_arg0 ();
|
|
const svalue *caller_arg0 = convert_svalue_from_summary (summary_arg0);
|
|
if (!caller_arg0)
|
|
return nullptr;
|
|
const svalue *summary_arg1 = binop_summary_sval->get_arg1 ();
|
|
const svalue *caller_arg1 = convert_svalue_from_summary (summary_arg1);
|
|
if (!caller_arg1)
|
|
return nullptr;
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_binop (summary_sval->get_type (),
|
|
binop_summary_sval->get_op (),
|
|
caller_arg0,
|
|
caller_arg1);
|
|
}
|
|
break;
|
|
case SK_SUB:
|
|
{
|
|
const sub_svalue *sub_summary_sval
|
|
= as_a <const sub_svalue *> (summary_sval);
|
|
region_model_manager *mgr = get_manager ();
|
|
const svalue *summary_parent_sval = sub_summary_sval->get_parent ();
|
|
if (!summary_parent_sval)
|
|
return nullptr;
|
|
const region *summary_subregion = sub_summary_sval->get_subregion ();
|
|
if (!summary_subregion)
|
|
return nullptr;
|
|
return mgr->get_or_create_sub_svalue (summary_sval->get_type (),
|
|
summary_parent_sval,
|
|
summary_subregion);
|
|
}
|
|
break;
|
|
case SK_REPEATED:
|
|
{
|
|
const repeated_svalue *repeated_summary_sval
|
|
= as_a <const repeated_svalue *> (summary_sval);
|
|
const svalue *summary_outer_size
|
|
= repeated_summary_sval->get_outer_size ();
|
|
const svalue *caller_outer_size
|
|
= convert_svalue_from_summary (summary_outer_size);
|
|
if (!caller_outer_size)
|
|
return nullptr;
|
|
const svalue *summary_inner_sval
|
|
= repeated_summary_sval->get_inner_svalue ();
|
|
const svalue *caller_inner_sval
|
|
= convert_svalue_from_summary (summary_inner_sval);
|
|
if (!caller_inner_sval)
|
|
return nullptr;
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_repeated_svalue (summary_sval->get_type (),
|
|
caller_outer_size,
|
|
caller_inner_sval);
|
|
}
|
|
break;
|
|
case SK_BITS_WITHIN:
|
|
{
|
|
const bits_within_svalue *bits_within_summary_sval
|
|
= as_a <const bits_within_svalue *> (summary_sval);
|
|
const bit_range &bits = bits_within_summary_sval->get_bits ();
|
|
const svalue *summary_inner_sval
|
|
= bits_within_summary_sval->get_inner_svalue ();
|
|
const svalue *caller_inner_sval
|
|
= convert_svalue_from_summary (summary_inner_sval);
|
|
if (!caller_inner_sval)
|
|
return nullptr;
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_bits_within (summary_sval->get_type (),
|
|
bits,
|
|
caller_inner_sval);
|
|
}
|
|
break;
|
|
case SK_UNMERGEABLE:
|
|
{
|
|
const unmergeable_svalue *unmergeable_summary_sval
|
|
= as_a <const unmergeable_svalue *> (summary_sval);
|
|
const svalue *summary_arg_sval = unmergeable_summary_sval->get_arg ();
|
|
const svalue *caller_arg_sval
|
|
= convert_svalue_from_summary (summary_arg_sval);
|
|
if (!caller_arg_sval)
|
|
return nullptr;
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_unmergeable (caller_arg_sval);
|
|
}
|
|
break;
|
|
case SK_WIDENING:
|
|
{
|
|
const widening_svalue *widening_summary_sval
|
|
= as_a <const widening_svalue *> (summary_sval);
|
|
const supernode *snode = widening_summary_sval->get_snode ();
|
|
const svalue *summary_base_sval
|
|
= widening_summary_sval->get_base_svalue ();
|
|
const svalue *caller_base_sval
|
|
= convert_svalue_from_summary (summary_base_sval);
|
|
if (!(caller_base_sval
|
|
&& caller_base_sval->can_have_associated_state_p ()))
|
|
return nullptr;
|
|
const svalue *summary_iter_sval
|
|
= widening_summary_sval->get_iter_svalue ();
|
|
const svalue *caller_iter_sval
|
|
= convert_svalue_from_summary (summary_iter_sval);
|
|
if (!(caller_iter_sval
|
|
&& caller_iter_sval->can_have_associated_state_p ()))
|
|
return nullptr;
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_widening_svalue
|
|
(summary_iter_sval->get_type (),
|
|
snode,
|
|
caller_base_sval,
|
|
caller_iter_sval);
|
|
}
|
|
break;
|
|
case SK_COMPOUND:
|
|
{
|
|
const compound_svalue *compound_summary_sval
|
|
= as_a <const compound_svalue *> (summary_sval);
|
|
region_model_manager *mgr = get_manager ();
|
|
store_manager *store_mgr = mgr->get_store_manager ();
|
|
binding_map caller_map (*store_mgr);
|
|
auto_vec <const binding_key *> summary_keys;
|
|
for (auto kv : *compound_summary_sval)
|
|
summary_keys.safe_push (kv.m_key);
|
|
summary_keys.qsort (binding_key::cmp_ptrs);
|
|
for (auto key : summary_keys)
|
|
{
|
|
gcc_assert (key->concrete_p ());
|
|
/* No remapping is needed for concrete binding keys. */
|
|
|
|
const svalue *bound_summary_sval
|
|
= compound_summary_sval->get_map ().get (key);
|
|
const svalue *caller_sval
|
|
= convert_svalue_from_summary (bound_summary_sval);
|
|
if (!caller_sval)
|
|
caller_sval = mgr->get_or_create_unknown_svalue (NULL_TREE);
|
|
|
|
if (const compound_svalue *inner_compound_sval
|
|
= caller_sval->dyn_cast_compound_svalue ())
|
|
{
|
|
const concrete_binding *outer_key
|
|
= as_a <const concrete_binding *> (key);
|
|
for (auto inner_kv : *inner_compound_sval)
|
|
{
|
|
// These should already be mapped to the caller.
|
|
const binding_key *inner_key = inner_kv.m_key;
|
|
const svalue *inner_sval = inner_kv.m_sval;
|
|
gcc_assert (inner_key->concrete_p ());
|
|
const concrete_binding *concrete_key
|
|
= as_a <const concrete_binding *> (inner_key);
|
|
bit_offset_t effective_start
|
|
= (concrete_key->get_start_bit_offset ()
|
|
+ outer_key->get_start_bit_offset ());
|
|
const concrete_binding *effective_concrete_key
|
|
= store_mgr->get_concrete_binding
|
|
(effective_start,
|
|
concrete_key->get_size_in_bits ());
|
|
caller_map.put (effective_concrete_key, inner_sval);
|
|
}
|
|
}
|
|
else
|
|
caller_map.put (key, caller_sval);
|
|
}
|
|
return mgr->get_or_create_compound_svalue (summary_sval->get_type (),
|
|
caller_map);
|
|
}
|
|
break;
|
|
case SK_CONJURED:
|
|
{
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_unknown_svalue (summary_sval->get_type ());
|
|
}
|
|
break;
|
|
case SK_ASM_OUTPUT:
|
|
{
|
|
const asm_output_svalue *asm_output_summary_sval
|
|
= as_a <const asm_output_svalue *> (summary_sval);
|
|
const char *asm_string = asm_output_summary_sval->get_asm_string ();
|
|
unsigned output_idx = asm_output_summary_sval->get_output_idx ();
|
|
unsigned num_inputs = asm_output_summary_sval->get_num_inputs ();
|
|
unsigned num_outputs = asm_output_summary_sval->get_num_outputs ();
|
|
auto_vec<const svalue *> inputs (num_inputs);
|
|
for (unsigned idx = 0; idx < num_inputs; idx++)
|
|
{
|
|
const svalue *summary_input
|
|
= asm_output_summary_sval->get_input (idx);
|
|
const svalue *caller_input
|
|
= convert_svalue_from_summary (summary_input);
|
|
if (!caller_input)
|
|
return nullptr;
|
|
inputs.safe_push (caller_input);
|
|
}
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_asm_output_svalue (summary_sval->get_type (),
|
|
asm_string,
|
|
output_idx,
|
|
num_outputs,
|
|
inputs);
|
|
}
|
|
break;
|
|
case SK_CONST_FN_RESULT:
|
|
{
|
|
const const_fn_result_svalue *const_fn_result_summary_sval
|
|
= as_a <const const_fn_result_svalue *> (summary_sval);
|
|
tree fndecl = const_fn_result_summary_sval->get_fndecl ();
|
|
unsigned num_inputs = const_fn_result_summary_sval->get_num_inputs ();
|
|
auto_vec<const svalue *> inputs (num_inputs);
|
|
for (unsigned idx = 0; idx < num_inputs; idx++)
|
|
{
|
|
const svalue *summary_input
|
|
= const_fn_result_summary_sval->get_input (idx);
|
|
const svalue *caller_input
|
|
= convert_svalue_from_summary (summary_input);
|
|
if (!caller_input)
|
|
return nullptr;
|
|
inputs.safe_push (caller_input);
|
|
}
|
|
region_model_manager *mgr = get_manager ();
|
|
return mgr->get_or_create_const_fn_result_svalue
|
|
(summary_sval->get_type (),
|
|
fndecl,
|
|
inputs);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Try to convert SUMMARY_REG in the summary to a corresponding region
|
|
in the caller, caching the result.
|
|
|
|
Return nullptr if the conversion is not possible. */
|
|
|
|
const region *
|
|
call_summary_replay::convert_region_from_summary (const region *summary_reg)
|
|
{
|
|
gcc_assert (summary_reg);
|
|
|
|
if (const region **slot
|
|
= m_map_region_from_summary_to_caller.get (summary_reg))
|
|
return *slot;
|
|
|
|
const region *caller_reg = convert_region_from_summary_1 (summary_reg);
|
|
|
|
if (caller_reg)
|
|
if (summary_reg->get_type () && caller_reg->get_type ())
|
|
gcc_assert (types_compatible_p (summary_reg->get_type (),
|
|
caller_reg->get_type ()));
|
|
|
|
/* Add to cache. */
|
|
add_region_mapping (summary_reg, caller_reg);
|
|
|
|
return caller_reg;
|
|
}
|
|
|
|
/* Implementation of call_summary_replay::convert_region_from_summary. */
|
|
|
|
const region *
|
|
call_summary_replay::convert_region_from_summary_1 (const region *summary_reg)
|
|
{
|
|
gcc_assert (summary_reg);
|
|
|
|
region_model_manager *mgr = get_manager ();
|
|
switch (summary_reg->get_kind ())
|
|
{
|
|
default:
|
|
gcc_unreachable ();
|
|
/* Top-level regions. */
|
|
case RK_FRAME:
|
|
case RK_GLOBALS:
|
|
case RK_CODE:
|
|
case RK_STACK:
|
|
case RK_HEAP:
|
|
case RK_THREAD_LOCAL:
|
|
case RK_ROOT:
|
|
/* These should never be pointed to by a region_svalue. */
|
|
gcc_unreachable ();
|
|
|
|
case RK_FUNCTION:
|
|
case RK_LABEL:
|
|
case RK_STRING:
|
|
case RK_ERRNO:
|
|
case RK_UNKNOWN:
|
|
case RK_PRIVATE:
|
|
/* We can reuse these regions directly. */
|
|
return summary_reg;
|
|
|
|
case RK_SYMBOLIC:
|
|
{
|
|
const symbolic_region *summary_symbolic_reg
|
|
= as_a <const symbolic_region *> (summary_reg);
|
|
const svalue *summary_ptr_sval = summary_symbolic_reg->get_pointer ();
|
|
const svalue *caller_ptr_sval
|
|
= convert_svalue_from_summary (summary_ptr_sval);
|
|
if (!caller_ptr_sval)
|
|
return nullptr;
|
|
const region *caller_reg
|
|
= get_caller_model ()->deref_rvalue (caller_ptr_sval,
|
|
NULL_TREE,
|
|
get_ctxt ());
|
|
caller_reg = mgr->get_cast_region (caller_reg,
|
|
summary_reg->get_type ());
|
|
return caller_reg;
|
|
}
|
|
break;
|
|
|
|
case RK_DECL:
|
|
{
|
|
const decl_region *summary_decl_reg
|
|
= as_a <const decl_region *> (summary_reg);
|
|
tree decl = summary_decl_reg->get_decl ();
|
|
switch (TREE_CODE (decl))
|
|
{
|
|
default:
|
|
gcc_unreachable ();
|
|
case SSA_NAME:
|
|
/* We don't care about writes to locals within
|
|
the summary. */
|
|
return nullptr;
|
|
case VAR_DECL:
|
|
/* We don't care about writes to locals within
|
|
the summary. */
|
|
if (is_global_var (decl))
|
|
/* If it's a global, we can reuse the region directly. */
|
|
return summary_reg;
|
|
else
|
|
/* Otherwise, we don't care about locals. */
|
|
return nullptr;
|
|
case RESULT_DECL:
|
|
return m_cd.get_lhs_region ();
|
|
case PARM_DECL:
|
|
/* Writes (by value) to parms should be visible to the caller. */
|
|
return nullptr;
|
|
}
|
|
}
|
|
break;
|
|
case RK_FIELD:
|
|
{
|
|
const field_region *summary_field_reg
|
|
= as_a <const field_region *> (summary_reg);
|
|
const region *summary_parent_reg = summary_reg->get_parent_region ();
|
|
const region *caller_parent_reg
|
|
= convert_region_from_summary (summary_parent_reg);
|
|
if (!caller_parent_reg)
|
|
return nullptr;
|
|
tree field = summary_field_reg->get_field ();
|
|
return mgr->get_field_region (caller_parent_reg, field);
|
|
}
|
|
break;
|
|
case RK_ELEMENT:
|
|
{
|
|
const element_region *summary_element_reg
|
|
= as_a <const element_region *> (summary_reg);
|
|
const region *summary_parent_reg = summary_reg->get_parent_region ();
|
|
const region *caller_parent_reg
|
|
= convert_region_from_summary (summary_parent_reg);
|
|
if (!caller_parent_reg)
|
|
return nullptr;
|
|
const svalue *summary_index = summary_element_reg->get_index ();
|
|
const svalue *caller_index
|
|
= convert_svalue_from_summary (summary_index);
|
|
if (!caller_index)
|
|
return nullptr;
|
|
return mgr->get_element_region (caller_parent_reg,
|
|
summary_reg->get_type (),
|
|
caller_index);
|
|
}
|
|
break;
|
|
case RK_OFFSET:
|
|
{
|
|
const offset_region *summary_offset_reg
|
|
= as_a <const offset_region *> (summary_reg);
|
|
const region *summary_parent_reg = summary_reg->get_parent_region ();
|
|
const region *caller_parent_reg
|
|
= convert_region_from_summary (summary_parent_reg);
|
|
if (!caller_parent_reg)
|
|
return nullptr;
|
|
const svalue *summary_byte_offset
|
|
= summary_offset_reg->get_byte_offset ();
|
|
const svalue *caller_byte_offset
|
|
= convert_svalue_from_summary (summary_byte_offset);
|
|
if (!caller_byte_offset)
|
|
return nullptr;
|
|
return mgr->get_offset_region (caller_parent_reg,
|
|
summary_reg->get_type (),
|
|
caller_byte_offset);
|
|
}
|
|
break;
|
|
case RK_SIZED:
|
|
{
|
|
const sized_region *summary_sized_reg
|
|
= as_a <const sized_region *> (summary_reg);
|
|
const region *summary_parent_reg = summary_reg->get_parent_region ();
|
|
const region *caller_parent_reg
|
|
= convert_region_from_summary (summary_parent_reg);
|
|
if (!caller_parent_reg)
|
|
return nullptr;
|
|
const svalue *summary_byte_size
|
|
= summary_sized_reg->get_byte_size_sval (mgr);
|
|
const svalue *caller_byte_size
|
|
= convert_svalue_from_summary (summary_byte_size);
|
|
if (!caller_byte_size)
|
|
return nullptr;
|
|
return mgr->get_sized_region (caller_parent_reg,
|
|
summary_reg->get_type (),
|
|
caller_byte_size);
|
|
}
|
|
break;
|
|
case RK_CAST:
|
|
{
|
|
const region *summary_parent_reg = summary_reg->get_parent_region ();
|
|
const region *caller_parent_reg
|
|
= convert_region_from_summary (summary_parent_reg);
|
|
if (!caller_parent_reg)
|
|
return nullptr;
|
|
return mgr->get_cast_region (caller_parent_reg,
|
|
summary_reg->get_type ());
|
|
}
|
|
break;
|
|
case RK_HEAP_ALLOCATED:
|
|
{
|
|
/* If we have a heap-allocated region in the summary, then
|
|
it was allocated within the callee.
|
|
Create a new heap-allocated region to summarize this. */
|
|
auto_bitmap heap_regs_in_use;
|
|
get_caller_model ()->get_referenced_base_regions (heap_regs_in_use);
|
|
return mgr->get_or_create_region_for_heap_alloc (heap_regs_in_use);
|
|
}
|
|
break;
|
|
case RK_ALLOCA:
|
|
return nullptr;
|
|
case RK_BIT_RANGE:
|
|
{
|
|
const bit_range_region *summary_bit_range_reg
|
|
= as_a <const bit_range_region *> (summary_reg);
|
|
const region *summary_parent_reg = summary_reg->get_parent_region ();
|
|
const region *caller_parent_reg
|
|
= convert_region_from_summary (summary_parent_reg);
|
|
if (!caller_parent_reg)
|
|
return nullptr;
|
|
const bit_range &bits = summary_bit_range_reg->get_bits ();
|
|
return mgr->get_bit_range (caller_parent_reg,
|
|
summary_reg->get_type (),
|
|
bits);
|
|
}
|
|
break;
|
|
case RK_VAR_ARG:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
/* Try to convert SUMMARY_KEY in the summary to a corresponding binding key
|
|
in the caller.
|
|
|
|
Return nullptr if the conversion is not possible. */
|
|
|
|
const binding_key *
|
|
call_summary_replay::convert_key_from_summary (const binding_key *summary_key)
|
|
{
|
|
if (summary_key->concrete_p ())
|
|
return summary_key;
|
|
|
|
const symbolic_binding *symbolic_key = (const symbolic_binding *)summary_key;
|
|
const region *summary_reg = symbolic_key->get_region ();
|
|
const region *caller_reg = convert_region_from_summary (summary_reg);
|
|
if (!caller_reg)
|
|
return nullptr;
|
|
region_model_manager *mgr = get_manager ();
|
|
store_manager *store_mgr = mgr->get_store_manager ();
|
|
return store_mgr->get_symbolic_binding (caller_reg);
|
|
}
|
|
|
|
/* Record that SUMMARY_SVAL maps to CALLER_SVAL for this replay. */
|
|
|
|
void
|
|
call_summary_replay::add_svalue_mapping (const svalue *summary_sval,
|
|
const svalue *caller_sval)
|
|
{
|
|
gcc_assert (summary_sval);
|
|
// CALLER_SVAL can be nullptr
|
|
m_map_svalue_from_summary_to_caller.put (summary_sval, caller_sval);
|
|
}
|
|
|
|
/* Record that SUMMARY_REG maps to CALLER_REG for this replay. */
|
|
|
|
void
|
|
call_summary_replay::add_region_mapping (const region *summary_reg,
|
|
const region *caller_reg)
|
|
{
|
|
gcc_assert (summary_reg);
|
|
// CALLER_REG can be nullptr
|
|
m_map_region_from_summary_to_caller.put (summary_reg, caller_reg);
|
|
}
|
|
|
|
/* Dump a multiline representation of this object to PP. */
|
|
|
|
void
|
|
call_summary_replay::dump_to_pp (pretty_printer *pp, bool simple) const
|
|
{
|
|
pp_newline (pp);
|
|
pp_string (pp, "CALL DETAILS:");
|
|
pp_newline (pp);
|
|
m_cd.dump_to_pp (pp, simple);
|
|
|
|
pp_newline (pp);
|
|
pp_string (pp, "CALLEE SUMMARY:");
|
|
pp_newline (pp);
|
|
m_summary.dump_to_pp (m_ext_state, pp, simple);
|
|
|
|
/* Current state of caller (could be in mid-update). */
|
|
pp_newline (pp);
|
|
pp_string (pp, "CALLER:");
|
|
pp_newline (pp);
|
|
m_cd.get_model ()->dump_to_pp (pp, simple, true);
|
|
|
|
pp_newline (pp);
|
|
pp_string (pp, "REPLAY STATE:");
|
|
pp_newline (pp);
|
|
pp_string (pp, "svalue mappings from summary to caller:");
|
|
pp_newline (pp);
|
|
auto_vec <const svalue *> summary_svals;
|
|
for (auto kv : m_map_svalue_from_summary_to_caller)
|
|
summary_svals.safe_push (kv.first);
|
|
summary_svals.qsort (svalue::cmp_ptr_ptr);
|
|
for (auto summary_sval : summary_svals)
|
|
{
|
|
pp_string (pp, "sval in summary: ");
|
|
summary_sval->dump_to_pp (pp, simple);
|
|
pp_newline (pp);
|
|
|
|
const svalue *caller_sval
|
|
= *((const_cast<svalue_map_t &>
|
|
(m_map_svalue_from_summary_to_caller)).get (summary_sval));
|
|
pp_string (pp, " sval in caller: ");
|
|
caller_sval->dump_to_pp (pp, simple);
|
|
pp_newline (pp);
|
|
}
|
|
|
|
pp_newline (pp);
|
|
pp_string (pp, "region mappings from summary to caller:");
|
|
pp_newline (pp);
|
|
auto_vec <const region *> summary_regs;
|
|
for (auto kv : m_map_region_from_summary_to_caller)
|
|
summary_regs.safe_push (kv.first);
|
|
summary_regs.qsort (region::cmp_ptr_ptr);
|
|
for (auto summary_reg : summary_regs)
|
|
{
|
|
pp_string (pp, "reg in summary: ");
|
|
if (summary_reg)
|
|
summary_reg->dump_to_pp (pp, simple);
|
|
else
|
|
pp_string (pp, "(null)");
|
|
pp_newline (pp);
|
|
|
|
const region *caller_reg
|
|
= *((const_cast<region_map_t &>
|
|
(m_map_region_from_summary_to_caller)).get (summary_reg));
|
|
pp_string (pp, " reg in caller: ");
|
|
if (caller_reg)
|
|
caller_reg->dump_to_pp (pp, simple);
|
|
else
|
|
pp_string (pp, "(null)");
|
|
pp_newline (pp);
|
|
}
|
|
}
|
|
|
|
/* Dump a multiline representation of this object to FILE. */
|
|
|
|
void
|
|
call_summary_replay::dump (FILE *fp, bool simple) const
|
|
{
|
|
tree_dump_pretty_printer pp (fp);
|
|
dump_to_pp (&pp, simple);
|
|
}
|
|
|
|
/* Dump a multiline representation of this object to stderr. */
|
|
|
|
DEBUG_FUNCTION void
|
|
call_summary_replay::dump (bool simple) const
|
|
{
|
|
dump (stderr, simple);
|
|
}
|
|
|
|
} // namespace ana
|
|
|
|
#endif /* #if ENABLE_ANALYZER */
|