1  /* Classes for purging state at function_points.
       2     Copyright (C) 2019-2023 Free Software Foundation, Inc.
       3     Contributed by David Malcolm <dmalcolm@redhat.com>.
       4  
       5  This file is part of GCC.
       6  
       7  GCC is free software; you can redistribute it and/or modify it
       8  under the terms of the GNU General Public License as published by
       9  the Free Software Foundation; either version 3, or (at your option)
      10  any later version.
      11  
      12  GCC is distributed in the hope that it will be useful, but
      13  WITHOUT ANY WARRANTY; without even the implied warranty of
      14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15  General Public License for more details.
      16  
      17  You should have received a copy of the GNU General Public License
      18  along with GCC; see the file COPYING3.  If not see
      19  <http://www.gnu.org/licenses/>.  */
      20  
      21  #ifndef GCC_ANALYZER_STATE_PURGE_H
      22  #define GCC_ANALYZER_STATE_PURGE_H
      23  
      24  /* Hash traits for function_point.  */
      25  
      26  template <> struct default_hash_traits<function_point>
      27  : public pod_hash_traits<function_point>
      28  {
      29    static const bool empty_zero_p = false;
      30  };
      31  
      32  template <>
      33  inline hashval_t
      34  pod_hash_traits<function_point>::hash (value_type v)
      35  {
      36    return v.hash ();
      37  }
      38  
      39  template <>
      40  inline bool
      41  pod_hash_traits<function_point>::equal (const value_type &existing,
      42                                   const value_type &candidate)
      43  {
      44    return existing == candidate;
      45  }
      46  template <>
      47  inline void
      48  pod_hash_traits<function_point>::mark_deleted (value_type &v)
      49  {
      50    v = function_point::deleted ();
      51  }
      52  template <>
      53  inline void
      54  pod_hash_traits<function_point>::mark_empty (value_type &v)
      55  {
      56    v = function_point::empty ();
      57  }
      58  template <>
      59  inline bool
      60  pod_hash_traits<function_point>::is_deleted (value_type v)
      61  {
      62    return v.get_kind () == PK_DELETED;
      63  }
      64  template <>
      65  inline bool
      66  pod_hash_traits<function_point>::is_empty (value_type v)
      67  {
      68    return v.get_kind () == PK_EMPTY;
      69  }
      70  
      71  namespace ana {
      72  
      73  /* The result of analyzing which decls and SSA names can be purged from state at
      74     different points in the program, so that we can simplify program_state
      75     objects, in the hope of reducing state-blowup.  */
      76  
      77  class state_purge_map : public log_user
      78  {
      79  public:
      80    typedef ordered_hash_map<tree, state_purge_per_ssa_name *> ssa_map_t;
      81    typedef ssa_map_t::iterator ssa_iterator;
      82  
      83    typedef ordered_hash_map<tree, state_purge_per_decl *> decl_map_t;
      84    typedef decl_map_t::iterator decl_iterator;
      85  
      86    state_purge_map (const supergraph &sg,
      87  		   region_model_manager *mgr,
      88  		   logger *logger);
      89    ~state_purge_map ();
      90  
      91    const state_purge_per_ssa_name &get_data_for_ssa_name (tree name) const
      92    {
      93      gcc_assert (TREE_CODE (name) == SSA_NAME);
      94      if (tree var = SSA_NAME_VAR (name))
      95        if (TREE_CODE (var) == VAR_DECL)
      96  	gcc_assert (!VAR_DECL_IS_VIRTUAL_OPERAND (var));
      97  
      98      state_purge_per_ssa_name **slot
      99        = const_cast <ssa_map_t&> (m_ssa_map).get (name);
     100      return **slot;
     101    }
     102  
     103    const state_purge_per_decl *get_any_data_for_decl (tree decl) const
     104    {
     105      gcc_assert (TREE_CODE (decl) == VAR_DECL
     106  		|| TREE_CODE (decl) == PARM_DECL
     107  		|| TREE_CODE (decl) == RESULT_DECL);
     108      if (state_purge_per_decl **slot
     109  	= const_cast <decl_map_t&> (m_decl_map).get (decl))
     110        return *slot;
     111      else
     112        return NULL;
     113    }
     114  
     115    state_purge_per_decl &get_or_create_data_for_decl (function *fun, tree decl);
     116  
     117    const supergraph &get_sg () const { return m_sg; }
     118  
     119    ssa_iterator begin_ssas () const { return m_ssa_map.begin (); }
     120    ssa_iterator end_ssas () const { return m_ssa_map.end (); }
     121  
     122    decl_iterator begin_decls () const { return m_decl_map.begin (); }
     123    decl_iterator end_decls () const { return m_decl_map.end (); }
     124  
     125  private:
     126    DISABLE_COPY_AND_ASSIGN (state_purge_map);
     127  
     128    const supergraph &m_sg;
     129    ssa_map_t m_ssa_map;
     130    decl_map_t m_decl_map;
     131  };
     132  
     133    /* Base class for state_purge_per_ssa_name and state_purge_per_decl.  */
     134  
     135  class state_purge_per_tree
     136  {
     137  public:
     138    function *get_function () const { return m_fun; }
     139    tree get_fndecl () const { return m_fun->decl; }
     140  
     141  protected:
     142    typedef hash_set<function_point> point_set_t;
     143  
     144    state_purge_per_tree (function *fun)
     145    : m_fun (fun)
     146    {
     147    }
     148  
     149  private:
     150    function *m_fun;
     151  };
     152  
     153  /* The part of a state_purge_map relating to a specific SSA name.
     154  
     155     The result of analyzing a given SSA name, recording which
     156     function_points need to retain state information about it to handle
     157     their successor states, so that we can simplify program_state objects,
     158     in the hope of reducing state-blowup.  */
     159  
     160  class state_purge_per_ssa_name : public state_purge_per_tree
     161  {
     162  public:
     163    state_purge_per_ssa_name (const state_purge_map &map,
     164  			    tree name,
     165  			    function *fun);
     166  
     167    bool needed_at_point_p (const function_point &point) const;
     168  
     169  private:
     170    static function_point before_use_stmt (const state_purge_map &map,
     171  					 const gimple *use_stmt);
     172  
     173    void add_to_worklist (const function_point &point,
     174  			auto_vec<function_point> *worklist,
     175  			logger *logger);
     176  
     177    void process_point (const function_point &point,
     178  		      auto_vec<function_point> *worklist,
     179  		      const state_purge_map &map);
     180  
     181    point_set_t m_points_needing_name;
     182    tree m_name;
     183  };
     184  
     185  /* The part of a state_purge_map relating to a specific decl.
     186  
     187     Analogous to state_purge_per_ssa_name, but for local decls.
     188  
     189     This is more involved than the SSA name case, because we also need
     190     to handle pointers and components.  */
     191  
     192  class state_purge_per_decl : public state_purge_per_tree
     193  {
     194  public:
     195    state_purge_per_decl (const state_purge_map &map,
     196  			tree decl,
     197  			function *fun);
     198  
     199    bool needed_at_point_p (const function_point &point) const;
     200  
     201    void add_needed_at (const function_point &point);
     202    void add_pointed_to_at (const function_point &point);
     203    void process_worklists (const state_purge_map &map,
     204  			  region_model_manager *mgr);
     205  
     206  private:
     207    static function_point before_use_stmt (const state_purge_map &map,
     208  					 const gimple *use_stmt);
     209  
     210    void add_to_worklist (const function_point &point,
     211  			auto_vec<function_point> *worklist,
     212  			point_set_t *seen,
     213  			logger *logger);
     214  
     215    void process_point_backwards (const function_point &point,
     216  				auto_vec<function_point> *worklist,
     217  				point_set_t *seen,
     218  				const state_purge_map &map,
     219  				const region_model &model);
     220    void process_point_forwards (const function_point &point,
     221  			       auto_vec<function_point> *worklist,
     222  			       point_set_t *seen,
     223  			       const state_purge_map &map);
     224  
     225    point_set_t m_points_needing_decl;
     226    point_set_t m_points_taking_address;
     227    tree m_decl;
     228  };
     229  
     230  /* Subclass of dot_annotator for use by -fdump-analyzer-state-purge.
     231     Annotate the .dot output with state-purge information.  */
     232  
     233  class state_purge_annotator : public dot_annotator
     234  {
     235  public:
     236    state_purge_annotator (const state_purge_map *map) : m_map (map) {}
     237  
     238    bool add_node_annotations (graphviz_out *gv, const supernode &n, bool)
     239      const final override;
     240  
     241    void add_stmt_annotations (graphviz_out *gv, const gimple *stmt,
     242  			     bool within_row)
     243      const final override;
     244  
     245  private:
     246    void print_needed (graphviz_out *gv,
     247  		     const function_point &point,
     248  		     bool within_table) const;
     249  
     250    const state_purge_map *m_map;
     251  };
     252  
     253  } // namespace ana
     254  
     255  #endif /* GCC_ANALYZER_STATE_PURGE_H */