/* Callgraph summary data structure. Copyright (C) 2014-2019 Free Software Foundation, Inc. Contributed by Martin Liska 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 . */ #ifndef GCC_SYMBOL_SUMMARY_H #define GCC_SYMBOL_SUMMARY_H /* We want to pass just pointer types as argument for function_summary template class. */ template class function_summary { private: function_summary(); }; /* Function summary is a helper class that is used to associate a data structure related to a callgraph node. Typical usage can be seen in IPA passes which create a temporary pass-related structures. The summary class registers hooks that are triggered when a new node is inserted, duplicated and deleted. A user of a summary class can ovewrite virtual methods than are triggered by the summary if such hook is triggered. Apart from a callgraph node, the user is given a data structure tied to the node. The function summary class can work both with a heap-allocated memory and a memory gained by garbage collected memory. */ template class GTY((user)) function_summary { public: /* Default construction takes SYMTAB as an argument. */ function_summary (symbol_table *symtab, bool ggc = false); /* Destructor. */ virtual ~function_summary () { release (); } /* Destruction method that can be called for GGT purpose. */ void release (); /* Traverses all summarys with a function F called with ARG as argument. */ template void traverse (Arg a) const { m_map.traverse (a); } /* Basic implementation of insert operation. */ virtual void insert (cgraph_node *, T *) {} /* Basic implementation of removal operation. */ virtual void remove (cgraph_node *, T *) {} /* Basic implementation of duplication operation. */ virtual void duplicate (cgraph_node *, cgraph_node *, T *, T *) {} /* Allocates new data that are stored within map. */ T* allocate_new () { /* Call gcc_internal_because we do not want to call finalizer for a type T. We call dtor explicitly. */ return m_ggc ? new (ggc_internal_alloc (sizeof (T))) T () : new T () ; } /* Release an item that is stored within map. */ void release (T *item); /* Getter for summary callgraph node pointer. If a summary for a node does not exist it will be created. */ T* get_create (cgraph_node *node) { bool existed; T **v = &m_map.get_or_insert (node->get_uid (), &existed); if (!existed) *v = allocate_new (); return *v; } /* Getter for summary callgraph node pointer. */ T* get (cgraph_node *node) ATTRIBUTE_PURE { T **v = m_map.get (node->get_uid ()); return v == NULL ? NULL : *v; } /* Remove node from summary. */ void remove (cgraph_node *node) { int uid = node->get_uid (); T **v = m_map.get (uid); if (v) { m_map.remove (uid); release (*v); } } /* Return number of elements handled by data structure. */ size_t elements () { return m_map.elements (); } /* Return true if a summary for the given NODE already exists. */ bool exists (cgraph_node *node) { return m_map.get (node->get_uid ()) != NULL; } /* Enable insertion hook invocation. */ void enable_insertion_hook () { m_insertion_enabled = true; } /* Enable insertion hook invocation. */ void disable_insertion_hook () { m_insertion_enabled = false; } /* Symbol insertion hook that is registered to symbol table. */ static void symtab_insertion (cgraph_node *node, void *data); /* Symbol removal hook that is registered to symbol table. */ static void symtab_removal (cgraph_node *node, void *data); /* Symbol duplication hook that is registered to symbol table. */ static void symtab_duplication (cgraph_node *node, cgraph_node *node2, void *data); protected: /* Indication if we use ggc summary. */ bool m_ggc; private: typedef int_hash map_hash; /* Indicates if insertion hook is enabled. */ bool m_insertion_enabled; /* Indicates if the summary is released. */ bool m_released; /* Main summary store, where summary ID is used as key. */ hash_map m_map; /* Internal summary insertion hook pointer. */ cgraph_node_hook_list *m_symtab_insertion_hook; /* Internal summary removal hook pointer. */ cgraph_node_hook_list *m_symtab_removal_hook; /* Internal summary duplication hook pointer. */ cgraph_2node_hook_list *m_symtab_duplication_hook; /* Symbol table the summary is registered to. */ symbol_table *m_symtab; template friend void gt_ggc_mx (function_summary * const &); template friend void gt_pch_nx (function_summary * const &); template friend void gt_pch_nx (function_summary * const &, gt_pointer_operator, void *); }; template function_summary::function_summary (symbol_table *symtab, bool ggc): m_ggc (ggc), m_insertion_enabled (true), m_released (false), m_map (13, ggc), m_symtab (symtab) { m_symtab_insertion_hook = symtab->add_cgraph_insertion_hook (function_summary::symtab_insertion, this); m_symtab_removal_hook = symtab->add_cgraph_removal_hook (function_summary::symtab_removal, this); m_symtab_duplication_hook = symtab->add_cgraph_duplication_hook (function_summary::symtab_duplication, this); } template void function_summary::release () { if (m_released) return; m_symtab->remove_cgraph_insertion_hook (m_symtab_insertion_hook); m_symtab->remove_cgraph_removal_hook (m_symtab_removal_hook); m_symtab->remove_cgraph_duplication_hook (m_symtab_duplication_hook); /* Release all summaries. */ typedef typename hash_map ::iterator map_iterator; for (map_iterator it = m_map.begin (); it != m_map.end (); ++it) release ((*it).second); m_released = true; } template void function_summary::release (T *item) { if (m_ggc) { item->~T (); ggc_free (item); } else delete item; } template void function_summary::symtab_insertion (cgraph_node *node, void *data) { gcc_checking_assert (node->get_uid ()); function_summary *summary = (function_summary *) (data); if (summary->m_insertion_enabled) summary->insert (node, summary->get_create (node)); } template void function_summary::symtab_removal (cgraph_node *node, void *data) { gcc_checking_assert (node->get_uid ()); function_summary *summary = (function_summary *) (data); int uid = node->get_uid (); T **v = summary->m_map.get (uid); if (v) { summary->remove (node, *v); if (!summary->m_ggc) delete (*v); summary->m_map.remove (uid); } } template void function_summary::symtab_duplication (cgraph_node *node, cgraph_node *node2, void *data) { function_summary *summary = (function_summary *) (data); T *v = summary->get (node); if (v) { /* This load is necessary, because we insert a new value! */ T *duplicate = summary->allocate_new (); summary->m_map.put (node2->get_uid (), duplicate); summary->duplicate (node, node2, v, duplicate); } } template void gt_ggc_mx(function_summary* const &summary) { gcc_checking_assert (summary->m_ggc); gt_ggc_mx (&summary->m_map); } template void gt_pch_nx(function_summary* const &summary) { gcc_checking_assert (summary->m_ggc); gt_pch_nx (&summary->m_map); } template void gt_pch_nx(function_summary* const& summary, gt_pointer_operator op, void *cookie) { gcc_checking_assert (summary->m_ggc); gt_pch_nx (&summary->m_map, op, cookie); } /* An impossible class templated by non-pointers so, which makes sure that only summaries gathering pointers can be created. */ template class call_summary { private: call_summary(); }; /* Class to store auxiliary information about call graph edges. */ template class GTY((user)) call_summary { public: /* Default construction takes SYMTAB as an argument. */ call_summary (symbol_table *symtab, bool ggc = false): m_ggc (ggc), m_initialize_when_cloning (false), m_map (13, ggc), m_released (false), m_symtab (symtab) { m_symtab_removal_hook = symtab->add_edge_removal_hook (call_summary::symtab_removal, this); m_symtab_duplication_hook = symtab->add_edge_duplication_hook (call_summary::symtab_duplication, this); } /* Destructor. */ virtual ~call_summary () { release (); } /* Destruction method that can be called for GGT purpose. */ void release (); /* Traverses all summarys with a function F called with ARG as argument. */ template void traverse (Arg a) const { m_map.traverse (a); } /* Basic implementation of removal operation. */ virtual void remove (cgraph_edge *, T *) {} /* Basic implementation of duplication operation. */ virtual void duplicate (cgraph_edge *, cgraph_edge *, T *, T *) {} /* Allocates new data that are stored within map. */ T* allocate_new () { /* Call gcc_internal_because we do not want to call finalizer for a type T. We call dtor explicitly. */ return m_ggc ? new (ggc_internal_alloc (sizeof (T))) T () : new T () ; } /* Release an item that is stored within map. */ void release (T *item); /* Getter for summary callgraph edge pointer. If a summary for an edge does not exist, it will be created. */ T* get_create (cgraph_edge *edge) { bool existed; T **v = &m_map.get_or_insert (edge->get_uid (), &existed); if (!existed) *v = allocate_new (); return *v; } /* Getter for summary callgraph edge pointer. */ T* get (cgraph_edge *edge) ATTRIBUTE_PURE { T **v = m_map.get (edge->get_uid ()); return v == NULL ? NULL : *v; } /* Remove edge from summary. */ void remove (cgraph_edge *edge) { int uid = edge->get_uid (); T **v = m_map.get (uid); if (v) { m_map.remove (uid); release (*v); } } /* Return number of elements handled by data structure. */ size_t elements () { return m_map.elements (); } /* Return true if a summary for the given EDGE already exists. */ bool exists (cgraph_edge *edge) { return m_map.get (edge->get_uid ()) != NULL; } /* Symbol removal hook that is registered to symbol table. */ static void symtab_removal (cgraph_edge *edge, void *data); /* Symbol duplication hook that is registered to symbol table. */ static void symtab_duplication (cgraph_edge *edge1, cgraph_edge *edge2, void *data); protected: /* Indication if we use ggc summary. */ bool m_ggc; /* Initialize summary for an edge that is cloned. */ bool m_initialize_when_cloning; private: typedef int_hash map_hash; /* Main summary store, where summary ID is used as key. */ hash_map m_map; /* Internal summary removal hook pointer. */ cgraph_edge_hook_list *m_symtab_removal_hook; /* Internal summary duplication hook pointer. */ cgraph_2edge_hook_list *m_symtab_duplication_hook; /* Indicates if the summary is released. */ bool m_released; /* Symbol table the summary is registered to. */ symbol_table *m_symtab; template friend void gt_ggc_mx (call_summary * const &); template friend void gt_pch_nx (call_summary * const &); template friend void gt_pch_nx (call_summary * const &, gt_pointer_operator, void *); }; template void call_summary::release () { if (m_released) return; m_symtab->remove_edge_removal_hook (m_symtab_removal_hook); m_symtab->remove_edge_duplication_hook (m_symtab_duplication_hook); /* Release all summaries. */ typedef typename hash_map ::iterator map_iterator; for (map_iterator it = m_map.begin (); it != m_map.end (); ++it) release ((*it).second); m_released = true; } template void call_summary::release (T *item) { if (m_ggc) { item->~T (); ggc_free (item); } else delete item; } template void call_summary::symtab_removal (cgraph_edge *edge, void *data) { call_summary *summary = (call_summary *) (data); int h_uid = edge->get_uid (); T **v = summary->m_map.get (h_uid); if (v) { summary->remove (edge, *v); summary->release (*v); summary->m_map.remove (h_uid); } } template void call_summary::symtab_duplication (cgraph_edge *edge1, cgraph_edge *edge2, void *data) { call_summary *summary = (call_summary *) (data); T *edge1_summary = NULL; if (summary->m_initialize_when_cloning) edge1_summary = summary->get_create (edge1); else { T **v = summary->m_map.get (edge1->get_uid ()); if (v) { /* This load is necessary, because we insert a new value! */ edge1_summary = *v; } } if (edge1_summary) { T *duplicate = summary->allocate_new (); summary->m_map.put (edge2->get_uid (), duplicate); summary->duplicate (edge1, edge2, edge1_summary, duplicate); } } template void gt_ggc_mx(call_summary* const &summary) { gcc_checking_assert (summary->m_ggc); gt_ggc_mx (&summary->m_map); } template void gt_pch_nx(call_summary* const &summary) { gcc_checking_assert (summary->m_ggc); gt_pch_nx (&summary->m_map); } template void gt_pch_nx(call_summary* const& summary, gt_pointer_operator op, void *cookie) { gcc_checking_assert (summary->m_ggc); gt_pch_nx (&summary->m_map, op, cookie); } #endif /* GCC_SYMBOL_SUMMARY_H */