https://gcc.gnu.org/g:75321514b9193f8f2b54623e903238ea62c50f8f
commit r17-4902-g75321514b9193f8f2b54623e903238ea62c50f8f Author: linden <[email protected]> Date: Sun Sep 6 15:58:20 2026 +1200 analyzer: avoid deep-copying program_state when creating an exploded_node get_or_create_node deep-copied program_state twice when creating a node, even though the source was discarded right after; program_state owns its region_model via pointer, so each copy cloned it. Add move support to program_state, point_and_state and exploded_node, cutting a new node's cost from three deep-copies to one. The pruned_state copy used as the hash-map key stays, since the merge loop needs it stable while mutating the key. gcc/analyzer/ChangeLog: * program-state.h (program_state::operator=): Declare move-assignment overload. * program-state.cc (program_state::operator=): Implement it: steal the region_model and sm_state_map pointers, leaving the source empty. * exploded-graph.h (point_and_state::point_and_state): Add a move ctor; explicitly default the copy ctor. (point_and_state::set_state): Add a program_state&& overload. (exploded_node::exploded_node): Declare a (point_and_state&&, int) overload. * engine.cc (exploded_node::exploded_node): Implement the (point_and_state&&, int) overload; check m_ps rather than the argument in the existing overload's assertion. (exploded_graph::get_or_create_node): Move the merged state into point_and_state::set_state; move the point_and_state into the new exploded_node; log node->get_state () instead of the moved-from ps. Signed-off-by: linden <[email protected]> Diff: --- gcc/analyzer/engine.cc | 15 +++++++++++---- gcc/analyzer/exploded-graph.h | 16 ++++++++++++++++ gcc/analyzer/program-state.cc | 27 +++++++++++++++++++++++++++ gcc/analyzer/program-state.h | 1 + 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 59cdcc51e6c9..830164dac778 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -1030,7 +1030,14 @@ exploded_node::exploded_node (const point_and_state &ps, int index) : m_ps (ps), m_status (status::worklist), m_index (index) { - gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ()); + gcc_checking_assert (m_ps.get_state ().m_region_model->canonicalized_p ()); +} + +exploded_node::exploded_node (point_and_state &&ps, + int index) +: m_ps (std::move (ps)), m_status (status::worklist), m_index (index) +{ + gcc_checking_assert (m_ps.get_state ().m_region_model->canonicalized_p ()); } /* For use by dump_dot, get a value for the .dot "fillcolor" attribute. @@ -2571,7 +2578,7 @@ exploded_graph::get_or_create_node (const program_point &point, Whether we get one or not, merged_state's value_ids have no relationship to those of the input state, and thus to those of CHANGE, so we must purge any svalue_ids from *CHANGE. */ - ps.set_state (merged_state); + ps.set_state (std::move (merged_state)); if (exploded_node **slot = m_point_and_state_to_node.get (&ps)) { @@ -2612,7 +2619,7 @@ exploded_graph::get_or_create_node (const program_point &point, ps.validate (m_ext_state); /* An exploded_node for "ps" doesn't already exist; create one. */ - exploded_node *node = new exploded_node (ps, m_nodes.length ()); + exploded_node *node = new exploded_node (std::move (ps), m_nodes.length ()); add_node (node); m_point_and_state_to_node.put (node->get_ps_key (), node); @@ -2634,7 +2641,7 @@ exploded_graph::get_or_create_node (const program_point &point, logger->end_log_line (); logger->start_log_line (); pp_string (pp, "state: "); - ps.get_state ().dump_to_pp (m_ext_state, true, false, pp); + node->get_state ().dump_to_pp (m_ext_state, true, false, pp); logger->end_log_line (); } diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h index 7a0060521db8..c33bd476c9d0 100644 --- a/gcc/analyzer/exploded-graph.h +++ b/gcc/analyzer/exploded-graph.h @@ -150,6 +150,15 @@ public: gcc_assert (state.m_valid); } + point_and_state (const point_and_state &other) = default; + + point_and_state (point_and_state &&other) + : m_point (other.m_point), + m_state (std::move (other.m_state)), + m_hash (other.m_hash) + { + } + hashval_t hash () const { return m_hash; @@ -168,6 +177,12 @@ public: m_hash = m_point.hash () ^ m_state.hash (); } + void set_state (program_state &&state) + { + m_state = std::move (state); + m_hash = m_point.hash () ^ m_state.hash (); + } + void validate (const extrinsic_state &ext_state) const; private: @@ -231,6 +246,7 @@ class exploded_node : public dnode<eg_traits> static const char * status_to_str (enum status s); exploded_node (const point_and_state &ps, int index); + exploded_node (point_and_state &&ps, int index); hashval_t hash () const { return m_ps.hash (); } diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index 5ebaafb2661f..1c1c58784f42 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -1021,6 +1021,33 @@ program_state::program_state (program_state &&other) m_valid = other.m_valid; } +/* Move assignment for program_state. */ + +program_state& +program_state::operator= (program_state &&other) +{ + if (this == &other) + return *this; + + delete m_region_model; + m_region_model = other.m_region_model; + other.m_region_model = nullptr; + + int i; + sm_state_map *smap; + FOR_EACH_VEC_ELT (m_checker_states, i, smap) + delete smap; + m_checker_states.truncate (0); + gcc_assert (m_checker_states.space (other.m_checker_states.length ())); + FOR_EACH_VEC_ELT (other.m_checker_states, i, smap) + m_checker_states.quick_push (smap); + other.m_checker_states.truncate (0); + + m_valid = other.m_valid; + + return *this; +} + /* program_state's dtor. */ program_state::~program_state () diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index 982075003163..e098d84a4552 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -227,6 +227,7 @@ public: program_state (const program_state &other); program_state& operator= (const program_state &other); program_state (program_state &&other); + program_state& operator= (program_state &&other); ~program_state (); hashval_t hash () const;
