diff options
Diffstat (limited to '0040-Port-icp-patch-to-GCC-12.patch')
-rw-r--r-- | 0040-Port-icp-patch-to-GCC-12.patch | 2387 |
1 files changed, 2387 insertions, 0 deletions
diff --git a/0040-Port-icp-patch-to-GCC-12.patch b/0040-Port-icp-patch-to-GCC-12.patch new file mode 100644 index 0000000..d0b3412 --- /dev/null +++ b/0040-Port-icp-patch-to-GCC-12.patch @@ -0,0 +1,2387 @@ +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 + |