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;

Reply via email to