代码拉取完成,页面将自动刷新
同步操作将从 ChunyangWang/gcc 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
From b73462757734c62f64e7a4379340679ec6f19669 Mon Sep 17 00:00:00 2001
From: Diachkov Ilia <diachkov.ilia1@huawei-partners.com>
Date: Tue, 27 Feb 2024 07:28:12 +0800
Subject: [PATCH 06/18] Port icp patch to GCC 12
---
gcc/common.opt | 8 +
gcc/dbgcnt.def | 1 +
gcc/ipa-devirt.cc | 1855 +++++++++++++++++++++++++++++++++++
gcc/passes.def | 1 +
gcc/testsuite/gcc.dg/icp1.c | 40 +
gcc/testsuite/gcc.dg/icp2.c | 38 +
gcc/testsuite/gcc.dg/icp3.c | 52 +
gcc/testsuite/gcc.dg/icp4.c | 55 ++
gcc/testsuite/gcc.dg/icp5.c | 66 ++
gcc/testsuite/gcc.dg/icp6.c | 66 ++
gcc/testsuite/gcc.dg/icp7.c | 48 +
gcc/timevar.def | 1 +
gcc/tree-pass.h | 1 +
13 files changed, 2232 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/icp1.c
create mode 100644 gcc/testsuite/gcc.dg/icp2.c
create mode 100644 gcc/testsuite/gcc.dg/icp3.c
create mode 100644 gcc/testsuite/gcc.dg/icp4.c
create mode 100644 gcc/testsuite/gcc.dg/icp5.c
create mode 100644 gcc/testsuite/gcc.dg/icp6.c
create mode 100644 gcc/testsuite/gcc.dg/icp7.c
diff --git a/gcc/common.opt b/gcc/common.opt
index 39c90604e..16aadccf6 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1316,6 +1316,14 @@ fdevirtualize
Common Var(flag_devirtualize) Optimization
Try to convert virtual calls to direct ones.
+ficp
+Common Var(flag_icp) Optimization Init(0)
+Try to promote indirect calls to direct ones.
+
+ficp-speculatively
+Common Var(flag_icp_speculatively) Optimization
+Promote indirect calls speculatively.
+
fdiagnostics-show-location=
Common Joined RejectNegative Enum(diagnostic_prefixing_rule)
-fdiagnostics-show-location=[once|every-line] How often to emit source location at the beginning of line-wrapped diagnostics.
diff --git a/gcc/dbgcnt.def b/gcc/dbgcnt.def
index 3aa18cd0c..a00bbc31b 100644
--- a/gcc/dbgcnt.def
+++ b/gcc/dbgcnt.def
@@ -170,6 +170,7 @@ DEBUG_COUNTER (graphite_scop)
DEBUG_COUNTER (hoist)
DEBUG_COUNTER (hoist_insn)
DEBUG_COUNTER (ia64_sched2)
+DEBUG_COUNTER (icp)
DEBUG_COUNTER (if_after_combine)
DEBUG_COUNTER (if_after_reload)
DEBUG_COUNTER (if_conversion)
diff --git a/gcc/ipa-devirt.cc b/gcc/ipa-devirt.cc
index 74fe65608..383839189 100644
--- a/gcc/ipa-devirt.cc
+++ b/gcc/ipa-devirt.cc
@@ -103,9 +103,14 @@ along with GCC; see the file COPYING3. If not see
indirect polymorphic edge all possible polymorphic call targets of the call.
pass_ipa_devirt performs simple speculative devirtualization.
+ pass_ipa_icp performs simple indirect call promotion.
*/
#include "config.h"
+#define INCLUDE_ALGORITHM
+#define INCLUDE_SET
+#define INCLUDE_MAP
+#define INCLUDE_LIST
#include "system.h"
#include "coretypes.h"
#include "backend.h"
@@ -127,6 +132,7 @@ along with GCC; see the file COPYING3. If not see
#include "ipa-fnsummary.h"
#include "demangle.h"
#include "dbgcnt.h"
+#include "gimple-iterator.h"
#include "gimple-pretty-print.h"
#include "intl.h"
#include "stringpool.h"
@@ -4401,5 +4407,1854 @@ make_pass_ipa_odr (gcc::context *ctxt)
return new pass_ipa_odr (ctxt);
}
+/* Function signature map used to look up function decl which corresponds to
+ the given function type. */
+typedef std::set<unsigned> type_set;
+typedef std::set<tree> decl_set;
+typedef std::map<unsigned, type_set*> type_alias_map;
+typedef std::map<unsigned, decl_set*> type_decl_map;
+typedef std::map<unsigned, tree> uid_to_type_map;
+typedef std::map<tree, tree> type_map;
+
+static bool has_address_taken_functions_with_varargs = false;
+static type_set *unsafe_types = NULL;
+static type_alias_map *fta_map = NULL;
+static type_alias_map *ta_map = NULL;
+static type_map *ctype_map = NULL;
+static type_alias_map *cbase_to_ptype = NULL;
+static type_decl_map *fs_map = NULL;
+static uid_to_type_map *type_uid_map = NULL;
+
+static void
+print_type_set(unsigned ftype_uid, type_alias_map *map)
+{
+ if (!map->count (ftype_uid))
+ return;
+ type_set* s = (*map)[ftype_uid];
+ for (type_set::const_iterator it = s->begin (); it != s->end (); it++)
+ fprintf (dump_file, it == s->begin () ? "%d" : ", %d", *it);
+}
+
+static void
+dump_type_with_uid (const char *msg, tree type, dump_flags_t flags = TDF_NONE)
+{
+ fprintf (dump_file, msg);
+ print_generic_expr (dump_file, type, flags);
+ fprintf (dump_file, " (%d)\n", TYPE_UID (type));
+}
+
+/* Walk aggregate type and collect types of scalar elements. */
+
+static void
+collect_scalar_types (tree tp, std::list<tree> &types)
+{
+ /* TODO: take into account different field offsets.
+ Also support array casts. */
+ if (tp && dump_file && (dump_flags & TDF_DETAILS))
+ dump_type_with_uid ("Walk var's type: ", tp, TDF_UID);
+ if (RECORD_OR_UNION_TYPE_P (tp))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Record's fields {\n");
+ for (tree field = TYPE_FIELDS (tp); field;
+ field = DECL_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+ collect_scalar_types (TREE_TYPE (field), types);
+ }
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "}\n");
+ return;
+ }
+ if (TREE_CODE (tp) == ARRAY_TYPE)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Array's innermost type:\n");
+ /* Take the innermost component type. */
+ tree elt;
+ for (elt = TREE_TYPE (tp); TREE_CODE (elt) == ARRAY_TYPE;
+ elt = TREE_TYPE (elt))
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ print_generic_expr (dump_file, elt);
+ collect_scalar_types (elt, types);
+ return;
+ }
+ types.push_back (tp);
+}
+
+static void maybe_register_aliases (tree type1, tree type2);
+
+/* Walk type lists and maybe register type aliases. */
+
+static void
+compare_type_lists (std::list<tree> tlist1, std::list<tree> tlist2)
+{
+ for (std::list<tree>::iterator ti1 = tlist1.begin (), ti2 = tlist2.begin ();
+ ti1 != tlist1.end (); ++ti1, ++ti2)
+ {
+ /* TODO: correct the analysis results if lists have different length. */
+ if (ti2 == tlist2.end ())
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Type lists with different length!\n");
+ break;
+ }
+ maybe_register_aliases (*ti1, *ti2);
+ }
+}
+
+/* For two given types collect scalar element types and
+ compare the result lists to find type aliases. */
+
+static void
+collect_scalar_types_and_find_aliases (tree t1, tree t2)
+{
+ std::list<tree> tlist1;
+ std::list<tree> tlist2;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "First type list: ");
+ collect_scalar_types (t1, tlist1);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Second type list: ");
+ collect_scalar_types (t2, tlist2);
+ compare_type_lists (tlist1, tlist2);
+}
+
+/* Dump type with the corresponding set from the map. */
+
+static void
+dump_type_uid_with_set (const char *msg, tree type, type_alias_map *map,
+ bool dump_type = true, bool with_newline = true)
+{
+ fprintf (dump_file, msg, TYPE_UID (type));
+ if (dump_type)
+ print_generic_expr (dump_file, type);
+ fprintf (dump_file, " (");
+ print_type_set (TYPE_UID (type), map);
+ fprintf (dump_file, ")");
+ fprintf (dump_file, with_newline ? "\n" : " ");
+}
+
+static void
+dump_two_types_uids_with_set (const char *msg, unsigned t1_uid,
+ unsigned t2_uid, type_alias_map *map)
+{
+ fprintf (dump_file, msg, t1_uid, t2_uid);
+ fprintf (dump_file, " (");
+ print_type_set (t1_uid, map);
+ fprintf (dump_file, ")\n");
+}
+
+/* Register type aliases in the map. Return true if new alias
+ is registered. */
+
+static bool
+register_ailas_type (tree type, tree alias_type, type_alias_map *map,
+ bool only_merge = false)
+{
+ /* TODO: maybe support the case with one missed type. */
+ if (!type || !alias_type)
+ return false;
+ unsigned type_uid = TYPE_UID (type);
+ unsigned alias_type_uid = TYPE_UID (alias_type);
+ if (type_uid_map->count (type_uid) == 0)
+ (*type_uid_map)[type_uid] = type;
+ if (type_uid_map->count (alias_type_uid) == 0)
+ (*type_uid_map)[alias_type_uid] = alias_type;
+
+ if (map->count (type_uid) == 0 && map->count (alias_type_uid) == 0)
+ {
+ (*map)[type_uid] = new type_set ();
+ (*map)[alias_type_uid] = (*map)[type_uid];
+ }
+ else if (map->count (type_uid) == 0)
+ (*map)[type_uid] = (*map)[alias_type_uid];
+ else if (map->count (alias_type_uid) == 0)
+ (*map)[alias_type_uid] = (*map)[type_uid];
+ else if (map->count (type_uid) && map->count (alias_type_uid))
+ {
+ if ((*map)[type_uid] == (*map)[alias_type_uid])
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_two_types_uids_with_set ("Types (%d) and (%d) are already in",
+ type_uid, alias_type_uid, map);
+ return false;
+ }
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ dump_type_uid_with_set ("T1 (%d) in set", type, map, false, true);
+ dump_type_uid_with_set ("T2 (%d) in set", alias_type, map,
+ false, true);
+ }
+ (*map)[type_uid]->insert ((*map)[alias_type_uid]->begin (),
+ (*map)[alias_type_uid]->end ());
+ type_set *type_set = (*map)[alias_type_uid];
+ for (type_set::const_iterator it1 = type_set->begin ();
+ it1 != type_set->end (); ++it1)
+ (*map)[*it1] = (*map)[type_uid];
+ delete type_set;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "MERGE: ");
+ }
+ if (!only_merge)
+ {
+ (*map)[type_uid]->insert (alias_type_uid);
+ (*map)[type_uid]->insert (type_uid);
+ }
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_two_types_uids_with_set ("Insert types (%d) and (%d) into set",
+ type_uid, alias_type_uid, map);
+ return true;
+}
+
+static void
+dump_two_types_with_uids (const char *msg, tree t1, tree t2)
+{
+ fprintf (dump_file, msg);
+ print_generic_expr (dump_file, t1, TDF_UID);
+ fprintf (dump_file, " (%d), ", TYPE_UID (t1));
+ print_generic_expr (dump_file, t2, TDF_UID);
+ fprintf (dump_file, " (%d)\n", TYPE_UID (t2));
+}
+
+static void
+analyze_pointees (tree type1, tree type2)
+{
+ gcc_assert (POINTER_TYPE_P (type1) && POINTER_TYPE_P (type2));
+ tree base1 = TREE_TYPE (type1);
+ tree base2 = TREE_TYPE (type2);
+ /* TODO: maybe analyze void pointers. */
+ if (VOID_TYPE_P(base1) || VOID_TYPE_P(base2))
+ return;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_two_types_with_uids ("Walk pointee types: ", base1, base2);
+ collect_scalar_types_and_find_aliases (base1, base2);
+}
+
+static void
+map_canonical_base_to_pointer (tree type, tree to_insert)
+{
+ type = TYPE_MAIN_VARIANT (type);
+ tree base_type = TREE_TYPE (type);
+ tree cbase_type = TYPE_CANONICAL (base_type);
+ if (!cbase_type)
+ return;
+ unsigned cbase_type_uid = TYPE_UID (cbase_type);
+ if (type_uid_map->count (cbase_type_uid) == 0)
+ (*type_uid_map)[cbase_type_uid] = cbase_type;
+
+ if (cbase_to_ptype->count (cbase_type_uid) == 0)
+ {
+ (*cbase_to_ptype)[cbase_type_uid] = new type_set ();
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "New map cb-to-p=(%d): ", cbase_type_uid);
+ }
+ else if (!(*cbase_to_ptype)[cbase_type_uid]->count (TYPE_UID (to_insert)))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Found map cb-to-p=(%d): ", cbase_type_uid);
+ }
+ else
+ return;
+ /* Add all variants of 'to_insert' type. */
+ for (tree t = to_insert; t; t = TYPE_NEXT_VARIANT (t))
+ {
+ unsigned t_uid = TYPE_UID (t);
+ if (!(*cbase_to_ptype)[cbase_type_uid]->count (t_uid))
+ {
+ (*cbase_to_ptype)[cbase_type_uid]->insert (t_uid);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "(%d) ", t_uid);
+ }
+ if (type_uid_map->count (t_uid) == 0)
+ (*type_uid_map)[t_uid] = t;
+ }
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\n");
+}
+
+/* Analyse two types and maybe register them as aliases. Also collect
+ unsafe function types and map canonical base types to corresponding
+ pointer types. */
+
+static void
+maybe_register_aliases (tree type1, tree type2)
+{
+ if (type1 && POINTER_TYPE_P (type1) && !FUNCTION_POINTER_TYPE_P (type1))
+ map_canonical_base_to_pointer (type1, type1);
+ if (type2 && POINTER_TYPE_P (type2) && !FUNCTION_POINTER_TYPE_P (type2))
+ map_canonical_base_to_pointer (type2, type2);
+
+ if (type1 == type2 || !type1 || !type2)
+ return;
+
+ if (POINTER_TYPE_P (type1) && POINTER_TYPE_P (type2))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_two_types_with_uids ("Pointer types: ", type1, type2);
+ if (register_ailas_type (type1, type2, ta_map))
+ analyze_pointees (type1, type2);
+ }
+ /* If function and non-function type pointers alias,
+ the function type is unsafe. */
+ if (FUNCTION_POINTER_TYPE_P (type1) && !FUNCTION_POINTER_TYPE_P (type2))
+ unsafe_types->insert (TYPE_UID (type1));
+ if (FUNCTION_POINTER_TYPE_P (type2) && !FUNCTION_POINTER_TYPE_P (type1))
+ unsafe_types->insert (TYPE_UID (type2));
+
+ /* Try to figure out with pointers to incomplete types. */
+ if (POINTER_TYPE_P (type1) && POINTER_TYPE_P (type2))
+ {
+ type1 = TYPE_MAIN_VARIANT (type1);
+ type2 = TYPE_MAIN_VARIANT (type2);
+ tree base1 = TREE_TYPE (type1);
+ tree base2 = TREE_TYPE (type2);
+ if (RECORD_OR_UNION_TYPE_P (base1) && RECORD_OR_UNION_TYPE_P (base2))
+ {
+ tree cb1 = TYPE_CANONICAL (base1);
+ tree cb2 = TYPE_CANONICAL (base2);
+ if (cb1 && !cb2)
+ map_canonical_base_to_pointer (type1, type2);
+ if (cb2 && !cb1)
+ map_canonical_base_to_pointer (type2, type1);
+ }
+ }
+}
+
+/* Maybe register non-void/equal type aliases. */
+
+static void
+maybe_register_non_void_aliases (tree t1, tree t2)
+{
+ gcc_assert (t1 && t2);
+ if (type_uid_map->count (TYPE_UID (t1)) == 0)
+ (*type_uid_map)[TYPE_UID (t1)] = t1;
+ if (type_uid_map->count (TYPE_UID (t2)) == 0)
+ (*type_uid_map)[TYPE_UID (t2)] = t2;
+
+ /* Skip equal and void types. */
+ if (t1 == t2 || VOID_TYPE_P (t1) || VOID_TYPE_P (t2))
+ return;
+ maybe_register_aliases (t1, t2);
+}
+
+/* Detect function type in call stmt. */
+
+static tree
+get_call_fntype (gcall *stmt)
+{
+ tree fntype = NULL;
+ if (gimple_call_fndecl (stmt) && TREE_TYPE (gimple_call_fndecl (stmt)))
+ fntype = TREE_TYPE (gimple_call_fndecl (stmt));
+ else
+ {
+ tree call_fn = gimple_call_fn (stmt);
+ tree ptype = TREE_TYPE (call_fn);
+ gcc_assert (ptype && TREE_TYPE (ptype));
+ fntype = TREE_TYPE (ptype);
+ }
+ gcc_assert (fntype && fntype != void_type_node
+ && (TREE_CODE (fntype) == FUNCTION_TYPE
+ || TREE_CODE (fntype) == METHOD_TYPE));
+ return fntype;
+}
+
+static void
+dump_global_var (tree decl)
+{
+ fprintf (dump_file, "Analyze global var: ");
+ print_generic_decl (dump_file, decl, TDF_NONE);
+ fprintf (dump_file, "\n");
+}
+
+static void
+collect_block_elt_types (tree tp, std::list<tree> &types, tree block)
+{
+ tree vt = TREE_TYPE (tp);
+ gcc_assert (vt);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ const char *msg = TREE_CODE (block) == BLOCK ? "VAR's block: " :
+ "VAR's ctor: ";
+ fprintf (dump_file, msg);
+ print_generic_expr (dump_file, tp);
+ dump_type_with_uid (" with type ", vt);
+ }
+ collect_scalar_types (vt, types);
+}
+
+/* Compare types of initialization block's or constructor's elements and
+ fields of the initializer type to find type aliases. */
+
+static void
+compare_block_and_init_type (tree block, tree t1)
+{
+ std::list<tree> tlist1;
+ std::list<tree> tlist2;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Init's type list: ");
+ collect_scalar_types (t1, tlist1);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Block's type list: ");
+ if (TREE_CODE (block) == CONSTRUCTOR)
+ {
+ unsigned HOST_WIDE_INT idx;
+ tree value;
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (block), idx, value)
+ {
+ gcc_assert (value);
+ collect_block_elt_types (value, tlist2, block);
+ }
+ }
+ else if (TREE_CODE (block) == BLOCK)
+ for (tree var = BLOCK_VARS (block); var; var = DECL_CHAIN (var))
+ {
+ if (TREE_CODE (var) != VAR_DECL)
+ continue;
+ collect_block_elt_types (var, tlist2, block);
+ }
+ else
+ gcc_unreachable ();
+ compare_type_lists (tlist1, tlist2);
+}
+
+/* Analyze global var to find type aliases comparing types of var and
+ initializer elements. */
+
+static void
+analyze_global_var (varpool_node *var)
+{
+ var->get_constructor();
+ tree decl = var->decl;
+ if (TREE_CODE (decl) == SSA_NAME || !DECL_INITIAL (decl)
+ || integer_zerop (DECL_INITIAL (decl)))
+ return;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_global_var (decl);
+ tree var_type = TREE_TYPE (decl);
+ tree init_type = TREE_TYPE (DECL_INITIAL (decl));
+ gcc_assert (var_type && init_type);
+ if (RECORD_OR_UNION_TYPE_P (init_type)
+ && !initializer_zerop (DECL_INITIAL (decl)))
+ compare_block_and_init_type (DECL_INITIAL (decl), init_type);
+ else if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Is not a record with nonzero init\n");
+
+ if (var_type == init_type)
+ return;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_two_types_with_uids ("Mismatch of var and init types: ",
+ var_type, init_type);
+ collect_scalar_types_and_find_aliases (var_type, init_type);
+}
+
+static void
+dump_function_node_info (struct cgraph_node *n)
+{
+ fprintf (dump_file, "\nAnalyse function node: ");
+ print_generic_expr (dump_file, n->decl);
+ fprintf (dump_file, "\n");
+ tree fndecl_type = TREE_TYPE (n->decl);
+ dump_type_with_uid ("Function decl type: ", fndecl_type, TDF_UID);
+ if (TREE_TYPE (fndecl_type))
+ dump_type_with_uid ("Return type: ", TREE_TYPE (fndecl_type));
+ tree argt = TYPE_ARG_TYPES (fndecl_type);
+ for (unsigned i = 1; argt && argt != void_type_node
+ && !VOID_TYPE_P (TREE_VALUE (argt)); ++i, argt = TREE_CHAIN (argt))
+ {
+ tree atype = TREE_VALUE (argt);
+ fprintf (dump_file, "%d-arg type: ", i);
+ dump_type_with_uid ("", atype);
+ }
+ fprintf (dump_file, "\n");
+}
+
+static void
+dump_call_stmt_info (gcall *stmt, tree fntype)
+{
+ fprintf (dump_file, "\nAnalyse call stmt: ");
+ if (stmt)
+ print_gimple_stmt (dump_file, stmt, 3, TDF_DETAILS);
+ else
+ fprintf (dump_file, "(no stmt)\n");
+ dump_type_with_uid ("fntype=", fntype, TDF_UID);
+ if (gimple_call_fntype (stmt))
+ dump_type_with_uid ("fntype1=", gimple_call_fntype (stmt), TDF_UID);
+ if (gimple_call_fndecl (stmt) && TREE_TYPE (gimple_call_fndecl (stmt)))
+ dump_type_with_uid ("fntype2=", TREE_TYPE (gimple_call_fndecl (stmt)),
+ TDF_UID);
+}
+
+/* Dump actual and formal arg types. */
+
+static void
+dump_arg_types_with_uids (int i, tree t1, tree t2)
+{
+ if (i >= 0)
+ fprintf (dump_file, "Call's %d-arg types: ", i);
+ else
+ fprintf (dump_file, "Call's return types: ");
+ fprintf (dump_file, "(%d) and (%d) ", TYPE_UID (t1), TYPE_UID (t2));
+ print_generic_expr (dump_file, t1, TDF_UID);
+ fprintf (dump_file, " ");
+ print_generic_expr (dump_file, t2, TDF_UID);
+ fprintf (dump_file, "\n");
+}
+
+/* Analyze call graph edge with connected call stmt to find type aliases in
+ arguments and return value casts. */
+
+static void
+analyze_cgraph_edge (cgraph_edge *e)
+{
+ gcall *stmt = e->call_stmt;
+ gcc_assert (stmt != NULL);
+ tree fntype = get_call_fntype (stmt);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_call_stmt_info (stmt, fntype);
+ if (gimple_has_lhs (stmt))
+ {
+ tree t1 = TREE_TYPE (gimple_call_lhs (stmt));
+ tree t2 = TREE_TYPE (fntype);
+ const int is_return_arg = -1;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_arg_types_with_uids (is_return_arg, t1, t2);
+ maybe_register_non_void_aliases (t1, t2);
+ }
+
+ tree argt = TYPE_ARG_TYPES (fntype);
+ if (!argt)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Finish call stmt analysis\n");
+ return;
+ }
+ gcc_assert (argt);
+ unsigned num_args = gimple_call_num_args (stmt);
+ for (unsigned i = 0; i < num_args && argt; ++i, argt = TREE_CHAIN (argt))
+ {
+ tree arg = gimple_call_arg (stmt, i);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_arg_types_with_uids (i, TREE_VALUE (argt), TREE_TYPE (arg));
+ if (TREE_VALUE (argt) == TREE_TYPE (arg)
+ || !POINTER_TYPE_P (TREE_VALUE (argt))
+ || !POINTER_TYPE_P (TREE_TYPE (arg)))
+ continue;
+ maybe_register_non_void_aliases (TREE_VALUE (argt), TREE_TYPE (arg));
+ tree t1 = TREE_TYPE (TREE_VALUE (argt));
+ tree t2 = TREE_TYPE (TREE_TYPE (arg));
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Call's %d-arg base types: (%d) and (%d)\n",
+ i, (t1 ? TYPE_UID (t1) : 0), (t2 ? TYPE_UID (t2) : 0));
+ maybe_register_non_void_aliases (t1, t2);
+ }
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "End list of args\n");
+ tree fndecl_type = NULL;
+ if (e->callee && e->callee->decl)
+ fndecl_type = TREE_TYPE (e->callee->decl);
+ if (fndecl_type && fndecl_type != fntype)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Function decl and edge types mismatch:\n");
+ register_ailas_type (fntype, fndecl_type, fta_map);
+ }
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "End call stmt analysis\n");
+}
+
+static void
+dump_assign_info (gimple *stmt, tree rhs, tree lhs_type, tree rhs_type)
+{
+ fprintf (dump_file, "\nAnalyse assign cast/copy stmt, rhs=%s: ",
+ get_tree_code_name (TREE_CODE (rhs)));
+ print_gimple_stmt (dump_file, stmt, 3, TDF_DETAILS);
+ fprintf (dump_file, "Types: ");
+ print_generic_expr (dump_file, lhs_type);
+ fprintf (dump_file, ", ");
+ print_generic_expr (dump_file, rhs_type);
+ fprintf (dump_file, "\n");
+}
+
+/* Analyze cast/copy assign stmt to find type aliases. */
+
+static void
+analyze_assign_stmt (gimple *stmt)
+{
+ gcc_assert (is_gimple_assign (stmt));
+ tree rhs_type = NULL_TREE;
+ tree lhs_type = TREE_TYPE (gimple_assign_lhs (stmt));
+ tree rhs = gimple_assign_rhs1 (stmt);
+ if (TREE_CODE (rhs) == MEM_REF)
+ {
+ rhs = TREE_OPERAND (rhs, 0);
+ tree ptr_type = TREE_TYPE (rhs);
+ gcc_assert (POINTER_TYPE_P (ptr_type));
+ rhs_type = TREE_TYPE (ptr_type);
+ }
+ else if (TREE_CODE (rhs) == ADDR_EXPR)
+ {
+ rhs = TREE_OPERAND (rhs, 0);
+ if (VAR_OR_FUNCTION_DECL_P (rhs) || TREE_CODE (rhs) == STRING_CST
+ || TREE_CODE (rhs) == ARRAY_REF || TREE_CODE (rhs) == PARM_DECL)
+ rhs_type = build_pointer_type (TREE_TYPE (rhs));
+ else if (TREE_CODE (rhs) == COMPONENT_REF)
+ {
+ rhs = TREE_OPERAND (rhs, 1);
+ rhs_type = build_pointer_type (TREE_TYPE (rhs));
+ }
+ else if (TREE_CODE (rhs) == MEM_REF)
+ {
+ rhs = TREE_OPERAND (rhs, 0);
+ rhs_type = TREE_TYPE (rhs);
+ gcc_assert (POINTER_TYPE_P (rhs_type));
+ }
+ else
+ gcc_unreachable();
+ }
+ else
+ rhs_type = TREE_TYPE (rhs);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_assign_info (stmt, rhs, lhs_type, rhs_type);
+ if (CONSTANT_CLASS_P (rhs) && !zerop (rhs)
+ && FUNCTION_POINTER_TYPE_P (TREE_TYPE (rhs)))
+ {
+ tree ftype = TREE_TYPE (rhs_type);
+ unsafe_types->insert (TYPE_UID (ftype));
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Function type (%d) is unsafe due to assign "
+ "non-zero cst to function pointer\n", TYPE_UID (ftype));
+ }
+ maybe_register_non_void_aliases (lhs_type, rhs_type);
+}
+
+/* Walk all fn's stmt to analyze assigns. */
+
+static void
+analyze_assigns (function* fn)
+{
+ push_cfun (fn);
+ basic_block bb;
+ gimple_stmt_iterator si;
+ FOR_EACH_BB_FN (bb, fn)
+ for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
+ {
+ gimple *stmt = gsi_stmt (si);
+ if (!gimple_assign_cast_p (stmt) && !gimple_assign_copy_p (stmt))
+ continue;
+ analyze_assign_stmt (stmt);
+ }
+ pop_cfun ();
+}
+
+/* Walk all functions to collect sets of type aliases. */
+
+static void
+collect_type_alias_sets ()
+{
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\n\nCollect type alias sets walking global vars.\n");
+
+ varpool_node *var;
+ FOR_EACH_VARIABLE (var)
+ if (var->real_symbol_p ())
+ analyze_global_var (var);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\nCollect type alias sets walking functions.\n");
+
+ struct cgraph_node *n;
+ FOR_EACH_FUNCTION (n)
+ {
+ if (!n->has_gimple_body_p ())
+ continue;
+ n->get_body ();
+ function *fn = DECL_STRUCT_FUNCTION (n->decl);
+ if (!fn)
+ continue;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_function_node_info (n);
+ /* Analyze direct/indirect function calls. */
+ for (cgraph_edge *e = n->callees; e; e = e->next_callee)
+ analyze_cgraph_edge (e);
+ for (cgraph_edge *e = n->indirect_calls; e; e = e->next_callee)
+ analyze_cgraph_edge (e);
+ /* Analyze assign (with casts) statements. */
+ analyze_assigns (fn);
+ }
+}
+
+static void
+process_cbase_to_ptype_map ()
+{
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\nProcess types in cbase-to-ptypes map:\n");
+
+ for (type_alias_map::iterator it1 = cbase_to_ptype->begin ();
+ it1 != cbase_to_ptype->end (); ++it1)
+ {
+ type_set *set = it1->second;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_type_uid_with_set ("cb=(%d): ", (*type_uid_map)[it1->first],
+ cbase_to_ptype);
+ tree ctype = NULL;
+ for (type_set::const_iterator it2 = set->begin ();
+ it2 != set->end (); it2++)
+ {
+ tree t2 = (*type_uid_map)[*it2];
+ if (t2 == TYPE_MAIN_VARIANT (t2))
+ {
+ ctype = t2;
+ break;
+ }
+ }
+ if (!ctype)
+ continue;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_type_with_uid ("Select canonical type: ", ctype);
+ for (type_set::const_iterator it2 = set->begin ();
+ it2 != set->end (); it2++)
+ {
+ tree t = (*type_uid_map)[*it2];
+ if (!ctype_map->count (t))
+ {
+ (*ctype_map)[t] = ctype;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Set canonical type for (%d)->c(%d)\n",
+ *it2, TYPE_UID (ctype));
+ }
+ else if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Canonical type is already set (%d)->c(%d)\n",
+ *it2, TYPE_UID ((*ctype_map)[t]));
+ }
+ }
+}
+
+static void
+set_canonical_type_for_type_set (type_set *set)
+{
+ tree one_canonical = NULL;
+ for (type_set::const_iterator it = set->begin (); it != set->end (); it++)
+ {
+ tree t = (*type_uid_map)[*it];
+ gcc_assert (t);
+ if ((TYPE_CANONICAL (t) || ctype_map->count (t)))
+ {
+ one_canonical = TYPE_CANONICAL (t) ? TYPE_CANONICAL (t)
+ : (*ctype_map)[t];
+ gcc_assert (COMPLETE_TYPE_P (t));
+ break;
+ }
+ }
+ for (type_set::const_iterator it = set->begin (); it != set->end (); it++)
+ {
+ tree t = (*type_uid_map)[*it];
+ if (!ctype_map->count (t))
+ {
+ (*ctype_map)[t] = one_canonical;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ if (one_canonical)
+ fprintf (dump_file, "Set canonical type for (%d)->c(%d)\n",
+ TYPE_UID (t), TYPE_UID (one_canonical));
+ else
+ fprintf (dump_file, "Set NULL canonical for (%d)\n", *it);
+ }
+ }
+ else if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ tree ct = (*ctype_map)[t];
+ fprintf (dump_file, "Canonical type is already set (%d)->c(%d)\n",
+ TYPE_UID (t), ct ? TYPE_UID (ct) : -1);
+ }
+ }
+}
+
+static void
+dump_is_type_set_incomplete (type_set * set)
+{
+ bool has_complete_types = false;
+ for (type_set::const_iterator it = set->begin (); it != set->end (); it++)
+ if (COMPLETE_TYPE_P ((*type_uid_map)[*it]))
+ {
+ has_complete_types = true;
+ break;
+ }
+ if (!has_complete_types)
+ fprintf (dump_file, "Set of incomplete types\n");
+}
+
+static void
+process_alias_type_sets ()
+{
+ if (dump_file)
+ fprintf (dump_file, "\nProcess alias sets of types:\n");
+ /* Keep processed types to process each type set (in ta_map) only once. */
+ type_set processed_types;
+ for (type_alias_map::iterator it1 = ta_map->begin ();
+ it1 != ta_map->end (); ++it1)
+ {
+ tree type = (*type_uid_map)[it1->first];
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_type_uid_with_set ("(%d) ", type, ta_map);
+ if (processed_types.count (TYPE_UID (type)) != 0
+ || unsafe_types->count (TYPE_UID (type)) != 0)
+ continue;
+ type_set *set = it1->second;
+ for (type_set::const_iterator it2 = set->begin ();
+ it2 != set->end (); it2++)
+ processed_types.insert (*it2);
+ /* Check if this type set contains function pointers and
+ non-function pointers. */
+ bool has_no_fp = false, has_fp = false;
+ for (type_set::const_iterator it2 = set->begin ();
+ it2 != set->end (); it2++)
+ {
+ tree t2 = (*type_uid_map)[*it2];
+ if (FUNCTION_POINTER_TYPE_P (t2))
+ has_fp = true;
+ else
+ has_no_fp = true;
+ if (has_fp && has_no_fp)
+ break;
+ }
+ if (has_fp)
+ {
+ for (type_set::const_iterator it2 = set->begin ();
+ it2 != set->end (); it2++)
+ {
+ tree t2 = (*type_uid_map)[*it2];
+ /* If it's a type set with mixed function and not-function types,
+ mark all function pointer types in the set as unsafe. */
+ if (has_no_fp && FUNCTION_POINTER_TYPE_P (t2))
+ {
+ tree ftype = TREE_TYPE (t2);
+ unsafe_types->insert (TYPE_UID (ftype));
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Insert function type (%d) to unsafe "
+ "due to escape its pointer type (%d) to mixed "
+ "alias set (printed before)\n",
+ TYPE_UID (ftype), TYPE_UID (t2));
+ }
+ /* If it's a type set with only function pointer types,
+ mark all base function types in the set as aliases. */
+ if (!has_no_fp)
+ {
+ gcc_assert (FUNCTION_POINTER_TYPE_P (type)
+ && FUNCTION_POINTER_TYPE_P (t2));
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Insert function type aliases by "
+ "function pointer aliases:\n");
+ register_ailas_type (TREE_TYPE (type), TREE_TYPE (t2),
+ fta_map);
+ }
+ }
+ }
+ set_canonical_type_for_type_set (set);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_is_type_set_incomplete (set);
+ }
+}
+
+static void
+dump_unsafe_and_canonical_types ()
+{
+ fprintf (dump_file, "\nList of unsafe types:\n");
+ for (type_set::iterator it = unsafe_types->begin ();
+ it != unsafe_types->end (); ++it)
+ {
+ print_generic_expr (dump_file, (*type_uid_map)[*it]);
+ fprintf (dump_file, " (%d)\n", *it);
+ }
+ fprintf (dump_file, "\nList of alias canonical types:\n");
+ for (type_alias_map::iterator it = ta_map->begin ();
+ it != ta_map->end (); ++it)
+ {
+ tree type = (*type_uid_map)[it->first];
+ if (ctype_map->count (type) == 0)
+ continue;
+ print_generic_expr (dump_file, type);
+ fprintf (dump_file, " -> ");
+ tree ctype = (*ctype_map)[type];
+ if (ctype != NULL)
+ {
+ print_generic_expr (dump_file, ctype);
+ fprintf (dump_file, " (%d)->(%d)\n",
+ TYPE_UID (type), TYPE_UID (ctype));
+ }
+ else
+ fprintf (dump_file, " null\n");
+ }
+}
+
+static void
+init_function_type_alias_for_edge (cgraph_edge *e)
+{
+ gcall *stmt = e->call_stmt;
+ gcc_assert (stmt != NULL);
+ tree fntype = get_call_fntype (stmt);
+ if (fta_map->count (TYPE_UID (fntype)) == 0)
+ register_ailas_type (fntype, fntype, fta_map);
+}
+
+/* This pass over all function types makes each function type to have
+ at least one alias (itself). */
+
+static void
+init_function_type_aliases ()
+{
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\nInit aliases for all function types.\n");
+
+ struct cgraph_node *n;
+ FOR_EACH_FUNCTION (n)
+ {
+ tree fntype = TREE_TYPE (n->decl);
+ if (fta_map->count (TYPE_UID (fntype)) == 0)
+ register_ailas_type (fntype, fntype, fta_map);
+
+ if (!n->has_gimple_body_p ())
+ continue;
+ n->get_body ();
+ function *fn = DECL_STRUCT_FUNCTION (n->decl);
+ if (!fn)
+ continue;
+
+ /* Init for function types of direct/indirect callees. */
+ for (cgraph_edge *e = n->callees; e; e = e->next_callee)
+ init_function_type_alias_for_edge (e);
+ for (cgraph_edge *e = n->indirect_calls; e; e = e->next_callee)
+ init_function_type_alias_for_edge (e);
+ }
+}
+
+/* In lto-common.c there is the global canonical type table and the
+ corresponding machinery which detects the same types from differens
+ modules and joins them assigning the one canonical type. However
+ lto does not set the goal to do a complete and precise matching, so
+ sometimes a few types has no TYPE_CANONICAL set. Since ICP relies on
+ precise type matching, we create the similar table and register all
+ the required types in it. */
+
+static std::map<const_tree, hashval_t> *canonical_type_hash_cache = NULL;
+static std::map<hashval_t, tree> *icp_canonical_types = NULL;
+
+static hashval_t hash_canonical_type (tree type);
+
+/* Register canonical type in icp_canonical_types and ctype_map evaluating
+ its hash (using hash_canonical_type) if it's needed. */
+
+static hashval_t
+icp_register_canonical_type (tree t)
+{
+ hashval_t hash;
+ if (canonical_type_hash_cache->count ((const_tree) t) == 0)
+ {
+ tree t1 = TYPE_MAIN_VARIANT (t);
+ if (!COMPLETE_TYPE_P (t1) && TYPE_CANONICAL (t1)
+ && COMPLETE_TYPE_P (TYPE_CANONICAL (t1)))
+ {
+ t1 = TYPE_CANONICAL (t1);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Use complete canonical (%d) for (%d)\n",
+ TYPE_UID (t1), TYPE_UID (t));
+ }
+ hash = hash_canonical_type (t1);
+ /* Cache the just computed hash value. */
+ (*canonical_type_hash_cache)[(const_tree) t] = hash;
+ }
+ else
+ hash = (*canonical_type_hash_cache)[(const_tree) t];
+
+ tree new_type = t;
+ if (icp_canonical_types->count (hash))
+ {
+ new_type = (*icp_canonical_types)[hash];
+ gcc_checking_assert (new_type != t);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Found canonical (%d) for (%d), h=%u\n",
+ TYPE_UID (new_type), TYPE_UID (t), (unsigned int) hash);
+ }
+ else
+ {
+ (*icp_canonical_types)[hash] = t;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Register canonical %d, h=%u\n", TYPE_UID (t),
+ (unsigned int) hash);
+ }
+ if (ctype_map->count (t) == 0)
+ (*ctype_map)[t] = new_type;
+ return hash;
+}
+
+/* Merge hstate with hash of the given type. If the type is not registered,
+ register it in the maps of the canonical types. */
+
+static void
+iterative_hash_canonical_type (tree type, inchash::hash &hstate)
+{
+ hashval_t v;
+ /* All type variants have same TYPE_CANONICAL. */
+ type = TYPE_MAIN_VARIANT (type);
+ if (canonical_type_hash_cache->count ((const_tree) type))
+ v = (*canonical_type_hash_cache)[(const_tree) type];
+ else
+ v = icp_register_canonical_type (type);
+ hstate.merge_hash (v);
+}
+
+/* Compute and return hash for the given type. It does not take into account
+ base types of pointer types. */
+
+static hashval_t
+hash_canonical_type (tree type)
+{
+ inchash::hash hstate;
+ enum tree_code code;
+ /* Combine a few common features of types so that types are grouped into
+ smaller sets; when searching for existing matching types to merge,
+ only existing types having the same features as the new type will be
+ checked. */
+ code = tree_code_for_canonical_type_merging (TREE_CODE (type));
+ hstate.add_int (code);
+ if (!RECORD_OR_UNION_TYPE_P (type))
+ hstate.add_int (TYPE_MODE (type));
+ /* Incorporate common features of numerical types. */
+ if (INTEGRAL_TYPE_P (type)
+ || SCALAR_FLOAT_TYPE_P (type)
+ || FIXED_POINT_TYPE_P (type)
+ || TREE_CODE (type) == OFFSET_TYPE
+ || POINTER_TYPE_P (type))
+ {
+ hstate.add_int (TYPE_PRECISION (type));
+ if (!type_with_interoperable_signedness (type))
+ hstate.add_int (TYPE_UNSIGNED (type));
+ }
+ if (VECTOR_TYPE_P (type))
+ {
+ hstate.add_poly_int (TYPE_VECTOR_SUBPARTS (type));
+ hstate.add_int (TYPE_UNSIGNED (type));
+ }
+ if (TREE_CODE (type) == COMPLEX_TYPE)
+ hstate.add_int (TYPE_UNSIGNED (type));
+ if (POINTER_TYPE_P (type))
+ hstate.add_int (TYPE_ADDR_SPACE (TREE_TYPE (type)));
+ /* For array types hash the domain bounds and the string flag. */
+ if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type))
+ {
+ hstate.add_int (TYPE_STRING_FLAG (type));
+ /* OMP lowering can introduce error_mark_node in place of
+ random local decls in types. */
+ if (TYPE_MIN_VALUE (TYPE_DOMAIN (type)) != error_mark_node)
+ inchash::add_expr (TYPE_MIN_VALUE (TYPE_DOMAIN (type)), hstate);
+ if (TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != error_mark_node)
+ inchash::add_expr (TYPE_MAX_VALUE (TYPE_DOMAIN (type)), hstate);
+ }
+ /* Recurse for aggregates with a single element type. */
+ if (TREE_CODE (type) == ARRAY_TYPE
+ || TREE_CODE (type) == COMPLEX_TYPE
+ || TREE_CODE (type) == VECTOR_TYPE)
+ iterative_hash_canonical_type (TREE_TYPE (type), hstate);
+ /* Incorporate function return and argument types. */
+ if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
+ {
+ unsigned nargs = 0;
+ iterative_hash_canonical_type (TREE_TYPE (type), hstate);
+ for (tree p = TYPE_ARG_TYPES (type); p; p = TREE_CHAIN (p))
+ {
+ iterative_hash_canonical_type (TREE_VALUE (p), hstate);
+ nargs++;
+ }
+ hstate.add_int (nargs);
+ }
+ if (RECORD_OR_UNION_TYPE_P (type))
+ {
+ unsigned nfields = 0;
+ for (tree f = TYPE_FIELDS (type); f; f = TREE_CHAIN (f))
+ if (TREE_CODE (f) == FIELD_DECL)
+ {
+ iterative_hash_canonical_type (TREE_TYPE (f), hstate);
+ nfields++;
+ }
+ hstate.add_int (nfields);
+ }
+ return hstate.end ();
+}
+
+/* It finds canonical type in ctype_map and icp_canonical_types maps. */
+
+static tree
+find_canonical_type (tree type)
+{
+ if (ctype_map->count (type))
+ return (*ctype_map)[type];
+ if (canonical_type_hash_cache->count ((const_tree) type) == 0)
+ return NULL;
+ hashval_t h = (*canonical_type_hash_cache)[(const_tree) type];
+ if (icp_canonical_types->count (h))
+ return (*icp_canonical_types)[h];
+ return NULL;
+}
+
+/* It updates hash for the given type taking into account pointees in pointer
+ types. If the type is incomplete function type, it returns true. It's used
+ only for function type hash calculation. */
+
+static bool
+initial_hash_canonical_type (tree type, inchash::hash &hstate)
+{
+ /* All type variants have same TYPE_CANONICAL. */
+ type = TYPE_MAIN_VARIANT (type);
+ if (VOID_TYPE_P (type))
+ {
+ hstate.add_int (POINTER_TYPE);
+ return false;
+ }
+ hstate.add_int (TREE_CODE (type));
+ hstate.add_int (TYPE_MODE (type));
+ if (POINTER_TYPE_P (type))
+ {
+ tree base_type = TREE_TYPE (type);
+ hstate.add_int (TYPE_ADDR_SPACE (base_type));
+ return initial_hash_canonical_type (base_type, hstate);
+ }
+ tree ctype = find_canonical_type (type);
+ if (!ctype)
+ {
+ if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Due to ftype (%d)\n", TYPE_UID (type));
+ return true;
+ }
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_type_with_uid ("Has NO canonical type: ", type, TDF_UID);
+ icp_register_canonical_type (type);
+ if (ctype_map->count(type))
+ ctype = (*ctype_map)[type];
+ if (ctype && dump_file && (dump_flags & TDF_DETAILS))
+ dump_type_with_uid ("Found canonical type: ", ctype, TDF_UID);
+ }
+ else if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_type_with_uid ("Canonical type: ", ctype, TDF_UID);
+ hstate.add_int (TYPE_UID (ctype));
+ return false;
+}
+
+/* It returns hash value for the given function type. If the function type is
+ incomplete, insert it in the incomplete_hash_ftype set. */
+
+static hashval_t
+get_hash_for_ftype (tree type, type_set *incomplete_hash_ftype)
+{
+ bool incomplete = false;
+ inchash::hash hstate;
+ /* Function type is expected. */
+ gcc_assert (TREE_CODE (type) == FUNCTION_TYPE
+ || TREE_CODE (type) == METHOD_TYPE);
+ /* Hash return type. */
+ tree rt = TREE_TYPE (type);
+ tree ct = rt ? find_canonical_type (rt) : void_type_node;
+ incomplete |= initial_hash_canonical_type (ct ? ct : rt, hstate);
+ /* Hash arg types. */
+ tree argt = TYPE_ARG_TYPES (type);
+ if (!argt)
+ incomplete |= initial_hash_canonical_type (void_type_node, hstate);
+ else
+ for (unsigned i = 1; argt; ++i, argt = TREE_CHAIN (argt))
+ {
+ tree ct = find_canonical_type (TREE_VALUE (argt));
+ ct = ct ? ct : TREE_VALUE (argt);
+ incomplete |= initial_hash_canonical_type (ct, hstate);
+ }
+ if (incomplete && incomplete_hash_ftype->count (TYPE_UID (type)) == 0)
+ incomplete_hash_ftype->insert (TYPE_UID (type));
+ else if (!incomplete && incomplete_hash_ftype->count (TYPE_UID (type)) != 0)
+ incomplete_hash_ftype->erase (TYPE_UID (type));
+ return hstate.end();
+}
+
+/* Find type aliases evaluating type hashes and connecting types with
+ the same hash values. */
+
+static void
+find_type_aliases_by_compatibility ()
+{
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\nFind type aliases checking their compatibility.\n");
+
+ std::map<hashval_t, tree> hash_to_ftype;
+ type_set *incomplete_hash_ftype = new type_set;
+ canonical_type_hash_cache = new std::map<const_tree, hashval_t>;
+ icp_canonical_types = new std::map<hashval_t, tree>;
+
+ bool changed;
+ int i = 0;
+ do
+ {
+ changed = false;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Iteration %d\n", i);
+ for (type_alias_map::iterator it = fta_map->begin ();
+ it != fta_map->end (); ++it)
+ {
+ tree type = (*type_uid_map)[it->first];
+ if (TYPE_CANONICAL (type))
+ continue;
+ hashval_t hash = get_hash_for_ftype (type, incomplete_hash_ftype);
+ if (incomplete_hash_ftype->count (TYPE_UID (type)) != 0)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Incomplete (%d), h=%u\n", TYPE_UID (type),
+ (unsigned int) hash);
+ continue;
+ }
+ if (hash_to_ftype.count (hash) == 0)
+ hash_to_ftype[hash] = type;
+ TYPE_CANONICAL (type) = hash_to_ftype[hash];
+ changed = true;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "(%d)->(%d), h=%u\n", TYPE_UID (type),
+ TYPE_UID (TYPE_CANONICAL (type)), (unsigned int) hash);
+ }
+ i++;
+ }
+ while (changed);
+
+ delete incomplete_hash_ftype;
+ delete icp_canonical_types;
+ delete canonical_type_hash_cache;
+}
+
+static void
+dump_function_type_aliases_list ()
+{
+ fprintf (dump_file, "\nList of function type aliases:\n");
+ for (type_alias_map::iterator it = fta_map->begin ();
+ it != fta_map->end (); ++it)
+ dump_type_uid_with_set ("(%d) ", (*type_uid_map)[it->first], fta_map);
+}
+
+/* Collect type aliases and find missed canonical types. */
+
+static void
+collect_function_type_aliases ()
+{
+ collect_type_alias_sets ();
+ process_cbase_to_ptype_map ();
+ process_alias_type_sets ();
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_unsafe_and_canonical_types ();
+
+ /* TODO: maybe remove this pass. */
+ init_function_type_aliases ();
+ for (type_alias_map::iterator it = fta_map->begin ();
+ it != fta_map->end (); ++it)
+ set_canonical_type_for_type_set (it->second);
+ find_type_aliases_by_compatibility ();
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_function_type_aliases_list ();
+}
+
+static void
+dump_function_signature_info (struct cgraph_node *n, tree ftype, bool varargs)
+{
+ fprintf (dump_file, "Function decl: ");
+ print_generic_expr (dump_file, n->decl);
+ dump_type_uid_with_set (" with type (%d) ", ftype, fta_map, true, false);
+ if (varargs)
+ fprintf (dump_file, "has varargs, ");
+ if (TREE_CODE (ftype) == METHOD_TYPE)
+ fprintf (dump_file, "is method, ");
+ if (!n->address_taken)
+ fprintf (dump_file, "is not address taken, ");
+ if (unsafe_types->count (TYPE_UID (ftype)))
+ fprintf (dump_file, "is unsafe, ");
+ fprintf (dump_file, "\n");
+}
+
+/* Check if the function has variadic arguments.
+ It's corrected count_num_arguments (). */
+
+static bool
+has_varargs (tree decl)
+{
+ tree t;
+ unsigned int num = 0;
+ for (t = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ t && TREE_VALUE (t) != void_type_node; t = TREE_CHAIN (t))
+ num++;
+ if (!t && num)
+ return true;
+ return false;
+}
+
+/* Join fs_map's sets for function type aliases. */
+
+static void
+merge_fs_map_for_ftype_aliases ()
+{
+ if (dump_file)
+ fprintf (dump_file, "\n\nMerge decl sets for function type aliases:\n");
+ type_set processed_types;
+ for (type_decl_map::iterator it1 = fs_map->begin ();
+ it1 != fs_map->end (); ++it1)
+ {
+ if (processed_types.count (it1->first) != 0)
+ continue;
+ decl_set *d_set = it1->second;
+ tree type = (*type_uid_map)[it1->first];
+ type_set *set = (*fta_map)[it1->first];
+ for (type_set::const_iterator it2 = set->begin ();
+ it2 != set->end (); it2++)
+ {
+ tree t2 = (*type_uid_map)[*it2];
+ processed_types.insert (*it2);
+ if (type == t2)
+ continue;
+ gcc_assert ((TREE_CODE (type) == FUNCTION_TYPE
+ || TREE_CODE (type) == METHOD_TYPE)
+ && (TREE_CODE (t2) == FUNCTION_TYPE
+ || TREE_CODE (t2) == METHOD_TYPE));
+ if (fs_map->count (*it2) == 0 || (*fs_map)[*it2] == NULL)
+ (*fs_map)[*it2] = d_set;
+ else
+ {
+ decl_set *t2_decl_set = (*fs_map)[*it2];
+ (*fs_map)[*it2] = d_set;
+ gcc_assert (t2_decl_set && t2_decl_set->size() > 0);
+ d_set->insert (t2_decl_set->begin (), t2_decl_set->end ());
+ delete t2_decl_set;
+ }
+ }
+ }
+}
+
+/* Dump function types with set of functions corresponding to it. */
+
+static void
+dump_function_signature_sets ()
+{
+ fprintf (dump_file, "\n\nUnique sets of function signatures:\n");
+ std::set<decl_set *> processed_sets;
+ for (type_decl_map::iterator it1 = fs_map->begin ();
+ it1 != fs_map->end (); ++it1)
+ {
+ decl_set *set = it1->second;
+ if (processed_sets.count (set) != 0)
+ continue;
+ processed_sets.insert (set);
+ fprintf (dump_file, "{ ");
+ print_type_set (it1->first, fta_map);
+ fprintf (dump_file, " : ");
+ for (decl_set::const_iterator it2 = set->begin ();
+ it2 != set->end (); it2++)
+ {
+ fprintf (dump_file, it2 == set->begin () ? "" : ", ");
+ print_generic_expr (dump_file, *it2);
+ fprintf (dump_file, "(%d)", DECL_UID (*it2));
+ }
+ fprintf (dump_file, "}\n");
+ }
+}
+
+/* Fill the map of function types to sets of function decls. */
+
+static void
+collect_function_signatures ()
+{
+ if (dump_file)
+ fprintf (dump_file, "\n\nCollect function signatures:\n");
+ struct cgraph_node *n;
+ FOR_EACH_FUNCTION (n)
+ {
+ gcc_assert (n->decl && TREE_TYPE (n->decl));
+ tree ftype = TREE_TYPE (n->decl);
+ bool varargs = has_varargs (n->decl);
+ if (varargs && n->address_taken)
+ has_address_taken_functions_with_varargs = true;
+ if (dump_file)
+ dump_function_signature_info (n, ftype, varargs);
+ if (!n->address_taken)
+ continue;
+ /* TODO: make a separate pass at the end to remove canonicals. */
+ tree ctype = TYPE_CANONICAL (ftype);
+ unsigned alias_type_fs = ctype ? TYPE_UID (ctype) : 0;
+ if (dump_file)
+ fprintf (dump_file, "canonical type: %d %ld\n",
+ alias_type_fs, fs_map->count (alias_type_fs));
+ if (alias_type_fs)
+ {
+ if (fs_map->count (TYPE_UID (ctype)) == 0)
+ (*fs_map)[TYPE_UID (ctype)] = new decl_set ();
+ if (dump_file)
+ fprintf (dump_file, "insert decl (%d) to set of map [%d]\n",
+ DECL_UID (n->decl), TYPE_UID (ctype));
+ (*fs_map)[TYPE_UID (ctype)]->insert (n->decl);
+ }
+ }
+ merge_fs_map_for_ftype_aliases ();
+ if (dump_file)
+ dump_function_signature_sets ();
+}
+
+#define MAX_TARG_STAT 4
+struct icp_stats
+{
+ int npolymorphic;
+ int nspeculated;
+ int nsubst;
+ int ncold;
+ int nmultiple;
+ int noverwritable;
+ int nnotdefined;
+ int nexternal;
+ int nartificial;
+ int nremove;
+ int nicp;
+ int nspec;
+ int nf;
+ int ncalls;
+ int nindir;
+ int nind_only;
+ int ntargs[MAX_TARG_STAT + 1];
+};
+
+static void
+dump_processing_function (struct cgraph_node *n, struct icp_stats &stats)
+{
+ fprintf (dump_file, "\n\nProcesing function %s\n", n->dump_name ());
+ print_generic_expr (dump_file, n->decl);
+ fprintf (dump_file, "\n");
+ dump_type_with_uid ("Func's type: ", TREE_TYPE (n->decl));
+ if (dump_file && (dump_flags & TDF_STATS))
+ {
+ struct cgraph_edge *e;
+ stats.nf++;
+ for (e = n->indirect_calls; e; e = e->next_callee)
+ stats.nindir++;
+ for (e = n->callees; e; e = e->next_callee)
+ stats.ncalls++;
+ stats.ncalls += stats.nindir;
+ if (n->callers == NULL)
+ {
+ fprintf (dump_file, "Function has NO callers\n");
+ stats.nind_only++;
+ }
+ }
+}
+
+static void
+dump_indirect_call_site (tree call_fn, tree call_fn_ty)
+{
+ fprintf (dump_file, "Indirect call site: ");
+ print_generic_expr (dump_file, call_fn);
+ dump_type_with_uid ("\nFunction pointer type: ", call_fn_ty);
+}
+
+static void
+erase_from_unreachable (unsigned type_uid, type_set &unreachable)
+{
+ unreachable.erase (type_uid);
+ if (!fta_map->count (type_uid))
+ return;
+ type_set *set = (*fta_map)[type_uid];
+ for (type_set::const_iterator it = set->begin (); it != set->end (); it++)
+ unreachable.erase (*it);
+}
+
+static void
+dump_found_fdecls (decl_set *decls, unsigned ctype_uid)
+{
+ fprintf (dump_file, "Signature analysis FOUND decls (%d):", ctype_uid);
+ for (decl_set::const_iterator it = decls->begin (); it != decls->end (); it++)
+ {
+ print_generic_expr (dump_file, *it);
+ fprintf (dump_file, "(%d), ", DECL_UID (*it));
+ }
+ if (unsafe_types->count (ctype_uid))
+ fprintf (dump_file, "type is UNSAFE");
+ fprintf (dump_file, "\n");
+}
+
+static void
+count_found_targets (struct icp_stats &stats, unsigned size)
+{
+ gcc_assert (size > 0);
+ stats.ntargs[size > MAX_TARG_STAT ? MAX_TARG_STAT : size - 1]++;
+}
+
+/* Promote the indirect call. */
+
+static void
+promote_call (struct cgraph_edge *e, struct cgraph_node *n,
+ struct cgraph_node *likely_target, struct icp_stats *stats)
+{
+ if (dump_enabled_p ())
+ {
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
+ "promoting indirect call in %s to %s\n",
+ n->dump_name (), likely_target->dump_name ());
+ }
+ if (!likely_target->can_be_discarded_p ())
+ {
+ symtab_node *sn = likely_target->noninterposable_alias ();
+ cgraph_node *alias = dyn_cast<cgraph_node *> (sn);
+ if (alias)
+ likely_target = alias;
+ }
+ gimple *new_call;
+ if (flag_icp_speculatively)
+ {
+ e->make_speculative (likely_target, e->count.apply_scale (5, 10));
+ new_call = e->call_stmt;
+ stats->nspec++;
+ }
+ else
+ {
+ cgraph_edge *e2 = cgraph_edge::make_direct (e, likely_target);
+ new_call = cgraph_edge::redirect_call_stmt_to_callee (e2);
+ stats->nsubst++;
+ }
+ if (dump_file)
+ {
+ fprintf (dump_file, "The call is substituted by: ");
+ print_gimple_stmt (dump_file, new_call, 0);
+ fprintf (dump_file, "\n");
+ }
+}
+
+/* Find functions which are called only indirectly and if they are not in
+ fs_map, they can be removed. For now it is used only to print stats. */
+
+static int
+find_functions_can_be_removed (type_set &unreachable)
+{
+ int nremove = 0;
+ if (dump_file)
+ fprintf (dump_file, "\nRemove unused functions:\n");
+ struct cgraph_node *n;
+ FOR_EACH_FUNCTION (n)
+ {
+ gcc_assert (n->decl && TREE_TYPE (n->decl));
+ if (n->callers != NULL)
+ continue;
+ tree ftype = TREE_TYPE (n->decl);
+ tree ctype = TYPE_CANONICAL (ftype);
+ if (!ctype || !unreachable.count (TYPE_UID (ctype))
+ || unsafe_types->count (TYPE_UID (ftype))
+ || TREE_CODE (ftype) == METHOD_TYPE || n->callers != NULL
+ || !n->definition || n->alias || n->thunk || n->clones)
+ continue;
+ if (dump_file)
+ fprintf (dump_file, "%s is not used\n", n->dump_name ());
+ nremove++;
+ }
+ return nremove;
+}
+
+static void
+dump_stats (struct icp_stats &st)
+{
+ fprintf (dump_file, "\nSTATS: %i candidates for indirect call promotion,"
+ " %i substituted, %i speculatively promoted, %i cold\n"
+ "%i have multiple targets, %i already speculated, %i external,"
+ " %i not defined, %i artificial, %i polymorphic calls,"
+ " %i overwritable\n", st.nicp, st.nsubst, st.nspec, st.ncold,
+ st.nmultiple, st.nspeculated, st.nexternal, st.nnotdefined,
+ st.nartificial, st.npolymorphic, st.noverwritable);
+ if (!(dump_flags & TDF_STATS))
+ return;
+ fprintf (dump_file, "EXTRA STATS: %i functions, %i indirect calls,"
+ " %i total calls, %i called only indirectly, %i may be removed\n"
+ "Indirect call sites with found targets ", st.nf, st.nindir,
+ st.ncalls, st.nind_only, st.nremove);
+ for (unsigned i = 0; i < MAX_TARG_STAT; i++)
+ fprintf (dump_file, "%u:%i, ", i + 1, st.ntargs[i]);
+ fprintf (dump_file, "more:%i\n", st.ntargs[MAX_TARG_STAT]);
+}
+
+/* Optimize indirect calls. When an indirect call has only one target,
+ promote it into a direct call. */
+
+static bool
+optimize_indirect_calls ()
+{
+ /* TODO: maybe move to the top of ipa_icp. */
+ if (has_address_taken_functions_with_varargs)
+ {
+ if (dump_file)
+ fprintf (dump_file, "\n\nAddress taken function with varargs is found."
+ " Skip the optimization.\n");
+ return false;
+ }
+ struct icp_stats stats = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0}};
+ /* At first assume all function types are unreadchable. */
+ type_set unreachable_ftypes;
+ if (dump_file && (dump_flags & TDF_STATS))
+ for (type_decl_map::iterator it = fs_map->begin ();
+ it != fs_map->end (); ++it)
+ unreachable_ftypes.insert (it->first);
+
+ struct cgraph_node *n;
+ FOR_EACH_DEFINED_FUNCTION (n)
+ {
+ if (dump_file)
+ dump_processing_function (n, stats);
+ struct cgraph_edge *e;
+ bool update = false;
+ if (!opt_for_fn (n->decl, flag_icp) || !n->has_gimple_body_p ()
+ || n->inlined_to || !n->indirect_calls)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Skip the function\n");
+ continue;
+ }
+ /* If the function has indirect calls which are not polymorphic,
+ process its body, otherwise continue. */
+ bool non_polymorphic_calls = false;
+ for (e = n->indirect_calls; e; e = e->next_callee)
+ if (!e->indirect_info->polymorphic)
+ {
+ non_polymorphic_calls = true;
+ break;
+ }
+ if (!non_polymorphic_calls)
+ {
+ if (dump_file)
+ fprintf (dump_file, "All indirect calls are polymorphic,"
+ "skip...\n");
+ continue;
+ }
+ /* Get the function body to operate with call statements. */
+ n->get_body ();
+ /* Walk indirect call sites and apply the optimization. */
+ cgraph_edge *next;
+ for (e = n->indirect_calls; e; e = next)
+ {
+ next = e->next_callee;
+ if (e->indirect_info->polymorphic)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Target is polymorphic, skip...\n\n");
+ stats.npolymorphic++;
+ continue;
+ }
+ stats.nicp++;
+ struct cgraph_node *likely_target = NULL;
+ gcall *stmt = e->call_stmt;
+ gcc_assert (stmt != NULL);
+ tree call_fn = gimple_call_fn (stmt);
+ tree call_fn_ty = TREE_TYPE (call_fn);
+ if (dump_file)
+ dump_indirect_call_site (call_fn, call_fn_ty);
+ tree decl = NULL_TREE;
+ if (POINTER_TYPE_P (call_fn_ty))
+ {
+ if (dump_file)
+ dump_type_with_uid ("Pointee type: ", TREE_TYPE (call_fn_ty));
+ if (dump_file && (dump_flags & TDF_STATS))
+ erase_from_unreachable (TYPE_UID (TREE_TYPE (call_fn_ty)),
+ unreachable_ftypes);
+ /* Try to use the signature analysis results. */
+ tree ctype = TYPE_CANONICAL (TREE_TYPE (call_fn_ty));
+ unsigned ctype_uid = ctype ? TYPE_UID (ctype) : 0;
+ if (ctype_uid && fs_map->count (ctype_uid))
+ {
+ if (dump_flags && (dump_flags & TDF_STATS))
+ erase_from_unreachable (ctype_uid, unreachable_ftypes);
+ decl_set *decls = (*fs_map)[ctype_uid];
+ if (dump_file)
+ dump_found_fdecls (decls, ctype_uid);
+ /* TODO: optimize for multple targets. */
+ if (!unsafe_types->count (ctype_uid) && decls->size () == 1)
+ {
+ decl = *(decls->begin ());
+ likely_target = cgraph_node::get (decl);
+ }
+ if (!unsafe_types->count (ctype_uid)
+ && (dump_flags & TDF_STATS))
+ count_found_targets (stats, decls->size ());
+ }
+ }
+ if (!decl || !likely_target)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Callee is unknown\n\n");
+ continue;
+ }
+ if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Callee is method\n\n");
+ continue;
+ }
+ if (e->speculative)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Call is already speculated\n\n");
+ stats.nspeculated++;
+ continue;
+ }
+ if (!likely_target->definition)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Target is not a definition\n\n");
+ stats.nnotdefined++;
+ continue;
+ }
+ /* Do not introduce new references to external symbols. While we
+ can handle these just well, it is common for programs to
+ incorrectly with headers defining methods they are linked
+ with. */
+ if (DECL_EXTERNAL (likely_target->decl))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Target is external\n\n");
+ stats.nexternal++;
+ continue;
+ }
+ /* Don't use an implicitly-declared destructor (c++/58678). */
+ struct cgraph_node *non_thunk_target
+ = likely_target->function_symbol ();
+ if (DECL_ARTIFICIAL (non_thunk_target->decl))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Target is artificial\n\n");
+ stats.nartificial++;
+ continue;
+ }
+ if (likely_target->get_availability () <= AVAIL_INTERPOSABLE
+ && likely_target->can_be_discarded_p ())
+ {
+ if (dump_file)
+ fprintf (dump_file, "Target is overwritable\n\n");
+ stats.noverwritable++;
+ continue;
+ }
+ else if (dbg_cnt (icp))
+ {
+ promote_call (e, n, likely_target, &stats);
+ update = true;
+ }
+ }
+ if (update)
+ ipa_update_overall_fn_summary (n);
+ }
+
+ if (dump_file && (dump_flags & TDF_STATS))
+ stats.nremove = find_functions_can_be_removed (unreachable_ftypes);
+
+ if (dump_file)
+ dump_stats (stats);
+ return stats.nsubst || stats.nspec;
+}
+
+/* Delete the given MAP with allocated sets. One set may be associated with
+ more then one type/decl. */
+
+template <typename MAP>
+static void
+remove_type_alias_map (MAP *map)
+{
+ std::set<typename MAP::mapped_type> processed_sets;
+ for (typename MAP::iterator it = map->begin (); it != map->end (); it++)
+ {
+ typename MAP::mapped_type set = it->second;
+ if (processed_sets.count (set) != 0)
+ continue;
+ processed_sets.insert (set);
+ delete set;
+ }
+ delete map;
+}
+
+/* The ipa indirect call promotion pass. Run required analysis and optimize
+ indirect calls.
+ When indirect call has only one target, promote it into a direct call. */
+
+static unsigned int
+ipa_icp (void)
+{
+ ta_map = new type_alias_map;
+ fta_map = new type_alias_map;
+ cbase_to_ptype = new type_alias_map;
+ fs_map = new type_decl_map;
+ ctype_map = new type_map;
+ unsafe_types = new type_set;
+ type_uid_map = new uid_to_type_map;
+
+ /* Find type aliases, fill the function signature map and
+ optimize indirect calls. */
+ collect_function_type_aliases ();
+ collect_function_signatures ();
+ bool optimized = optimize_indirect_calls ();
+
+ remove_type_alias_map (ta_map);
+ remove_type_alias_map (fta_map);
+ remove_type_alias_map (cbase_to_ptype);
+ remove_type_alias_map (fs_map);
+ delete ctype_map;
+ delete unsafe_types;
+ delete type_uid_map;
+
+ return optimized ? TODO_remove_functions : 0;
+}
+
+namespace {
+
+const pass_data pass_data_ipa_icp =
+{
+ IPA_PASS, /* type */
+ "icp", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_IPA_ICP, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_ipa_icp : public ipa_opt_pass_d
+{
+public:
+ pass_ipa_icp (gcc::context *ctxt)
+ : ipa_opt_pass_d (pass_data_ipa_icp, ctxt,
+ NULL, /* generate_summary */
+ NULL, /* write_summary */
+ NULL, /* read_summary */
+ NULL, /* write_optimization_summary */
+ NULL, /* read_optimization_summary */
+ NULL, /* stmt_fixup */
+ 0, /* function_transform_todo_flags_start */
+ NULL, /* function_transform */
+ NULL) /* variable_transform */
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *)
+ {
+ return (optimize && flag_icp && !seen_error ()
+ && (in_lto_p || flag_whole_program));
+ }
+
+ virtual unsigned int execute (function *) { return ipa_icp (); }
+
+}; // class pass_ipa_icp
+
+} // anon namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icp (gcc::context *ctxt)
+{
+ return new pass_ipa_icp (ctxt);
+}
#include "gt-ipa-devirt.h"
diff --git a/gcc/passes.def b/gcc/passes.def
index 9692066e4..d6db9be6e 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -156,6 +156,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_ipa_profile);
NEXT_PASS (pass_ipa_icf);
NEXT_PASS (pass_ipa_devirt);
+ NEXT_PASS (pass_ipa_icp);
NEXT_PASS (pass_ipa_cp);
NEXT_PASS (pass_ipa_sra);
NEXT_PASS (pass_ipa_cdtor_merge);
diff --git a/gcc/testsuite/gcc.dg/icp1.c b/gcc/testsuite/gcc.dg/icp1.c
new file mode 100644
index 000000000..c2117f738
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/icp1.c
@@ -0,0 +1,40 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -flto -ficp -fdump-ipa-icp=./icp1.c.077i.icp" } */
+
+int dummy = 0;
+
+typedef int (*ftype1)(int a);
+typedef float (*ftype2)(int a);
+
+ftype1 func1;
+
+struct {
+ int a;
+ int* b;
+ ftype1 myf1;
+ ftype2 myf2;
+} my_str;
+
+int foo(int a) {
+ my_str.myf1 = func1;
+ if (a % 2 == 0)
+ dummy += dummy % (dummy - a);
+ return a + 1;
+}
+
+float bar(int a) {
+ my_str.myf2 = &bar;
+ func1 = &foo;
+ return foo(a);
+}
+
+int main() {
+ bar(1);
+ my_str.myf2(3);
+ return (my_str.myf1(2) + func1(4)) != 8;
+}
+
+/* { dg-final { scan-ipa-dump "The call is substituted by:.*= foo \\(4\\);" "icp" } } */
+/* { dg-final { scan-ipa-dump "The call is substituted by:.*= foo \\(2\\);" "icp" } } */
+/* { dg-final { scan-ipa-dump "The call is substituted by: bar \\(3\\);" "icp" } } */
+/* { dg-final { scan-ipa-dump "STATS: 3 candidates for indirect call promotion, 3 substituted, 0 speculatively promoted, 0 cold" "icp" } } */
diff --git a/gcc/testsuite/gcc.dg/icp2.c b/gcc/testsuite/gcc.dg/icp2.c
new file mode 100644
index 000000000..03d31d407
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/icp2.c
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -flto -ficp -fdump-ipa-icp=./icp2.c.077i.icp" } */
+
+int dummy = 0;
+
+typedef int (*ftype1)(int a);
+typedef float (*ftype2)(int a);
+
+ftype1 func1;
+
+struct {
+ int a;
+ int* b;
+ ftype1 myf1;
+ ftype2 myf2;
+} my_str;
+
+int foo(int a) {
+ my_str.myf1 = func1;
+ if (a % 2 == 0)
+ dummy += dummy % (dummy - a);
+ return a + 1;
+}
+
+float bar(int a) {
+ my_str.myf2 = dummy ? (ftype2) &foo : &bar;
+ func1 = (ftype1) &bar;
+ return foo(a);
+}
+
+int main() {
+ bar(1);
+ my_str.myf2(3);
+ return (my_str.myf1(2) + func1(4)) != 8;
+}
+
+/* { dg-final { scan-ipa-dump-not "The call is substituted by.*" "icp" } } */
+/* { dg-final { scan-ipa-dump "STATS: 3 candidates for indirect call promotion, 0 substituted, 0 speculatively promoted, 0 cold" "icp" } } */
diff --git a/gcc/testsuite/gcc.dg/icp3.c b/gcc/testsuite/gcc.dg/icp3.c
new file mode 100644
index 000000000..2a7d1e6f5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/icp3.c
@@ -0,0 +1,52 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -flto -ficp -fdump-ipa-icp=./icp3.c.077i.icp" } */
+
+#include <stdio.h>
+
+int dummy = 0;
+
+typedef int (*ftype1)(int a);
+typedef float (*ftype2)(int a);
+typedef ftype1 (*ftype3) (ftype2);
+
+ftype1 func1;
+
+struct {
+ int a;
+ int* b;
+ ftype1 myf1;
+ ftype2 myf2;
+ ftype3 myf3;
+} my_str;
+
+ftype1 boo(ftype2 a) {
+ printf ("Call boo\n");
+ return (ftype1) a;
+}
+
+int foo(int a) {
+ printf ("Call foo\n");
+ my_str.myf1 = func1;
+ if (a % 2 == 0)
+ dummy += dummy % (dummy - a);
+ return a + 1;
+}
+
+float bar(int a) {
+ printf("Call bar\n");
+ my_str.myf2 = (ftype2) my_str.myf3((ftype2) foo);
+ func1 = &foo;
+ return foo(a);
+}
+
+int main() {
+ my_str.myf3 = &boo;
+ bar(1);
+ my_str.myf2(3);
+ return (my_str.myf1(2) + func1(4)) != 8;
+}
+
+/* { dg-final { scan-ipa-dump "The call is substituted by:.*= foo \\(4\\);" "icp" } } */
+/* { dg-final { scan-ipa-dump "The call is substituted by:.*= foo \\(2\\);" "icp" } } */
+/* { dg-final { scan-ipa-dump "The call is substituted by: foo \\(3\\);" "icp" } } */
+/* { dg-final { scan-ipa-dump "STATS: 4 candidates for indirect call promotion, 3 substituted, 0 speculatively promoted, 0 cold" "icp" } } */
diff --git a/gcc/testsuite/gcc.dg/icp4.c b/gcc/testsuite/gcc.dg/icp4.c
new file mode 100644
index 000000000..e3e1d5116
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/icp4.c
@@ -0,0 +1,55 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -flto -ficp -fdump-ipa-icp=./icp4.c.077i.icp" } */
+
+#include <stdio.h>
+
+int dummy = 0;
+
+typedef int (*ftype1)(int a);
+typedef float (*ftype2)(int a);
+typedef ftype1 (*ftype3) (ftype2);
+
+ftype1 func1;
+ftype1 boo(ftype2 a);
+int foo(int a);
+float bar(int a);
+
+typedef struct {
+ int a;
+ int* b;
+ ftype1 myf1;
+ ftype2 myf2;
+ ftype3 myf3;
+} T;
+
+T my_str = {0, (int*) &dummy, (ftype1) &boo, (ftype2) &foo, (ftype3) &bar};
+
+ftype1 boo(ftype2 a) {
+ printf ("Call boo\n");
+ return (ftype1) a;
+}
+
+int foo(int a) {
+ printf ("Call foo\n");
+ my_str.myf1 = func1;
+ if (a % 2 == 0)
+ dummy += dummy % (dummy - a);
+ return a + 1;
+}
+
+float bar(int a) {
+ printf("Call bar\n");
+ my_str.myf2 = (ftype2) my_str.myf3((ftype2) foo);
+ func1 = &foo;
+ return foo(a);
+}
+
+int main() {
+ my_str.myf3 = &boo;
+ bar(1);
+ my_str.myf2(3);
+ return (my_str.myf1(2) + func1(4)) != 8;
+}
+
+/* { dg-final { scan-ipa-dump-not "The call is substituted by.*" "icp" } } */
+/* { dg-final { scan-ipa-dump "STATS: 4 candidates for indirect call promotion, 0 substituted, 0 speculatively promoted, 0 cold" "icp" } } */
diff --git a/gcc/testsuite/gcc.dg/icp5.c b/gcc/testsuite/gcc.dg/icp5.c
new file mode 100644
index 000000000..c7709243c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/icp5.c
@@ -0,0 +1,66 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -flto -ficp -fdump-ipa-icp=./icp5.c.077i.icp" } */
+
+#include <stdio.h>
+
+int dummy = 0;
+
+typedef int (*ftype1)(int a);
+typedef float (*ftype2)(int a);
+typedef ftype1 (*ftype3) (ftype2);
+
+ftype1 func1;
+ftype1 boo(ftype2 a);
+int foo(int a);
+float bar(int a);
+
+typedef struct {
+ int a;
+ int* b;
+ ftype1 myf1;
+ ftype2 myf2;
+ ftype3 myf3;
+} T;
+
+T my_str;
+
+typedef struct {
+ int a;
+ int* b;
+ ftype3 myf1;
+ ftype2 myf2;
+ ftype1 myf3;
+} T1;
+
+T1 my1 = {0, &dummy, boo, &bar, &foo};
+
+ftype1 boo(ftype2 a) {
+ printf("Call boo\n");
+ return (ftype1) a;
+}
+
+int foo(int a) {
+ printf("Call foo\n");
+ my_str.myf1 = func1;
+ if (a % 2 == 0)
+ dummy += dummy % (dummy - a);
+ return a + 1;
+}
+
+float bar(int a) {
+ printf("Call bar\n");
+ my_str.myf2 = (ftype2) my_str.myf3((ftype2) foo);
+ func1 = &foo;
+ return foo(a);
+}
+
+int main() {
+ my_str = *(T*)&my1;
+ my_str.myf3 = &boo;
+ bar(1);
+ my_str.myf2(3);
+ return (my_str.myf1(2) + func1(4)) != 8;
+}
+
+/* { dg-final { scan-ipa-dump-not "The call is substituted by.*" "icp" } } */
+/* { dg-final { scan-ipa-dump "STATS: 4 candidates for indirect call promotion, 0 substituted, 0 speculatively promoted, 0 cold" "icp" } } */
diff --git a/gcc/testsuite/gcc.dg/icp6.c b/gcc/testsuite/gcc.dg/icp6.c
new file mode 100644
index 000000000..5a9f15045
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/icp6.c
@@ -0,0 +1,66 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -flto -ficp -fdump-ipa-icp=./icp6.c.077i.icp -Wno-int-conversion -Wno-incompatible-pointer-types" } */
+int dummy = 0;
+
+typedef int (*ftype1)(int a);
+typedef float (*ftype2)(int a);
+typedef int (*ftype3)();
+typedef int (*ftype4)(int a, int b);
+
+ftype1 func1;
+ftype4 func2;
+
+struct {
+ int a;
+ int* b;
+ ftype1 myf1;
+ ftype2 myf2;
+ ftype3 myf3;
+} my_str;
+
+int foo3(float a) {
+ return dummy;
+}
+
+int foo4(int a, int b) {
+ return a*b;
+}
+
+int foo(int a) {
+ my_str.myf1 = func1;
+ if (a % 2 == 0)
+ dummy += dummy % (dummy - a);
+ return a + 1;
+}
+
+int foo2(float a) {
+ func1 = (ftype1) &foo;
+ func2 = &foo4;
+ return dummy + foo3 (a);
+}
+
+float bar2(int a) {
+ my_str.myf2 = (ftype2)(0x864213);
+ func2 = 0x65378;
+ return foo(a);
+}
+
+float bar(int a) {
+ my_str.myf3 = &foo2;
+ my_str.myf2 = &bar;
+ func1 = (ftype1) &dummy;
+ func2 = (ftype4) &bar2;
+ return foo(a);
+}
+
+int main() {
+ bar(1);
+ bar2(1);
+ bar(0);
+ my_str.myf2(3);
+ ((ftype1) my_str.myf3)(0.0);
+ int sum = func1(4);
+ return (sum + my_str.myf1(2) + func2(5, 6)) != 38;
+}
+/* { dg-final { scan-ipa-dump "The call is substituted by.*foo2 \\(0\\);" "icp" } } */
+/* { dg-final { scan-ipa-dump "STATS: 5 candidates for indirect call promotion, 1 substituted, 0 speculatively promoted, 0 cold" "icp" } } */
diff --git a/gcc/testsuite/gcc.dg/icp7.c b/gcc/testsuite/gcc.dg/icp7.c
new file mode 100644
index 000000000..fa52197f4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/icp7.c
@@ -0,0 +1,48 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -flto -ficp -fdump-ipa-icp=./icp7.c.077i.icp" } */
+
+#include <stdarg.h>
+
+int dummy = 0;
+
+typedef int (*ftype1)(int a);
+typedef float (*ftype2)(int a);
+
+ftype1 func1;
+
+struct {
+ int a;
+ int* b;
+ ftype1 myf1;
+ ftype2 myf2;
+} my_str;
+
+int boo(int a, ...) {
+ va_list ap;
+ va_start(ap, a);
+ if (a == 0)
+ dummy += va_arg(ap, int);
+ va_end(ap);
+ return dummy;
+}
+
+int foo(int a) {
+ my_str.myf1 = func1;
+ if (a % 2 == 0)
+ dummy += dummy % (dummy - a);
+ return a + 1;
+}
+
+float bar(int a) {
+ my_str.myf2 = &bar;
+ func1 = (ftype1) &boo;
+ return foo(a);
+}
+
+int main() {
+ bar(1);
+ my_str.myf2(3);
+ return (my_str.myf1(2) + func1(4));
+}
+
+/* { dg-final { scan-ipa-dump "Address taken function with varargs is found. Skip the optimization." "icp" } } */
diff --git a/gcc/timevar.def b/gcc/timevar.def
index 98a5a490f..ca4156066 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -71,6 +71,7 @@ DEFTIMEVAR (TV_CGRAPHOPT , "callgraph optimization")
DEFTIMEVAR (TV_CGRAPH_FUNC_EXPANSION , "callgraph functions expansion")
DEFTIMEVAR (TV_CGRAPH_IPA_PASSES , "callgraph ipa passes")
DEFTIMEVAR (TV_IPA_ODR , "ipa ODR types")
+DEFTIMEVAR (TV_IPA_ICP , "ipa indirect call promotion")
DEFTIMEVAR (TV_IPA_FNSUMMARY , "ipa function summary")
DEFTIMEVAR (TV_IPA_UNREACHABLE , "ipa dead code removal")
DEFTIMEVAR (TV_IPA_INHERITANCE , "ipa inheritance graph")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 56898e019..5f09e4f8b 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -524,6 +524,7 @@ extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_sra (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icp (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_odr (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
--
2.33.0
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。