From 37c1aa0a69f8a99e0c56d5336718c4fe7fbf1b16 Mon Sep 17 00:00:00 2001 From: Xiong Zhou Date: Thu, 28 Dec 2023 10:42:13 +0800 Subject: [PATCH] Revert ICP and IPA prefetch related patches and sync patch --- 0156-add-icp-optimization.patch | 2387 ----------------- ...-Implement-IPA-prefetch-optimization.patch | 2072 -------------- ...ix-some-bugs-and-remove-variable-pre.patch | 2 +- 0173-IPA-Fix-test-completion-1.c.patch | 24 - ...-checked-build-and-comments-from-rev.patch | 71 - ...-ICP-src-openEuler-gcc-I8PYBF-I8PYLL.patch | 61 - 0177-Fix-sqlite-build.patch | 168 -- 0178-Fix-freetype-build.patch | 52 - 0180-Fix-issue-I8QD9H.patch | 115 - ...zation-level-requirement-to-the-gate.patch | 25 + gcc.spec | 25 +- 11 files changed, 35 insertions(+), 4967 deletions(-) delete mode 100644 0156-add-icp-optimization.patch delete mode 100644 0158-Implement-IPA-prefetch-optimization.patch delete mode 100644 0173-IPA-Fix-test-completion-1.c.patch delete mode 100644 0174-IPA-Fix-fails-on-checked-build-and-comments-from-rev.patch delete mode 100644 0176-Fix-bugs-in-ICP-src-openEuler-gcc-I8PYBF-I8PYLL.patch delete mode 100644 0177-Fix-sqlite-build.patch delete mode 100644 0178-Fix-freetype-build.patch delete mode 100644 0180-Fix-issue-I8QD9H.patch create mode 100644 0180-add-optimization-level-requirement-to-the-gate.patch diff --git a/0156-add-icp-optimization.patch b/0156-add-icp-optimization.patch deleted file mode 100644 index efdbcb0..0000000 --- a/0156-add-icp-optimization.patch +++ /dev/null @@ -1,2387 +0,0 @@ -From f1c357fc742e94b3d4a7123253db64d7ebac304f Mon Sep 17 00:00:00 2001 -From: Diachkov Ilia WX1215920 -Date: Mon, 12 Jun 2023 01:43:24 +0300 -Subject: [PATCH 3/6] add icp optimization - ---- - gcc/common.opt | 8 + - gcc/dbgcnt.def | 1 + - gcc/ipa-devirt.c | 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 6f0ed7cea..5a9e9c479 100644 ---- a/gcc/common.opt -+++ b/gcc/common.opt -@@ -1270,6 +1270,14 @@ fdevirtualize - Common Report Var(flag_devirtualize) Optimization - Try to convert virtual calls to direct ones. - -+ficp -+Common Report Var(flag_icp) Optimization Init(0) -+Try to promote indirect calls to direct ones. -+ -+ficp-speculatively -+Common Report 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 232b19289..9d38a094b 100644 ---- a/gcc/dbgcnt.def -+++ b/gcc/dbgcnt.def -@@ -166,6 +166,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.c b/gcc/ipa-devirt.c -index 3ab704973..79466d91d 100644 ---- a/gcc/ipa-devirt.c -+++ b/gcc/ipa-devirt.c -@@ -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" -@@ -4369,5 +4375,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 type_set; -+typedef std::set decl_set; -+typedef std::map type_alias_map; -+typedef std::map type_decl_map; -+typedef std::map uid_to_type_map; -+typedef std::map 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 &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 tlist1, std::list tlist2) -+{ -+ for (std::list::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 tlist1; -+ std::list 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 &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 tlist1; -+ std::list 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 *canonical_type_hash_cache = NULL; -+static std::map *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 hash_to_ftype; -+ type_set *incomplete_hash_ftype = new type_set; -+ canonical_type_hash_cache = new std::map; -+ icp_canonical_types = new std::map; -+ -+ 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 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 (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.thunk_p || 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 -+static void -+remove_type_alias_map (MAP *map) -+{ -+ std::set 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 ea50db086..2685018cd 100644 ---- a/gcc/passes.def -+++ b/gcc/passes.def -@@ -152,6 +152,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 -+ -+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 -+ -+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 -+ -+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 -+ -+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 2814b14f2..e12b0e50d 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 3cdc12466..f41c2692d 100644 ---- a/gcc/tree-pass.h -+++ b/gcc/tree-pass.h -@@ -506,6 +506,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_hsa (gcc::context *ctxt); --- -2.33.0 - diff --git a/0158-Implement-IPA-prefetch-optimization.patch b/0158-Implement-IPA-prefetch-optimization.patch deleted file mode 100644 index 7661117..0000000 --- a/0158-Implement-IPA-prefetch-optimization.patch +++ /dev/null @@ -1,2072 +0,0 @@ -From 0a83ceb119be0f1a1e061b3864752e783ec2c680 Mon Sep 17 00:00:00 2001 -From: Diachkov Ilia WX1215920 -Date: Tue, 12 Dec 2023 10:29:17 +0800 -Subject: [PATCH 5/6] Implement IPA prefetch optimization - ---- - gcc/Makefile.in | 1 + - gcc/cgraph.c | 1 + - gcc/cgraph.h | 2 + - gcc/common.opt | 8 + - gcc/ipa-devirt.c | 54 +- - gcc/ipa-prefetch.c | 1820 ++++++++++++++++++++++++++++++++++++++++++++ - gcc/ipa-sra.c | 8 + - gcc/params.opt | 8 + - gcc/passes.def | 1 + - gcc/timevar.def | 1 + - gcc/tree-pass.h | 1 + - 11 files changed, 1903 insertions(+), 2 deletions(-) - create mode 100644 gcc/ipa-prefetch.c - -diff --git a/gcc/Makefile.in b/gcc/Makefile.in -index 31bf2cde2..bcb9305a0 100644 ---- a/gcc/Makefile.in -+++ b/gcc/Makefile.in -@@ -1411,6 +1411,7 @@ OBJS = \ - ipa-inline-analysis.o \ - ipa-inline-transform.o \ - ipa-predicate.o \ -+ ipa-prefetch.o \ - ipa-profile.o \ - ipa-prop.o \ - ipa-param-manipulation.o \ -diff --git a/gcc/cgraph.c b/gcc/cgraph.c -index 4a44b4a48..57146bf8c 100644 ---- a/gcc/cgraph.c -+++ b/gcc/cgraph.c -@@ -988,6 +988,7 @@ cgraph_node::create_indirect_edge (gcall *call_stmt, int ecf_flags, - edge->indirect_info = cgraph_allocate_init_indirect_info (); - edge->indirect_info->ecf_flags = ecf_flags; - edge->indirect_info->vptr_changed = true; -+ edge->indirect_info->targets = NULL; - - /* Record polymorphic call info. */ - if (!cloning_p -diff --git a/gcc/cgraph.h b/gcc/cgraph.h -index 8e71af072..0b2bb1e82 100644 ---- a/gcc/cgraph.h -+++ b/gcc/cgraph.h -@@ -1710,6 +1710,8 @@ public: - int param_index; - /* ECF flags determined from the caller. */ - int ecf_flags; -+ /* Vector of potential call targets determined by analysis. */ -+ vec *targets; - - /* Number of speculative call targets, it's less than GCOV_TOPN_VALUES. */ - unsigned num_speculative_call_targets : 16; -diff --git a/gcc/common.opt b/gcc/common.opt -index f2c53cc31..cb5854463 100644 ---- a/gcc/common.opt -+++ b/gcc/common.opt -@@ -1274,6 +1274,10 @@ fdevirtualize - Common Report Var(flag_devirtualize) Optimization - Try to convert virtual calls to direct ones. - -+fipa-ic -+Common Report Var(flag_ipa_ic) Optimization Init(0) -+Perform interprocedural analysis of indirect calls. -+ - ficp - Common Report Var(flag_icp) Optimization Init(0) - Try to promote indirect calls to direct ones. -@@ -2245,6 +2249,10 @@ fllc-allocate - Common Report Var(flag_llc_allocate) Init(-1) Optimization - Generate LLC hint instructions. - -+fipa-prefetch -+Common Report Var(flag_ipa_prefetch) Init(0) Optimization -+Generate prefetch instructions, if available, using IPA info. -+ - fprofile - Common Report Var(profile_flag) - Enable basic program profiling code. -diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c -index 79466d91d..fbde7eb94 100644 ---- a/gcc/ipa-devirt.c -+++ b/gcc/ipa-devirt.c -@@ -5703,6 +5703,54 @@ merge_fs_map_for_ftype_aliases () - } - } - -+/* Save results of indirect call analysis for the next passes. */ -+ -+static void -+save_analysis_results () -+{ -+ if (dump_file) -+ fprintf (dump_file, "\n\nSave results of indirect call analysis.\n"); -+ -+ struct cgraph_node *n; -+ FOR_EACH_FUNCTION (n) -+ { -+ cgraph_edge *e, *next; -+ for (e = n->indirect_calls; e; e = next) -+ { -+ next = e->next_callee; -+ if (e->indirect_info->polymorphic) -+ continue; -+ 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 (!POINTER_TYPE_P (call_fn_ty)) -+ continue; -+ -+ tree ctype = TYPE_CANONICAL (TREE_TYPE (call_fn_ty)); -+ unsigned ctype_uid = ctype ? TYPE_UID (ctype) : 0; -+ if (!ctype_uid || unsafe_types->count (ctype_uid) -+ || !fs_map->count (ctype_uid)) -+ continue; -+ /* TODO: cleanup noninterposable aliases. */ -+ decl_set *decls = (*fs_map)[ctype_uid]; -+ if (dump_file) -+ { -+ fprintf (dump_file, "For call "); -+ print_gimple_stmt (dump_file, stmt, 0); -+ } -+ vec_alloc (e->indirect_info->targets, decls->size ()); -+ for (decl_set::const_iterator it = decls->begin (); -+ it != decls->end (); it++) -+ { -+ struct cgraph_node *target = cgraph_node::get (*it); -+ /* TODO: maybe discard some targets. */ -+ e->indirect_info->targets->quick_push (target); -+ } -+ } -+ } -+} -+ - /* Dump function types with set of functions corresponding to it. */ - - static void -@@ -5767,6 +5815,8 @@ collect_function_signatures () - } - } - merge_fs_map_for_ftype_aliases (); -+ if (flag_ipa_ic) -+ save_analysis_results (); - if (dump_file) - dump_function_signature_sets (); - } -@@ -6162,7 +6212,7 @@ ipa_icp (void) - optimize indirect calls. */ - collect_function_type_aliases (); - collect_function_signatures (); -- bool optimized = optimize_indirect_calls (); -+ bool optimized = flag_icp ? optimize_indirect_calls () : false; - - remove_type_alias_map (ta_map); - remove_type_alias_map (fta_map); -@@ -6209,7 +6259,7 @@ public: - /* opt_pass methods: */ - virtual bool gate (function *) - { -- return (optimize && flag_icp && !seen_error () -+ return (optimize && (flag_icp || flag_ipa_ic) && !seen_error () - && (in_lto_p || flag_whole_program)); - } - -diff --git a/gcc/ipa-prefetch.c b/gcc/ipa-prefetch.c -new file mode 100644 -index 000000000..93483a6e8 ---- /dev/null -+++ b/gcc/ipa-prefetch.c -@@ -0,0 +1,1820 @@ -+/* IPA prefetch optimizations. -+ Copyright (C) 2023 Free Software Foundation, Inc. -+ Contributed by Ilia Diachkov. -+ -+This file is part of GCC. -+ -+GCC is free software; you can redistribute it and/or modify it under -+the terms of the GNU General Public License as published by the Free -+Software Foundation; either version 3, or (at your option) any later -+version. -+ -+GCC is distributed in the hope that it will be useful, but WITHOUT ANY -+WARRANTY; without even the implied warranty of MERCHANTABILITY or -+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -+for more details. -+ -+You should have received a copy of the GNU General Public License -+along with GCC; see the file COPYING3. If not see -+. */ -+ -+/* IPA prefetch is an interprocedural pass that detects cases of indirect -+ memory access potentially in loops and inserts prefetch instructions -+ to optimize cache usage during these indirect memory accesses. */ -+ -+#include "config.h" -+#define INCLUDE_SET -+#define INCLUDE_MAP -+#include "system.h" -+#include "coretypes.h" -+#include "target.h" -+#include "tm.h" -+#include "tree.h" -+#include "tree-pass.h" -+#include "cgraph.h" -+#include "diagnostic-core.h" -+#include "function.h" -+#include "basic-block.h" -+#include "gimple.h" -+#include "vec.h" -+#include "tree-pretty-print.h" -+#include "gimple-pretty-print.h" -+#include "gimple-iterator.h" -+#include "gimple-walk.h" -+#include "cfg.h" -+#include "cfghooks.h" -+#include "ssa.h" -+#include "tree-dfa.h" -+#include "fold-const.h" -+#include "tree-inline.h" -+#include "stor-layout.h" -+#include "tree-into-ssa.h" -+#include "tree-cfg.h" -+#include "alloc-pool.h" -+#include "symbol-summary.h" -+#include "ipa-prop.h" -+#include "tree-eh.h" -+#include "bitmap.h" -+#include "cfgloop.h" -+#include "langhooks.h" -+#include "ipa-param-manipulation.h" -+#include "ipa-fnsummary.h" -+#include "tree-ssa-loop.h" -+#include "tree-ssa-loop-ivopts.h" -+#include "gimple-fold.h" -+#include "gimplify.h" -+ -+namespace { -+ -+/* Call graph analysis. */ -+ -+typedef std::set edge_set; -+typedef std::set node_set; -+typedef std::map node_to_iedge_map; -+typedef std::map node_to_node_map; -+typedef std::map edge_in_loop; -+typedef std::map node_in_loop; -+ -+static edge_in_loop *el_map = NULL; -+static node_in_loop *nl_map = NULL; -+static node_to_iedge_map *icn_map = NULL; -+/* Contains nodes which reachable from a given node. */ -+static node_to_node_map *nn_map = NULL; -+ -+static bool -+can_be_optimized (cgraph_node *n) -+{ -+ /* TODO: maybe check also inlined_to. */ -+ return opt_for_fn (n->decl, flag_ipa_prefetch) && n->has_gimple_body_p (); -+} -+ -+static void -+analyze_cgraph_edge (cgraph_edge *e) -+{ -+ gcall *stmt = e->call_stmt; -+ gcc_checking_assert (e && stmt); -+ basic_block bb = gimple_bb (stmt); -+ gcc_checking_assert (bb); -+ /* TODO: add the same check for indirect calls. */ -+ if (e->callee && !can_be_optimized (e->callee)) -+ return; -+ -+ if (dump_file) -+ { -+ if (e->callee) -+ fprintf (dump_file, "\t%*s%s %s%*s ", 1, "", -+ e->callee->dump_name (), !e->inline_failed ? "inlined" : -+ cgraph_inline_failed_string (e->inline_failed), 1, ""); -+ else -+ fprintf (dump_file, "\t%*s%s %s%*s ", 1, "", "(indirect)", -+ "n/a", 1, ""); -+ fprintf (dump_file, "freq:%4.2f", e->sreal_frequency ().to_double ()); -+ -+ if (e->callee && cross_module_call_p (e)) -+ fprintf (dump_file, " cross module"); -+ -+ class ipa_call_summary *es = ipa_call_summaries->get (e); -+ if (es) -+ fprintf (dump_file, " loop depth:%2i size:%2i time: %2i", -+ es->loop_depth, es->call_stmt_size, es->call_stmt_time); -+ -+ fprintf (dump_file, "\n"); -+ } -+ if (e->indirect_info && dump_file) -+ { -+ fprintf (dump_file, "II: %p\n", (void *) e->indirect_info->targets); -+ unsigned i = 0; -+ cgraph_node *n; -+ if (e->indirect_info->targets) -+ for (i = 0; e->indirect_info->targets->iterate (i, &n); ++i) -+ fprintf (dump_file, "\t%s\n", n->dump_name ()); -+ } -+ -+ if (bb_loop_depth (bb) == 0) -+ return; -+ -+ if (dump_file) -+ { -+ if (e->callee) -+ fprintf (dump_file, "\tCall in loop (%d): ", bb_loop_depth (bb)); -+ else -+ fprintf (dump_file, "\tICall in loop (%d): ", bb_loop_depth (bb)); -+ print_gimple_stmt (dump_file, stmt, 0); -+ } -+ (*el_map)[e] = e->sreal_frequency ().to_double (); -+} -+ -+/* Walk optimizible cgraph nodes and collect info for edges. */ -+ -+static void -+analyse_cgraph () -+{ -+ cgraph_node *n; -+ cgraph_edge *e; -+ FOR_EACH_DEFINED_FUNCTION (n) -+ { -+ if (dump_file) -+ { -+ fprintf (dump_file, "\n\nProcesing function %s\n", n->dump_name ()); -+ print_generic_expr (dump_file, n->decl); -+ fprintf (dump_file, "\n"); -+ } -+ if (!can_be_optimized (n)) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Skip the function\n"); -+ continue; -+ } -+ -+ /* TODO: maybe remove loop info here. */ -+ push_cfun (DECL_STRUCT_FUNCTION (n->decl)); -+ calculate_dominance_info (CDI_DOMINATORS); -+ loop_optimizer_init (LOOPS_NORMAL); -+ -+ for (e = n->callees; e; e = e->next_callee) -+ analyze_cgraph_edge (e); -+ for (e = n->indirect_calls; e; e = e->next_callee) -+ analyze_cgraph_edge (e); -+ -+ free_dominance_info (CDI_DOMINATORS); -+ loop_optimizer_finalize (); -+ -+ pop_cfun (); -+ } -+} -+ -+/* Save indirect call info to node:icall_target map. */ -+ -+static void -+prepare_indirect_call_info () -+{ -+ cgraph_node *n, *n2; -+ cgraph_edge *e; -+ FOR_EACH_DEFINED_FUNCTION (n) -+ for (e = n->indirect_calls; e; e = e->next_callee) -+ { -+ if (!e->indirect_info->targets) -+ continue; -+ for (unsigned i = 0; e->indirect_info->targets->iterate (i, &n2); ++i) -+ { -+ if (icn_map->count (n2) == 0) -+ (*icn_map)[n2] = new edge_set; -+ (*icn_map)[n2]->insert (e); -+ } -+ } -+} -+ -+static void -+collect_nn_info (struct cgraph_edge *e, struct cgraph_node *n) -+{ -+ struct cgraph_node *n2 = e->caller; -+ if (nn_map->count (n2) == 0) -+ (*nn_map)[n2] = new node_set; -+ (*nn_map)[n2]->insert (n); -+ if (nn_map->count (n) != 0) -+ { -+ node_set *set = (*nn_map)[n]; -+ for (node_set::const_iterator it = set->begin (); -+ it != set->end (); it++) -+ (*nn_map)[n2]->insert (*it); -+ } -+} -+ -+static bool -+check_loop_info_for_cgraph_edge (struct cgraph_edge *e, struct cgraph_node *n, -+ bool &all_in_loop, double &rate) -+{ -+ collect_nn_info (e, n); -+ if (el_map->count (e) == 0) -+ { -+ if (dump_file) -+ fprintf (dump_file, "not all: %s->%s\n", -+ e->caller->dump_name (), n->dump_name ()); -+ all_in_loop = false; -+ return false; -+ } -+ rate += (*el_map)[e]; -+ return true; -+} -+ -+static bool -+update_loop_info_for_cgraph_node (struct cgraph_node *n) -+{ -+ bool changed = false, all_in_loop = true; -+ double rate = 0.0; -+ struct cgraph_edge *e; -+ -+ /* Iterate all direct callers. */ -+ if (n->callers) -+ for (e = n->callers; e; e = e->next_caller) -+ if (!check_loop_info_for_cgraph_edge (e, n, all_in_loop, rate)) -+ break; -+ -+ /* Iterate all possible indirect callers. */ -+ edge_set *set = (*icn_map)[n]; -+ if (set) -+ for (edge_set::const_iterator it = set->begin (); it != set->end (); it++) -+ if (!check_loop_info_for_cgraph_edge (*it, n, all_in_loop, rate)) -+ break; -+ -+ /* The node had 0 loop count but the rate is > 0, -+ so something is changed. */ -+ if (dump_file) -+ fprintf (dump_file, "%s: all=%d, nl->c=%lu, r=%4.2f\n", n->dump_name (), -+ all_in_loop, nl_map->count (n), rate); -+ -+ if (all_in_loop && nl_map->count (n) == 0 && rate > 0.0) -+ { -+ if (dump_file) -+ fprintf (dump_file, "%s: new rate %4.2f\n", n->dump_name (), rate); -+ changed = true; -+ } -+ if (all_in_loop) -+ { -+ (*nl_map)[n] = nl_map->count (n) ? (*nl_map)[n] + rate : rate; -+ for (e = n->callees; e; e = e->next_callee) -+ (*el_map)[e] = el_map->count (e) ? (*el_map)[e] + rate : rate; -+ for (e = n->indirect_calls; e; e = e->next_callee) -+ { -+ (*el_map)[e] = el_map->count (e) ? (*el_map)[e] + rate : rate; -+ if (dump_file) -+ fprintf (dump_file, "%s: reset indirect e=%p to %4.2f\n", -+ n->dump_name (), (void *) e, (*el_map)[e]); -+ } -+ } -+ return changed; -+} -+ -+/* Propagate in_loop info over the call graph. */ -+ -+static void -+propagate_loop_info_in_cgraph () -+{ -+ struct cgraph_node *n; -+ bool changed; -+ unsigned iteration = 0; -+ do -+ { -+ changed = false; -+ if (dump_file) -+ fprintf (dump_file, "\nIteration %u\n", iteration++); -+ FOR_EACH_DEFINED_FUNCTION (n) -+ { -+ if (!n->callers && !(*icn_map)[n]) -+ continue; -+ if (update_loop_info_for_cgraph_node (n)) -+ changed = true; -+ } -+ } while (changed); -+ -+ if (dump_file) -+ { -+ fprintf (dump_file, "\nList of nodes in loops:\n"); -+ FOR_EACH_DEFINED_FUNCTION (n) -+ if (nl_map->count (n) != 0) -+ fprintf (dump_file, "%s: %4.2f\n", n->dump_name (), (*nl_map)[n]); -+ fprintf (dump_file, "\nList of callable nodes:\n"); -+ FOR_EACH_DEFINED_FUNCTION (n) -+ if (nn_map->count (n) != 0) -+ { -+ node_set *set = (*nn_map)[n]; -+ fprintf (dump_file, "%s: ", n->dump_name ()); -+ for (node_set::const_iterator it = set->begin (); -+ it != set->end (); it++) -+ fprintf (dump_file, "%s ", (*it)->dump_name ()); -+ fprintf (dump_file, "\n"); -+ } -+ } -+} -+ -+/* Analysis of memory references. */ -+ -+typedef enum -+{ -+ MR_NONE, -+ MR_SIMPLE, -+ MR_POLYNOMIAL, -+ MR_INDIRECT, -+ MR_UNSUPPORTED -+} mr_type; -+const char *mr_type_str[] = -+ {"none", "simple", "poly", "indirect", "unsuppoted"}; -+ -+struct memref_type; -+typedef std::set memref_set; -+ -+static unsigned max_mr_id = 0; -+typedef struct memref_type -+{ -+ unsigned mr_id = 0; -+ mr_type type = MR_NONE; -+ tree mem = NULL_TREE; -+ tree base = NULL_TREE; -+ tree offset = NULL_TREE; -+ vec stmts = vNULL; -+ memref_set used_mrs; -+ bool is_store = false; -+ bool is_incr = false; -+ tree step = NULL_TREE; -+} memref_t; -+ -+typedef std::map tree_memref_map; -+typedef std::map > function_mrs_map; -+typedef std::map funct_mrs_map; -+typedef std::map memref_map; -+typedef std::map memref_tree_map; -+ -+typedef std::set stmt_set; -+typedef std::map tree_map; -+ -+tree_memref_map *tm_map; -+funct_mrs_map *fmrs_map; -+funct_mrs_map *optimize_mrs_map; -+memref_map *mr_candidate_map; -+tree_map *decl_map; -+ -+static void analyse_mem_ref (gimple *stmt, tree mem, memref_t* mr); -+ -+static memref_t* -+get_memref (gimple *stmt, tree mem, bool is_store) -+{ -+ if (tm_map->count (mem)) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Found mr %d for %p.\n", -+ (*tm_map)[mem]->mr_id, (void *) mem); -+ return (*tm_map)[mem]; -+ } -+ -+ memref_t *mr = new memref_t; -+ mr->mr_id = ++max_mr_id; -+ mr->is_store = is_store; -+ mr->mem = mem; -+ (*tm_map)[mem] = mr; -+ if (dump_file) -+ fprintf (dump_file, "Create mr %d for %p.\n", -+ mr->mr_id, (void *) mem); -+ analyse_mem_ref (stmt, mem, mr); -+ return mr; -+} -+ -+static void -+print_mrs_ids (memref_set &mrs, const char *start) -+{ -+ if (start) -+ fprintf (dump_file, "%s", start); -+ for (memref_set::const_iterator it = mrs.begin (); it != mrs.end (); it++) -+ fprintf (dump_file, "%d ", (*it)->mr_id); -+ fprintf (dump_file, "\n"); -+} -+ -+static void -+print_memref (memref_t *mr) -+{ -+ fprintf (dump_file, "MR (%d) type: %s (%s) mem: ", mr->mr_id, -+ mr_type_str[mr->type], mr->is_store ? "st" : "ld"); -+ print_generic_expr (dump_file, mr->mem); -+ fprintf (dump_file, "\nbase: "); -+ if (mr->base) -+ print_generic_expr (dump_file, mr->base); -+ else -+ fprintf (dump_file, "null"); -+ fprintf (dump_file, "\noffset: "); -+ if (mr->offset) -+ print_generic_expr (dump_file, mr->offset); -+ else -+ fprintf (dump_file, "null"); -+ fprintf (dump_file, "\nstmts:\n"); -+ for (unsigned int i = 0; i < mr->stmts.length (); i++) -+ print_gimple_stmt (dump_file, mr->stmts[i], 0); -+ print_mrs_ids (mr->used_mrs, "\tused memrefs: "); -+ if (mr->is_incr) -+ { -+ fprintf (dump_file, "\tis incremental with step: "); -+ print_generic_expr (dump_file, mr->step); -+ } -+ fprintf (dump_file, "\n"); -+} -+ -+/* If there is a simple load or store to a memory reference in STMT, returns -+ the location of the memory reference, and sets IS_STORE according to whether -+ it is a store or load. Otherwise, returns NULL. -+ TODO: from gcc/tree-ssa-loop-im.c, maybe make it global. */ -+ -+static tree * -+simple_mem_ref_in_stmt (gimple *stmt, bool *is_store) -+{ -+ tree *lhs, *rhs; -+ -+ /* Recognize SSA_NAME = MEM and MEM = (SSA_NAME | invariant) patterns. */ -+ if (!gimple_assign_single_p (stmt)) -+ return NULL; -+ -+ lhs = gimple_assign_lhs_ptr (stmt); -+ rhs = gimple_assign_rhs1_ptr (stmt); -+ -+ if (TREE_CODE (*lhs) == SSA_NAME && gimple_vuse (stmt)) -+ { -+ *is_store = false; -+ return rhs; -+ } -+ else if (gimple_vdef (stmt) -+ && (TREE_CODE (*rhs) == SSA_NAME || is_gimple_min_invariant (*rhs))) -+ { -+ *is_store = true; -+ return lhs; -+ } -+ else -+ return NULL; -+} -+ -+static void -+analyse_incremental (gimple *stmt, memref_t* mr) -+{ -+ if (!gimple_assign_single_p (stmt)) -+ return; -+ tree rhs1, rhs2; -+ /* TODO: maybe support other types of stmts. */ -+ while (stmt && is_gimple_assign (stmt)) -+ { -+ enum tree_code def_code = gimple_assign_rhs_code (stmt); -+ gimple_rhs_class rhs_class = gimple_assign_rhs_class (stmt); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Incr: in assign (%s)\n", -+ get_tree_code_name (def_code)); -+ print_gimple_stmt (dump_file, stmt, 3, TDF_DETAILS); -+ } -+ gcc_assert (def_code != ERROR_MARK); -+ switch (rhs_class) -+ { -+ case GIMPLE_TERNARY_RHS: -+ if (dump_file) -+ fprintf (dump_file, "Incr: unsupported trinary rhs\n"); -+ stmt = NULL; -+ break; -+ case GIMPLE_UNARY_RHS: -+ case GIMPLE_SINGLE_RHS: -+ rhs1 = gimple_assign_rhs1 (stmt); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Incr: (%s)", -+ get_tree_code_name (TREE_CODE (rhs1))); -+ print_generic_expr (dump_file, rhs1); -+ fprintf (dump_file, "\n"); -+ } -+ if (def_code == SSA_NAME) -+ stmt = SSA_NAME_DEF_STMT (rhs1); -+ else if (def_code == MEM_REF || def_code == COMPONENT_REF -+ || def_code == ARRAY_REF) -+ { -+ /* If we have dereference in address evaluation, -+ it's indirect memory access. */ -+ if (dump_file) -+ { -+ if (operand_equal_p (mr->mem, rhs1)) -+ fprintf (dump_file, "Incr: the same MEM\n"); -+ else -+ fprintf (dump_file, "Incr: diff MEM\n"); -+ print_generic_expr (dump_file, rhs1); -+ fprintf (dump_file, " "); -+ print_generic_expr (dump_file, mr->mem); -+ fprintf (dump_file, "\n"); -+ } -+ if (operand_equal_p (mr->mem, rhs1) && mr->step) -+ mr->is_incr = true; -+ stmt = NULL; -+ } -+ else -+ { -+ if (dump_file) -+ fprintf (dump_file, "Incr: unsupported unary/single\n"); -+ stmt = NULL; -+ } -+ break; -+ case GIMPLE_BINARY_RHS: -+ rhs1 = gimple_assign_rhs1 (stmt); -+ rhs2 = gimple_assign_rhs2 (stmt); -+ if (dump_file) -+ { -+ fprintf (dump_file, "(%s) (%s)", -+ get_tree_code_name (TREE_CODE (rhs1)), -+ get_tree_code_name (TREE_CODE (rhs2))); -+ print_generic_expr (dump_file, rhs1); -+ fprintf (dump_file, " "); -+ print_generic_expr (dump_file, rhs2); -+ fprintf (dump_file, "\n"); -+ } -+ /* TODO: extend for other types of incrementation. */ -+ if (TREE_CODE (rhs1) == SSA_NAME && TREE_CODE (rhs2) == INTEGER_CST) -+ { -+ stmt = SSA_NAME_DEF_STMT (rhs1); -+ mr->step = rhs2; -+ if (dump_file) -+ { -+ fprintf (dump_file, "Incr: const increment stmt: "); -+ print_gimple_stmt (dump_file, stmt, 3, TDF_DETAILS); -+ } -+ } -+ else -+ stmt = NULL; -+ break; -+ default: -+ gcc_unreachable (); -+ } -+ } -+ if ((mr->step && !mr->is_incr) || (!mr->step && mr->is_incr)) -+ { -+ mr->step = NULL_TREE; -+ mr->is_incr = false; -+ } -+} -+ -+static mr_type -+get_memref_type (memref_t *base, memref_t *used, enum tree_code code) -+{ -+ /* TODO: improve memref type detection. */ -+ enum tree_code base_code = TREE_CODE (base->mem); -+ if (dump_file) -+ fprintf (dump_file, "get_memref_type: base=%d,%d used=%d,%d code=%s " -+ "base_code=%s\n", base->mr_id, base->type, -+ used ? used->mr_id : -1, used ? used->type : -1, -+ get_tree_code_name (code), get_tree_code_name (base_code)); -+ if (used) -+ { -+ if (base->type > used->type) -+ return base->type; -+ if (used->type == MR_SIMPLE) -+ return MR_POLYNOMIAL; -+ if (used->type == MR_POLYNOMIAL) -+ return base_code == ARRAY_REF ? MR_POLYNOMIAL : MR_INDIRECT; -+ if (used->type == MR_INDIRECT) -+ return MR_INDIRECT; -+ return MR_UNSUPPORTED; -+ } -+ if (code == MEM_REF || code == ARRAY_REF || code == COMPONENT_REF) -+ return base->type; -+ if (code == POINTER_PLUS_EXPR || code == PLUS_EXPR -+ || code == MINUS_EXPR || code == MULT_EXPR) -+ return base->type <= MR_POLYNOMIAL ? MR_POLYNOMIAL : base->type; -+ return base->type >= MR_INDIRECT ? base->type : MR_INDIRECT; -+} -+ -+/* Recursively walk defs of src expression and record used stmts and other mrs. -+ Return a base address candidate if it's found. */ -+ -+static tree -+analyse_addr_eval (tree src, memref_t* mr) -+{ -+ if (TREE_CODE (src) != SSA_NAME) -+ return NULL_TREE; -+ gimple *stmt = SSA_NAME_DEF_STMT (src); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Src_stmt: "); -+ print_gimple_stmt (dump_file, stmt, 0); -+ } -+ if (!is_gimple_assign (stmt)) -+ { -+ if (dump_file) -+ { -+ fprintf (dump_file, "Is not assign, stop analysis: "); -+ print_gimple_stmt (dump_file, stmt, 3, TDF_DETAILS); -+ } -+ mr->type = MR_UNSUPPORTED; -+ mr->stmts.safe_push (stmt); -+ return NULL_TREE; -+ } -+ enum tree_code def_code = gimple_assign_rhs_code (stmt); -+ if (def_code != MEM_REF && def_code != COMPONENT_REF -+ && def_code != ARRAY_REF) -+ mr->stmts.safe_push (stmt); -+ gimple_rhs_class rhs_class = gimple_assign_rhs_class (stmt); -+ tree rhs1, rhs2, base; -+ if (dump_file) -+ fprintf (dump_file, "In assign (%s): ", get_tree_code_name (def_code)); -+ -+ switch (rhs_class) -+ { -+ case GIMPLE_TERNARY_RHS: -+ if (dump_file) -+ fprintf (dump_file, "Unsupported trinary rhs\n"); -+ mr->type = MR_UNSUPPORTED; -+ return NULL_TREE; -+ case GIMPLE_UNARY_RHS: -+ case GIMPLE_SINGLE_RHS: -+ rhs1 = gimple_assign_rhs1 (stmt); -+ if (dump_file) -+ { -+ fprintf (dump_file, "(%s)", -+ get_tree_code_name (TREE_CODE (rhs1))); -+ print_generic_expr (dump_file, rhs1); -+ fprintf (dump_file, "\n"); -+ } -+ if (def_code == NOP_EXPR) -+ return analyse_addr_eval (rhs1, mr); -+ else if (def_code == MEM_REF || def_code == COMPONENT_REF -+ || def_code == ARRAY_REF) -+ { -+ memref_t *mr2 = get_memref (stmt, rhs1, false); -+ mr->type = get_memref_type (mr, mr2, def_code); -+ for (memref_set::const_iterator it = mr2->used_mrs.begin (); -+ it != mr2->used_mrs.end (); it++) -+ mr->used_mrs.insert (*it); -+ mr->used_mrs.insert (mr2); -+ return mr2->base; -+ } -+ else -+ { -+ if (dump_file) -+ fprintf (dump_file, "Unsupported unary/single\n"); -+ mr->type = MR_UNSUPPORTED; -+ } -+ return NULL_TREE; -+ case GIMPLE_BINARY_RHS: -+ rhs1 = gimple_assign_rhs1 (stmt); -+ rhs2 = gimple_assign_rhs2 (stmt); -+ if (dump_file) -+ { -+ fprintf (dump_file, "(%s) (%s)", -+ get_tree_code_name (TREE_CODE (rhs1)), -+ get_tree_code_name (TREE_CODE (rhs2))); -+ print_generic_expr (dump_file, rhs1); -+ fprintf (dump_file, " "); -+ print_generic_expr (dump_file, rhs2); -+ fprintf (dump_file, "\n"); -+ } -+ base = analyse_addr_eval (rhs1, mr); -+ analyse_addr_eval (rhs2, mr); -+ mr->type = get_memref_type (mr, NULL, def_code); -+ return base; -+ default: -+ gcc_unreachable (); -+ } -+ return NULL_TREE; -+} -+ -+static tree -+get_mem_ref_address_ssa_name (tree mem, tree base) -+{ -+ gcc_assert (TREE_CODE (mem) == MEM_REF); -+ if (base == NULL_TREE) -+ base = get_base_address (mem); -+ tree base_addr = NULL_TREE; -+ if (TREE_CODE (base) == MEM_REF) -+ base_addr = TREE_OPERAND (base, 0); -+ if (base_addr != NULL_TREE && TREE_CODE (base_addr) == SSA_NAME) -+ return base_addr; -+ return NULL_TREE; -+} -+ -+static void -+analyse_mem_ref (gimple *stmt, tree mem, memref_t* mr) -+{ -+ tree base = get_base_address (mem); -+ if (dump_file) -+ fprintf (dump_file, "Codes: base = %s, mem = %s\n", -+ base ? get_tree_code_name (TREE_CODE (base)) : "null", -+ mem ? get_tree_code_name (TREE_CODE (mem)) : "null"); -+ -+ mr->stmts.safe_push (stmt); -+ mr->base = base; -+ switch (TREE_CODE (mem)) -+ { -+ case COMPONENT_REF: -+ if (mr->is_store) -+ analyse_incremental (stmt, mr); -+ mr->type = MR_SIMPLE; -+ mr->offset = TREE_OPERAND (mem, 1); -+ return; -+ case ARRAY_REF: -+ analyse_addr_eval (TREE_OPERAND (mem, 1), mr); -+ return; -+ case MEM_REF: -+ { -+ tree base_addr = get_mem_ref_address_ssa_name (mem, base); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Base addr (%s): ", -+ base_addr ? get_tree_code_name (TREE_CODE (base_addr)) -+ : "null"); -+ if (base_addr) -+ print_generic_expr (dump_file, base_addr); -+ fprintf (dump_file, "\n"); -+ } -+ if (base_addr) -+ { -+ mr->base = analyse_addr_eval (base_addr, mr); -+ return; -+ } -+ break; -+ } -+ default: -+ break; -+ } -+ mr->type = MR_UNSUPPORTED; -+ mr->base = NULL_TREE; -+} -+ -+static void -+analyse_stmt (gimple *stmt) -+{ -+ bool is_store; -+ tree *mem = simple_mem_ref_in_stmt (stmt, &is_store); -+ if (!mem) -+ return; -+ if (dump_file) -+ { -+ fprintf (dump_file, "\n%s: mr is found in stmt (%s): ", -+ function_name (cfun), is_store ? "store" : "load"); -+ print_gimple_stmt (dump_file, stmt, 3, TDF_DETAILS); -+ } -+ memref_t *mr = get_memref (stmt, *mem, is_store); -+ (*fmrs_map)[cfun]->insert (mr); -+ if (dump_file) -+ print_memref (mr); -+} -+ -+/* Scan stmts for indirect stores/loads with bases passed as function args. */ -+ -+static void -+collect_memrefs_for_cgraph_node (struct cgraph_node *n) -+{ -+ if (dump_file) -+ fprintf (dump_file, "\nCollect indirect ptr info in %s\n", n->dump_name ()); -+ n->get_body (); -+ function *fn = DECL_STRUCT_FUNCTION (n->decl); -+ gcc_assert (fn && n->has_gimple_body_p ()); -+ -+ push_cfun (fn); -+ basic_block bb; -+ gimple_stmt_iterator si; -+ (*fmrs_map)[fn] = new memref_set; -+ FOR_EACH_BB_FN (bb, fn) -+ for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) -+ { -+ gimple *stmt = gsi_stmt (si); -+ analyse_stmt (stmt); -+ } -+ pop_cfun (); -+} -+ -+/* Walk cgraph nodes and collect memory references info. */ -+ -+static void -+collect_memory_references () -+{ -+ struct cgraph_node *n; -+ /* TODO: collect info only for loops and functions in loops. */ -+ FOR_EACH_DEFINED_FUNCTION (n) -+ if (nl_map->count (n) != 0 && n->has_gimple_body_p ()) -+ collect_memrefs_for_cgraph_node (n); -+ -+ if (dump_file) -+ { -+ fprintf (dump_file, "\n\nDump mem references:\n"); -+ FOR_EACH_DEFINED_FUNCTION (n) -+ if (nl_map->count (n) != 0 && n->has_gimple_body_p ()) -+ { -+ function *fn = DECL_STRUCT_FUNCTION (n->decl); -+ fprintf (dump_file, "\nIn function %s (%s):\n", function_name (fn), -+ nl_map->count (n) != 0 ? "in loop" : ""); -+ for (memref_set::const_iterator it = (*fmrs_map)[fn]->begin (); -+ it != (*fmrs_map)[fn]->end (); it++) -+ print_memref (*it); -+ } -+ } -+} -+ -+/* Analysis of loops. */ -+ -+memref_set *current_incr_mrs; -+memref_set *current_indirect_mrs; -+ -+static void -+collect_memref (memref_t *mr, class loop *loop, bool check_loop) -+{ -+ gimple *stmt = mr->stmts[0]; -+ gcc_assert (stmt); -+ if (check_loop && !flow_bb_inside_loop_p (loop, gimple_bb (stmt))) -+ return; -+ -+ /* TODO: Improve base invariant analysis for memrefs which are not local -+ (located in called functions). */ -+ bool is_base_inv = false; -+ if (mr->base) -+ is_base_inv = expr_invariant_in_loop_p (loop, mr->base); -+ -+ if (dump_file && (mr->type == MR_INDIRECT || mr->is_incr)) -+ { -+ fprintf (dump_file, "%s MR (%d): ", mr->is_incr ? "INCR" : "INDIRECT", -+ mr->mr_id); -+ print_generic_expr (dump_file, mr->mem); -+ fprintf (dump_file, "\twith base: "); -+ if (mr->base) -+ print_generic_expr (dump_file, mr->base); -+ else -+ fprintf (dump_file, "null"); -+ fprintf (dump_file, " (is_inv=%d)\n", is_base_inv); -+ } -+ -+ if (!is_base_inv) -+ return; -+ if (mr->type == MR_INDIRECT) -+ current_indirect_mrs->insert (mr); -+ if (mr->is_incr) -+ current_incr_mrs->insert (mr); -+} -+ -+static void -+analyse_callable_function (struct cgraph_node *n, class loop *loop) -+{ -+ if (dump_file) -+ fprintf (dump_file, "Callable (%s):\n", n->dump_name ()); -+ -+ function *fn = DECL_STRUCT_FUNCTION (n->decl); -+ if (fmrs_map->count (fn)) -+ for (memref_set::const_iterator it = (*fmrs_map)[fn]->begin (); -+ it != (*fmrs_map)[fn]->end (); it++) -+ collect_memref (*it, loop, false); -+} -+ -+static void -+insert_node_with_callable_nodes (node_set &s, struct cgraph_node *n) -+{ -+ s.insert (n); -+ if (nn_map->count (n) == 0) -+ return; -+ node_set *set = (*nn_map)[n]; -+ for (node_set::const_iterator it = set->begin (); it != set->end (); it++) -+ s.insert ((*it)); -+} -+ -+static bool -+compatible_memrefs_p (memref_t *mr1, memref_t *mr2, bool &compatible_offset) -+{ -+ if (!mr1->base || !mr2->base || !mr2->offset) -+ return false; -+ tree base_type1 = TYPE_MAIN_VARIANT (TREE_TYPE (mr1->base)); -+ tree base_type2 = TYPE_MAIN_VARIANT (TREE_TYPE (mr2->base)); -+ if (base_type1 != base_type2) -+ return false; -+ if (mr1->offset && mr1->offset == mr2->offset) -+ compatible_offset = true; -+ else -+ compatible_offset = false; -+ return true; -+} -+ -+static void -+compare_memrefs (memref_t* mr, memref_t* mr2) -+{ -+ /* TODO: improve analysis of memrefs from different functions: take into -+ account data flow and context. */ -+ bool compatible_offset = false; -+ if (!compatible_memrefs_p (mr, mr2, compatible_offset)) -+ return; -+ if (!compatible_offset) -+ { -+ for (memref_set::const_iterator it = mr->used_mrs.begin (); -+ it != mr->used_mrs.end (); it++) -+ if ((*it)->offset && (*it)->offset == mr2->offset) -+ { -+ compatible_offset = true; -+ if (dump_file) -+ fprintf (dump_file, "Used MR (%d) and INC MR have " -+ "the same offset\n", (*it)->mr_id); -+ break; -+ } -+ } -+ if (!compatible_offset) -+ return; -+ if (dump_file) -+ { -+ fprintf (dump_file, "MR (%d) is optimization candidate with offset: ", -+ mr->mr_id); -+ print_generic_expr (dump_file, mr2->offset); -+ fprintf (dump_file, "\n"); -+ } -+ -+ if (!mr_candidate_map->count (mr)) -+ { -+ (*mr_candidate_map)[mr] = mr2; -+ return; -+ } -+ /* TODO: support analysis with incrementation of different fields. */ -+ if ((*mr_candidate_map)[mr]->offset != mr2->offset) -+ { -+ if (dump_file) -+ { -+ fprintf (dump_file, "It conflicts with previously found MR (%d) " -+ "with offset ", (*mr_candidate_map)[mr]->mr_id); -+ if ((*mr_candidate_map)[mr] != NULL) -+ print_generic_expr (dump_file, (*mr_candidate_map)[mr]->offset); -+ fprintf (dump_file, ", disable the optimization\n"); -+ } -+ (*mr_candidate_map)[mr] = NULL; -+ } -+} -+ -+/* In the given loop and all functions called from the loop, collect -+ indirect/incremental memrefs with invariant base address and inductive -+ offset. */ -+ -+static void -+collect_memrefs_for_loop (class loop *loop, struct cgraph_node *n, -+ function *fn) -+{ -+ current_incr_mrs = new memref_set; -+ current_indirect_mrs = new memref_set; -+ -+ if (dump_file) -+ fprintf (dump_file, "Loop %d\n", loop->num); -+ if (fmrs_map->count (fn)) -+ for (memref_set::const_iterator it = (*fmrs_map)[fn]->begin (); -+ it != (*fmrs_map)[fn]->end (); it++) -+ collect_memref (*it, loop, true); -+ -+ /* Collect vector of functions called in the loop. */ -+ node_set set; -+ struct cgraph_edge *e; -+ struct cgraph_node *n2; -+ for (e = n->callees; e; e = e->next_callee) -+ { -+ gcall *stmt = e->call_stmt; -+ if (!flow_bb_inside_loop_p (loop, gimple_bb (stmt))) -+ continue; -+ insert_node_with_callable_nodes (set, e->callee); -+ } -+ for (e = n->indirect_calls; e; e = e->next_callee) -+ { -+ gcall *stmt = e->call_stmt; -+ if (!flow_bb_inside_loop_p (loop, gimple_bb (stmt)) -+ || !e->indirect_info->targets) -+ continue; -+ for (unsigned i = 0; e->indirect_info->targets->iterate (i, &n2); ++i) -+ insert_node_with_callable_nodes (set, n2); -+ } -+ if (set.empty ()) -+ return; -+ if (dump_file) -+ fprintf (dump_file, "Go inside all callables of %s\n", n->dump_name ()); -+ -+ for (node_set::const_iterator it = set.begin (); it != set.end (); it++) -+ analyse_callable_function (*it, loop); -+ -+ if (!current_incr_mrs->empty () && !current_indirect_mrs->empty ()) -+ { -+ if (dump_file) -+ { -+ fprintf (dump_file, "Loop has both incr and indirect memrefs\n" -+ "Incr: "); -+ for (memref_set::const_iterator it = current_incr_mrs->begin (); -+ it != current_incr_mrs->end (); it++) -+ fprintf (dump_file, "%d ", (*it)->mr_id); -+ fprintf (dump_file, "\nIndirect: "); -+ for (memref_set::const_iterator it = current_indirect_mrs->begin (); -+ it != current_indirect_mrs->end (); it++) -+ fprintf (dump_file, "%d ", (*it)->mr_id); -+ fprintf (dump_file, "\n"); -+ } -+ /* Check if indirect memref has a base address similar to one of -+ incremental memref. */ -+ for (memref_set::const_iterator it = current_indirect_mrs->begin (); -+ it != current_indirect_mrs->end (); it++) -+ for (memref_set::const_iterator it2 = current_incr_mrs->begin (); -+ it2 != current_incr_mrs->end (); it2++) -+ compare_memrefs (*it, *it2); -+ } -+ -+ delete current_incr_mrs; -+ delete current_indirect_mrs; -+} -+ -+static void -+analyse_loops_in_cgraph_node (struct cgraph_node *n) -+{ -+ if (dump_file) -+ fprintf (dump_file, "\nAnalyse loops in %s\n", n->dump_name ()); -+ -+ n->get_body (); -+ function *fn = DECL_STRUCT_FUNCTION (n->decl); -+ gcc_assert (fn && n->has_gimple_body_p ()); -+ -+ push_cfun (fn); -+ calculate_dominance_info (CDI_DOMINATORS); -+ loop_optimizer_init (LOOPS_NORMAL); -+ -+ class loop *loop; -+ FOR_EACH_LOOP (loop, 0) -+ { -+ class loop *outer = loop_outer (loop); -+ /* Walk only outermost loops. */ -+ if (outer->num != 0) -+ continue; -+ collect_memrefs_for_loop (loop, n, fn); -+ } -+ -+ free_dominance_info (CDI_DOMINATORS); -+ loop_optimizer_finalize (); -+ pop_cfun (); -+} -+ -+static void -+analyse_loops () -+{ -+ if (dump_file) -+ fprintf (dump_file, "\n\nLoops: procesing functions\n"); -+ cgraph_node *n; -+ FOR_EACH_DEFINED_FUNCTION (n) -+ { -+ if (!can_be_optimized (n)) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Skip the function\n"); -+ continue; -+ } -+ analyse_loops_in_cgraph_node (n); -+ } -+ -+ if (dump_file) -+ fprintf (dump_file, "\n\nList of optimization candidates:\n"); -+ -+ FOR_EACH_DEFINED_FUNCTION (n) -+ { -+ function *fn = DECL_STRUCT_FUNCTION (n->decl); -+ if (!can_be_optimized (n) || !fmrs_map->count (fn)) -+ continue; -+ for (memref_map::iterator it = mr_candidate_map->begin (); -+ it != mr_candidate_map->end (); ++it) -+ { -+ memref_t *mr = it->first, *mr2 = it->second; -+ if (mr2 == NULL || !(*fmrs_map)[fn]->count (mr)) -+ continue; -+ if (!optimize_mrs_map->count (fn)) -+ (*optimize_mrs_map)[fn] = new memref_set; -+ (*optimize_mrs_map)[fn]->insert (mr); -+ } -+ if (dump_file && optimize_mrs_map->count (fn)) -+ { -+ fprintf (dump_file, "Function %s\n", n->dump_name ()); -+ for (memref_set::const_iterator it -+ = (*optimize_mrs_map)[fn]->begin (); -+ it != (*optimize_mrs_map)[fn]->end (); it++) -+ { -+ memref_t *mr = *it, *mr2 = (*mr_candidate_map)[mr]; -+ fprintf (dump_file, "MRs %d,%d with incremental offset ", -+ mr->mr_id, mr2->mr_id); -+ print_generic_expr (dump_file, mr2->offset); -+ fprintf (dump_file, "\n"); -+ } -+ } -+ } -+} -+ -+/* Reduce the set filtering out memrefs with the same memory references, -+ return the result vector of memrefs. */ -+ -+static void -+reduce_memref_set (memref_set *set, vec &vec) -+{ -+ for (memref_set::const_iterator it = set->begin (); -+ it != set->end (); it++) -+ { -+ memref_t *mr1 = *it; -+ if (!vec.length ()) -+ vec.safe_push (mr1); -+ else -+ { -+ bool inserted = false; -+ for (unsigned int i = 0; i < vec.length (); i++) -+ { -+ /* mr2 is less than current mr1. */ -+ memref_t *mr2 = vec[i]; -+ if (operand_equal_p (mr1->mem, mr2->mem)) -+ { -+ if (dump_file) -+ fprintf (dump_file, "The same mems in MRs %d and %d\n", -+ mr1->mr_id, mr2->mr_id); -+ /* TODO: maybe build new memref which include stmts of both -+ mr1 and mr2. */ -+ if ((mr1->is_store && !mr2->is_store) -+ || mr1->stmts.length () > mr2->stmts.length ()) -+ { -+ inserted = true; -+ vec[i] = mr1; -+ } -+ } -+ } -+ if (!inserted) -+ vec.safe_push (mr1); -+ } -+ } -+ if (dump_file) -+ { -+ fprintf (dump_file, "MRs (%d) after filtering: ", vec.length ()); -+ for (unsigned int i = 0; i < vec.length (); i++) -+ fprintf (dump_file, "%d ", vec[i]->mr_id); -+ fprintf (dump_file, "\n"); -+ } -+} -+ -+static void -+find_nearest_common_dominator (memref_t *mr, basic_block &dom) -+{ -+ for (unsigned int i = 0; i < mr->stmts.length (); i++) -+ { -+ basic_block bb = gimple_bb (mr->stmts[i]); -+ gcc_assert (bb); -+ if (dom == bb) -+ continue; -+ if (dom) -+ dom = nearest_common_dominator (CDI_DOMINATORS, dom, bb); -+ else -+ dom = bb; -+ } -+} -+ -+/* Return true if DECL is a parameter or a SSA_NAME for a parameter. -+ TODO: from gcc/tree-inline.c, maybe make it global. */ -+ -+static bool -+is_parm (tree decl) -+{ -+ if (TREE_CODE (decl) == SSA_NAME) -+ { -+ decl = SSA_NAME_VAR (decl); -+ if (!decl) -+ return false; -+ } -+ -+ return (TREE_CODE (decl) == PARM_DECL); -+} -+ -+/* TODO: the following functions are inspired by remap in gcc/tree-inline.c, -+ maybe we can share some functionality. */ -+ -+static tree -+remap_name (tree name, gimple *stmt, bool is_lhs) -+{ -+ tree new_tree = NULL_TREE; -+ if (decl_map->count (name)) -+ { -+ if (dump_file) -+ { -+ fprintf (dump_file, "Find map: "); -+ print_generic_expr (dump_file, name); -+ fprintf (dump_file, " "); -+ print_generic_expr (dump_file, (*decl_map)[name]); -+ fprintf (dump_file, "\n"); -+ } -+ return unshare_expr ((*decl_map)[name]); -+ } -+ if (!is_lhs) -+ return name; -+ if (TREE_CODE (name) == SSA_NAME) -+ { -+ /* Remap anonymous SSA names or SSA names of anonymous decls. */ -+ tree var = SSA_NAME_VAR (name); -+ if (!var -+ || (!SSA_NAME_IS_DEFAULT_DEF (name) -+ && VAR_P (var) && !VAR_DECL_IS_VIRTUAL_OPERAND (var) -+ && DECL_ARTIFICIAL (var) && DECL_IGNORED_P (var) -+ && !DECL_NAME (var))) -+ { -+ new_tree = make_ssa_name (TREE_TYPE (name), stmt); -+ if (!var && SSA_NAME_IDENTIFIER (name)) -+ SET_SSA_NAME_VAR_OR_IDENTIFIER (new_tree, -+ SSA_NAME_IDENTIFIER (name)); -+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_tree) -+ = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name); -+ /* So can range-info. */ -+ if (!POINTER_TYPE_P (TREE_TYPE (name)) -+ && SSA_NAME_RANGE_INFO (name)) -+ duplicate_ssa_name_range_info (new_tree, -+ SSA_NAME_RANGE_TYPE (name), -+ SSA_NAME_RANGE_INFO (name)); -+ /* TODO: maybe correct the insertion. */ -+ (*decl_map)[name] = new_tree; -+ if (dump_file) -+ { -+ fprintf (dump_file, "New map (no var): "); -+ print_generic_expr (dump_file, name); -+ fprintf (dump_file, " "); -+ print_generic_expr (dump_file, new_tree); -+ fprintf (dump_file, "\n"); -+ } -+ return new_tree; -+ } -+ /* TODO: maybe remap_name or do the same as before for SSA_NAME_VAR. */ -+ new_tree = make_ssa_name (TREE_TYPE (name), stmt); -+ (*decl_map)[name] = new_tree; -+ if (dump_file) -+ { -+ fprintf (dump_file, "New map: "); -+ print_generic_expr (dump_file, name); -+ fprintf (dump_file, " "); -+ print_generic_expr (dump_file, new_tree); -+ fprintf (dump_file, "\n"); -+ } -+ } -+ else if (VAR_P (name) || TREE_CODE (name) == PARM_DECL) -+ { -+ if (dump_file) -+ { -+ fprintf (dump_file, "VAR/PARM: "); -+ print_generic_expr (dump_file, name); -+ fprintf (dump_file, "\n"); -+ } -+ return name; -+ } -+ else -+ { -+ if (dump_file) -+ { -+ fprintf (dump_file, "Unsupported: "); -+ print_generic_expr (dump_file, name); -+ fprintf (dump_file, "\n"); -+ } -+ //gcc_unreachable (); -+ return name; -+ } -+ return new_tree; -+} -+ -+/* Passed to walk_tree. Copies the node pointed to, if appropriate. */ -+ -+static tree -+ipa_copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) -+{ -+ enum tree_code code = TREE_CODE (*tp); -+ enum tree_code_class cl = TREE_CODE_CLASS (code); -+ -+ /* We make copies of most nodes. */ -+ if (IS_EXPR_CODE_CLASS (cl) -+ || code == TREE_LIST -+ || code == TREE_VEC -+ || code == TYPE_DECL -+ || code == OMP_CLAUSE) -+ { -+ /* Because the chain gets clobbered when we make a copy, we save it -+ here. */ -+ tree chain = NULL_TREE, new_tree; -+ -+ if (CODE_CONTAINS_STRUCT (code, TS_COMMON)) -+ chain = TREE_CHAIN (*tp); -+ -+ /* Copy the node. */ -+ new_tree = copy_node (*tp); -+ -+ *tp = new_tree; -+ -+ /* Now, restore the chain, if appropriate. That will cause -+ walk_tree to walk into the chain as well. */ -+ if (code == PARM_DECL -+ || code == TREE_LIST -+ || code == OMP_CLAUSE) -+ TREE_CHAIN (*tp) = chain; -+ -+ /* For now, we don't update BLOCKs when we make copies. So, we -+ have to nullify all BIND_EXPRs. */ -+ if (TREE_CODE (*tp) == BIND_EXPR) -+ BIND_EXPR_BLOCK (*tp) = NULL_TREE; -+ } -+ else if (code == CONSTRUCTOR || code == STATEMENT_LIST) -+ gcc_unreachable (); -+ else if (TREE_CODE_CLASS (code) == tcc_type -+ || TREE_CODE_CLASS (code) == tcc_declaration -+ || TREE_CODE_CLASS (code) == tcc_constant) -+ *walk_subtrees = 0; -+ return NULL_TREE; -+} -+ -+/* Remap the GIMPLE operand pointed to by *TP. DATA is really a -+ 'struct walk_stmt_info *'. DATA->INFO is a 'gimple *'. -+ WALK_SUBTREES is used to indicate walk_gimple_op whether to keep -+ recursing into the children nodes of *TP. */ -+ -+static tree -+remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data) -+{ -+ struct walk_stmt_info *wi_p = (struct walk_stmt_info *) data; -+ gimple *stmt = (gimple *) wi_p->info; -+ -+ /* For recursive invocations this is no longer the LHS itself. */ -+ bool is_lhs = wi_p->is_lhs; -+ wi_p->is_lhs = false; -+ -+ if (TREE_CODE (*tp) == SSA_NAME) -+ { -+ *tp = remap_name (*tp, stmt, is_lhs); -+ *walk_subtrees = 0; -+ if (is_lhs) -+ SSA_NAME_DEF_STMT (*tp) = wi_p->stmt; -+ return NULL; -+ } -+ else if (auto_var_in_fn_p (*tp, cfun->decl)) -+ { -+ /* Local variables and labels need to be replaced by equivalent -+ variables. We don't want to copy static variables; there's -+ only one of those, no matter how many times we inline the -+ containing function. Similarly for globals from an outer -+ function. */ -+ tree new_decl; -+ -+ /* Remap the declaration. */ -+ new_decl = remap_name (*tp, stmt, is_lhs); -+ gcc_assert (new_decl); -+ /* Replace this variable with the copy. */ -+ STRIP_TYPE_NOPS (new_decl); -+ /* ??? The C++ frontend uses void * pointer zero to initialize -+ any other type. This confuses the middle-end type verification. -+ As cloned bodies do not go through gimplification again the fixup -+ there doesn't trigger. */ -+ if (TREE_CODE (new_decl) == INTEGER_CST -+ && !useless_type_conversion_p (TREE_TYPE (*tp), TREE_TYPE (new_decl))) -+ new_decl = fold_convert (TREE_TYPE (*tp), new_decl); -+ *tp = new_decl; -+ *walk_subtrees = 0; -+ } -+ else if (TREE_CODE (*tp) == STATEMENT_LIST || TREE_CODE (*tp) == SAVE_EXPR) -+ { -+ if (dump_file) -+ { -+ fprintf (dump_file, "Unexpected tree: "); -+ print_generic_expr (dump_file, *tp); -+ fprintf (dump_file, "\n"); -+ } -+ gcc_unreachable (); -+ } -+ else -+ { -+ /* Otherwise, just copy the node. Note that copy_tree_r already -+ knows not to copy VAR_DECLs, etc., so this is safe. */ -+ -+ if (TREE_CODE (*tp) == MEM_REF) -+ { -+ /* We need to re-canonicalize MEM_REFs from inline substitutions -+ that can happen when a pointer argument is an ADDR_EXPR. -+ Recurse here manually to allow that. */ -+ tree ptr = TREE_OPERAND (*tp, 0); -+ tree type = TREE_TYPE (*tp); -+ tree old = *tp; -+ walk_tree (&ptr, remap_gimple_op_r, data, NULL); -+ *tp = fold_build2 (MEM_REF, type, ptr, TREE_OPERAND (*tp, 1)); -+ TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old); -+ TREE_SIDE_EFFECTS (*tp) = TREE_SIDE_EFFECTS (old); -+ TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old); -+ /* TODO: maybe support this case. */ -+ gcc_assert (MR_DEPENDENCE_CLIQUE (old) == 0); -+ /* We cannot propagate the TREE_THIS_NOTRAP flag if we have -+ remapped a parameter as the property might be valid only -+ for the parameter itself. */ -+ if (TREE_THIS_NOTRAP (old) && (!is_parm (TREE_OPERAND (old, 0)))) -+ TREE_THIS_NOTRAP (*tp) = 1; -+ REF_REVERSE_STORAGE_ORDER (*tp) = REF_REVERSE_STORAGE_ORDER (old); -+ *walk_subtrees = 0; -+ return NULL; -+ } -+ -+ /* Here is the "usual case". Copy this tree node, and then -+ tweak some special cases. */ -+ ipa_copy_tree_r (tp, walk_subtrees, NULL); -+ gcc_assert (!(TREE_CODE (*tp) == TARGET_EXPR && TREE_OPERAND (*tp, 3))); -+ if (TREE_CODE (*tp) == ADDR_EXPR) -+ { -+ /* TODO: If this used to be invariant, but is not any longer, -+ then regimplification is probably needed. */ -+ walk_tree (&TREE_OPERAND (*tp, 0), remap_gimple_op_r, data, NULL); -+ recompute_tree_invariant_for_addr_expr (*tp); -+ *walk_subtrees = 0; -+ } -+ } -+ /* TODO: maybe we need to update TREE_BLOCK (*tp). */ -+ -+ /* Keep iterating. */ -+ return NULL_TREE; -+} -+ -+static void -+create_cgraph_edge (cgraph_node *n, gimple *stmt) -+{ -+ gcall *call_stmt = dyn_cast (stmt); -+ basic_block bb = gimple_bb (stmt); -+ tree decl = gimple_call_fndecl (call_stmt); -+ if (!decl) -+ return; -+ struct cgraph_edge *e = n->create_edge (cgraph_node::get_create (decl), -+ call_stmt, bb->count); -+ /* TODO: maybe we need to store ipa_call_summary result. */ -+ ipa_call_summaries->get_create (e); -+} -+ -+/* Insert prefetch intrinsics in this function, return nonzero on success. */ -+ -+static int -+optimize_function (cgraph_node *n, function *fn) -+{ -+ /* In a given function, optimize only indirect memrefs with -+ the same incremental memref. -+ TODO: implement the optimization for other cases. */ -+ bool different_incrementals = false; -+ memref_t *first_mr = NULL; -+ memref_set used_mrs; -+ for (memref_set::const_iterator it = (*optimize_mrs_map)[fn]->begin (); -+ it != (*optimize_mrs_map)[fn]->end (); it++) -+ { -+ memref_t *mr = *it; -+ if (!first_mr) -+ first_mr = mr; -+ else if ((*mr_candidate_map)[first_mr] != (*mr_candidate_map)[mr]) -+ { -+ different_incrementals = true; -+ break; -+ } -+ for (memref_set::const_iterator it2 = mr->used_mrs.begin (); -+ it2 != mr->used_mrs.end (); it2++) -+ used_mrs.insert (*it2); -+ } -+ if (different_incrementals) -+ { -+ if (dump_file) -+ fprintf (dump_file, "It contains memrefs with different " -+ "incrementals. Skip the case.\n"); -+ return 0; -+ } -+ memref_t *inc_mr = (*mr_candidate_map)[first_mr]; -+ if (!inc_mr->stmts[0] || !gimple_assign_single_p (inc_mr->stmts[0])) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Incremental MR with unexpected stmt. " -+ "Skip the case.\n"); -+ return 0; -+ } -+ if (dump_file && !used_mrs.empty ()) -+ print_mrs_ids (used_mrs, "Common list of used mrs:\n"); -+ -+ /* Find a memref in used mrs which corresponds to the found incremental -+ memref. */ -+ memref_t *comp_mr = NULL; -+ for (memref_set::const_iterator it = used_mrs.begin (); -+ it != used_mrs.end (); it++) -+ { -+ bool c_offset; -+ if ((*it)->type != MR_SIMPLE || inc_mr->type != MR_SIMPLE -+ || !compatible_memrefs_p (*it, inc_mr, c_offset)) -+ continue; -+ if (c_offset) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Found compatible used MR (%d) and " -+ "incr MR (%d)\n", (*it)->mr_id, inc_mr->mr_id); -+ comp_mr = (*it); -+ } -+ } -+ if (!comp_mr || !comp_mr->stmts[0] -+ || !gimple_assign_single_p (comp_mr->stmts[0])) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Compatible MR in this function is not found " -+ " or it has unexpected stmt. Skip the case.\n"); -+ return 0; -+ } -+ -+ /* Filter out memrefs with the same memory references. -+ TODO: maybe do the same with used mrs. */ -+ vec vmrs = vNULL; -+ reduce_memref_set ((*optimize_mrs_map)[fn], vmrs); -+ -+ /* Find insertion place. Create new BB. */ -+ /* TODO: maybe it is useful to process also used_mrs. */ -+ basic_block dom_bb = NULL; -+ for (unsigned int i = 0; i < vmrs.length (); i++) -+ find_nearest_common_dominator (vmrs[i], dom_bb); -+ -+ if (!dom_bb) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Dominator bb for MRs is not found. " -+ "Skip the case.\n"); -+ return 0; -+ } -+ else if (dump_file) -+ fprintf (dump_file, "Dominator bb %d for MRs\n", dom_bb->index); -+ -+ split_block (dom_bb, (gimple *) NULL); -+ gimple_stmt_iterator gsi = gsi_last_bb (dom_bb); -+ -+ /* Create new inc var. Insert new_var = old_var + step * factor. */ -+ decl_map = new tree_map; -+ gcc_assert (comp_mr->stmts[0] && gimple_assign_single_p (comp_mr->stmts[0])); -+ tree inc_var = gimple_assign_lhs (comp_mr->stmts[0]); -+ gimple_seq stmts = NULL; -+ tree var_type = TREE_TYPE (inc_var); -+ enum tree_code inc_code; -+ if (TREE_CODE (var_type) == POINTER_TYPE) -+ inc_code = POINTER_PLUS_EXPR; -+ else -+ inc_code = PLUS_EXPR; -+ tree step = inc_mr->step; -+ unsigned dist_val = tree_to_uhwi (step) * param_ipa_prefetch_distance_factor; -+ tree dist = build_int_cst (TREE_TYPE (step), dist_val); -+ tree new_inc_var = gimple_build (&stmts, inc_code, var_type, inc_var, dist); -+ (*decl_map)[inc_var] = new_inc_var; -+ -+ /* Create other new vars. Insert new stmts. */ -+ struct walk_stmt_info wi; -+ stmt_set processed_stmts; -+ memref_tree_map mr_new_trees; -+ for (memref_set::const_iterator it = used_mrs.begin (); -+ it != used_mrs.end (); it++) -+ { -+ memref_t *mr = *it; -+ gimple *last_stmt = NULL; -+ if (mr == comp_mr) -+ continue; -+ for (int i = mr->stmts.length () - 1; i >= 0 ; i--) -+ { -+ if (processed_stmts.count (mr->stmts[i])) -+ continue; -+ processed_stmts.insert (mr->stmts[i]); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Copy stmt %d from used MR (%d):\n", -+ i, mr->mr_id); -+ print_gimple_stmt (dump_file, mr->stmts[i], 0); -+ } -+ /* Create a new copy of STMT and duplicate STMT's virtual -+ operands. */ -+ gimple *copy = gimple_copy (mr->stmts[i]); -+ gcc_checking_assert (!is_gimple_debug (copy)); -+ -+ /* Remap all the operands in COPY. */ -+ memset (&wi, 0, sizeof (wi)); -+ last_stmt = copy; -+ wi.info = copy; -+ walk_gimple_op (copy, remap_gimple_op_r, &wi); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Stmt %d after remap:\n",i); -+ print_gimple_stmt (dump_file, copy, 0); -+ } -+ gimple_seq_add_stmt (&stmts, copy); -+ } -+ gcc_assert (last_stmt); -+ mr_new_trees[mr] = gimple_assign_lhs (last_stmt); -+ if (dump_file) -+ { -+ fprintf (dump_file, "MR (%d) new mem: ", mr->mr_id); -+ print_generic_expr (dump_file, gimple_assign_lhs (last_stmt)); -+ fprintf (dump_file, "\n"); -+ } -+ } -+ /* On new load check page fault. */ -+ /* Insert prefetch instructions. */ -+ if (dump_file) -+ fprintf (dump_file, "Evaluate addresses and insert prefetch insn.\n"); -+ -+ vec pcalls = vNULL; -+ tree local; -+ switch (param_ipa_prefetch_locality) -+ { -+ case 0: -+ local = integer_zero_node; -+ break; -+ case 1: -+ local = integer_one_node; -+ break; -+ case 2: -+ local = build_int_cst (integer_type_node, 2); -+ break; -+ default: -+ case 3: -+ local = integer_three_node; -+ break; -+ } -+ for (unsigned int j = 0; j < vmrs.length (); j++) -+ { -+ memref_t *mr = vmrs[j]; -+ /* Don't need to copy the last stmt, since we insert prefetch insn -+ instead of it. */ -+ for (int i = mr->stmts.length () - 1; i >= 1 ; i--) -+ { -+ if (processed_stmts.count (mr->stmts[i])) -+ continue; -+ processed_stmts.insert (mr->stmts[i]); -+ -+ gimple *copy = gimple_copy (mr->stmts[i]); -+ gcc_checking_assert (!is_gimple_debug (copy)); -+ -+ /* Remap all the operands in COPY. */ -+ memset (&wi, 0, sizeof (wi)); -+ wi.info = copy; -+ walk_gimple_op (copy, remap_gimple_op_r, &wi); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Stmt %d after remap:\n",i); -+ print_gimple_stmt (dump_file, copy, 0); -+ } -+ gimple_seq_add_stmt (&stmts, copy); -+ } -+ gimple *last_stmt = mr->stmts[0]; -+ gcc_assert (last_stmt); -+ mr_new_trees[mr] = gimple_assign_lhs (last_stmt); -+ tree write_p = mr->is_store ? integer_one_node : integer_zero_node; -+ tree addr = get_mem_ref_address_ssa_name (mr->mem, NULL_TREE); -+ if (decl_map->count (addr)) -+ addr = (*decl_map)[addr]; -+ last_stmt = gimple_build_call (builtin_decl_explicit (BUILT_IN_PREFETCH), -+ 3, addr, write_p, local); -+ pcalls.safe_push (last_stmt); -+ gimple_seq_add_stmt (&stmts, last_stmt); -+ } -+ -+ gsi_insert_seq_after (&gsi, stmts, GSI_NEW_STMT); -+ delete decl_map; -+ -+ /* Modify cgraph inserting calls to prefetch intrinsics. */ -+ for (unsigned i = 0; i < pcalls.length (); i++) -+ create_cgraph_edge (n, pcalls[i]); -+ ipa_update_overall_fn_summary (n); -+ -+ return 1; -+} -+ -+static int -+insert_prefetch () -+{ -+ int res = 0; -+ cgraph_node *n; -+ FOR_EACH_DEFINED_FUNCTION (n) -+ { -+ function *fn = DECL_STRUCT_FUNCTION (n->decl); -+ if (!optimize_mrs_map->count (fn)) -+ continue; -+ if (dump_file) -+ fprintf (dump_file, "Optimize function %s\n", n->dump_name ()); -+ push_cfun (DECL_STRUCT_FUNCTION (n->decl)); -+ calculate_dominance_info (CDI_DOMINATORS); -+ res |= optimize_function (n, fn); -+ free_dominance_info (CDI_DOMINATORS); -+ pop_cfun (); -+ } -+ return res; -+} -+ -+static unsigned int -+ipa_prefetch (void) -+{ -+ if (!targetm.have_prefetch ()) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Prefetch is not supported by the target.\n"); -+ return 0; -+ } -+ -+ unsigned int ret = 0; -+ el_map = new edge_in_loop; -+ nl_map = new node_in_loop; -+ icn_map = new node_to_iedge_map; -+ nn_map = new node_to_node_map; -+ tm_map = new tree_memref_map; -+ fmrs_map = new funct_mrs_map; -+ mr_candidate_map = new memref_map; -+ optimize_mrs_map = new funct_mrs_map; -+ -+ max_mr_id = 0; -+ /* TODO: check if we really need this init. */ -+ if (!builtin_decl_explicit_p (BUILT_IN_PREFETCH)) -+ { -+ tree type = build_function_type_list (void_type_node, -+ const_ptr_type_node, NULL_TREE); -+ tree decl = add_builtin_function ("__builtin_prefetch", type, -+ BUILT_IN_PREFETCH, BUILT_IN_NORMAL, -+ NULL, NULL_TREE); -+ DECL_IS_NOVOPS (decl) = true; -+ set_builtin_decl (BUILT_IN_PREFETCH, decl, false); -+ } -+ -+ analyse_cgraph (); -+ prepare_indirect_call_info (); -+ propagate_loop_info_in_cgraph (); -+ collect_memory_references (); -+ analyse_loops (); -+ -+ /* TODO: implement some specific heuristics. */ -+ if (!optimize_mrs_map->empty ()) -+ ret = insert_prefetch (); -+ -+ delete el_map; -+ delete nl_map; -+ for (node_to_iedge_map::iterator it = icn_map->begin (); -+ it != icn_map->end (); ++it) -+ delete it->second; -+ delete icn_map; -+ for (node_to_node_map::iterator it = nn_map->begin (); -+ it != nn_map->end (); ++it) -+ delete it->second; -+ delete nn_map; -+ for (tree_memref_map::iterator it = tm_map->begin (); -+ it != tm_map->end (); ++it) -+ delete it->second; -+ delete tm_map; -+ for (funct_mrs_map::iterator it = fmrs_map->begin (); -+ it != fmrs_map->end (); ++it) -+ delete it->second; -+ delete fmrs_map; -+ delete mr_candidate_map; -+ delete optimize_mrs_map; -+ -+ /* TODO: maybe add other todos. */ -+ return ret | TODO_verify_all; -+} -+ -+const pass_data pass_data_ipa_prefetch = -+{ -+ SIMPLE_IPA_PASS, // type -+ "ipa_prefetch", // name -+ OPTGROUP_NONE, // optinfo_flags -+ TV_IPA_PREFETCH, // tv_id -+ 0, // properties_required -+ 0, // properties_provided -+ 0, // properties_destroyed -+ 0, // todo_flags_start -+ 0, // todo_flags_finish -+}; -+ -+class pass_ipa_prefetch : public simple_ipa_opt_pass -+{ -+public: -+ pass_ipa_prefetch (gcc::context *ctxt) -+ : simple_ipa_opt_pass (pass_data_ipa_prefetch, ctxt) -+ {} -+ -+ /* opt_pass methods: */ -+ virtual bool gate (function *); -+ virtual unsigned int execute (function *) -+ { -+ return ipa_prefetch (); -+ } -+}; // class pass_ipa_prefetch -+ -+bool -+pass_ipa_prefetch::gate (function *) -+{ -+ return (optimize >= 3 -+ && flag_ipa_prefetch -+ /* Don't bother doing anything if the program has errors. */ -+ && !seen_error () -+ && flag_lto_partition == LTO_PARTITION_ONE -+ /* Only enable struct optimizations in lto or whole_program. */ -+ && (in_lto_p || flag_whole_program)); -+} -+ -+} // anon namespace -+ -+simple_ipa_opt_pass * -+make_pass_ipa_prefetch (gcc::context *ctxt) -+{ -+ return new pass_ipa_prefetch (ctxt); -+} -diff --git a/gcc/ipa-sra.c b/gcc/ipa-sra.c -index 1cb30afc3..d7019ec42 100644 ---- a/gcc/ipa-sra.c -+++ b/gcc/ipa-sra.c -@@ -3090,6 +3090,14 @@ process_edge_to_unknown_caller (cgraph_edge *cs) - gcc_checking_assert (from_ifs); - isra_call_summary *csum = call_sums->get (cs); - -+ /* TODO: implement better support for call edges inserted after summary -+ collection but before sra wpa invocation. */ -+ if (!csum) -+ { -+ csum = call_sums->get_create (cs); -+ csum->m_return_ignored = true; -+ } -+ - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Processing an edge to an unknown caller from %s:\n", - cs->caller->dump_name ()); -diff --git a/gcc/params.opt b/gcc/params.opt -index 227175eef..1fe9513eb 100644 ---- a/gcc/params.opt -+++ b/gcc/params.opt -@@ -246,6 +246,14 @@ Maximum pieces that IPA-SRA tracks per formal parameter, as a consequence, also - Common Joined UInteger Var(param_ipa_sra_ptr_growth_factor) Init(2) Param Optimization - Maximum allowed growth of number and total size of new parameters that ipa-sra replaces a pointer to an aggregate with. - -+-param=ipa-prefetch-distance-factor= -+Common Joined UInteger Var(param_ipa_prefetch_distance_factor) Init(4) Param Optimization -+The factor represents the number of inductive variable incrementations to evaluate an indirect memory address for IPA prefetch. -+ -+-param=ipa-prefetch-locality= -+Common Joined UInteger Var(param_ipa_prefetch_locality) Init(3) Param Optimization -+The flag represents temporal locality values in the following way: 0:pstl1strm, 1:pstl3keep, 2:pstl2keep, 3:pstl1keep. -+ - -param=ira-loop-reserved-regs= - Common Joined UInteger Var(param_ira_loop_reserved_regs) Init(2) Param Optimization - The number of registers in each class kept unused by loop invariant motion. -diff --git a/gcc/passes.def b/gcc/passes.def -index 4e6a58634..b8739ab3c 100644 ---- a/gcc/passes.def -+++ b/gcc/passes.def -@@ -154,6 +154,7 @@ along with GCC; see the file COPYING3. If not see - NEXT_PASS (pass_ipa_icf); - NEXT_PASS (pass_ipa_devirt); - NEXT_PASS (pass_ipa_icp); -+ NEXT_PASS (pass_ipa_prefetch); - NEXT_PASS (pass_ipa_cp); - NEXT_PASS (pass_ipa_sra); - NEXT_PASS (pass_ipa_cdtor_merge); -diff --git a/gcc/timevar.def b/gcc/timevar.def -index 4059c57d5..98cf3f59e 100644 ---- a/gcc/timevar.def -+++ b/gcc/timevar.def -@@ -81,6 +81,7 @@ DEFTIMEVAR (TV_IPA_CONSTANT_PROP , "ipa cp") - DEFTIMEVAR (TV_IPA_INLINING , "ipa inlining heuristics") - DEFTIMEVAR (TV_IPA_FNSPLIT , "ipa function splitting") - DEFTIMEVAR (TV_IPA_COMDATS , "ipa comdats") -+DEFTIMEVAR (TV_IPA_PREFETCH , "ipa prefetch") - DEFTIMEVAR (TV_IPA_STRUCT_REORG , "ipa struct reorg optimization") - DEFTIMEVAR (TV_IPA_EXTEND_AUTO_PROFILE, "ipa extend auto profile") - DEFTIMEVAR (TV_IPA_OPT , "ipa various optimizations") -diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h -index 55d9dd668..a84924756 100644 ---- a/gcc/tree-pass.h -+++ b/gcc/tree-pass.h -@@ -513,6 +513,7 @@ 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_hsa (gcc::context *ctxt); - extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt); -+extern simple_ipa_opt_pass *make_pass_ipa_prefetch (gcc::context *ctxt); - extern simple_ipa_opt_pass *make_pass_ipa_struct_reorg (gcc::context *ctxt); - extern simple_ipa_opt_pass *make_pass_ipa_extend_auto_profile (gcc::context - *ctxt); --- -2.33.0 - diff --git a/0164-LLC-Allocation-Fix-some-bugs-and-remove-variable-pre.patch b/0164-LLC-Allocation-Fix-some-bugs-and-remove-variable-pre.patch index f1507fb..354f33c 100644 --- a/0164-LLC-Allocation-Fix-some-bugs-and-remove-variable-pre.patch +++ b/0164-LLC-Allocation-Fix-some-bugs-and-remove-variable-pre.patch @@ -46,7 +46,7 @@ index 56ad9a378..a8a2264ee 100644 +Common Report Var(flag_llc_allocate) Optimization Generate LLC hint instructions. - fipa-prefetch + fprofile diff --git a/gcc/params.opt b/gcc/params.opt index 792ca5c35..ef7bea311 100644 --- a/gcc/params.opt diff --git a/0173-IPA-Fix-test-completion-1.c.patch b/0173-IPA-Fix-test-completion-1.c.patch deleted file mode 100644 index 8444b44..0000000 --- a/0173-IPA-Fix-test-completion-1.c.patch +++ /dev/null @@ -1,24 +0,0 @@ -From d6ef1c0c182267d3ab68e3ae1d7f1a576a7bbb2a Mon Sep 17 00:00:00 2001 -From: Diachkov Ilia -Date: Wed, 20 Dec 2023 18:44:29 +0800 -Subject: [PATCH 2/5] [IPA] Fix test completion-1.c - ---- - gcc/testsuite/gcc.dg/completion-1.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/gcc/testsuite/gcc.dg/completion-1.c b/gcc/testsuite/gcc.dg/completion-1.c -index 64da64f1c..df2319c76 100644 ---- a/gcc/testsuite/gcc.dg/completion-1.c -+++ b/gcc/testsuite/gcc.dg/completion-1.c -@@ -2,6 +2,7 @@ - /* { dg-options "--completion=-fipa-ic" } */ - - /* { dg-begin-multiline-output "" } -+-fipa-ic - -fipa-icf - -fipa-icf-functions - -fipa-icf-variables --- -2.33.0 - diff --git a/0174-IPA-Fix-fails-on-checked-build-and-comments-from-rev.patch b/0174-IPA-Fix-fails-on-checked-build-and-comments-from-rev.patch deleted file mode 100644 index 54f9d56..0000000 --- a/0174-IPA-Fix-fails-on-checked-build-and-comments-from-rev.patch +++ /dev/null @@ -1,71 +0,0 @@ -From ed548cec9d8efe8ef742225c39f5d84aba4be81b Mon Sep 17 00:00:00 2001 -From: Diachkov Ilia WX1215920 -Date: Wed, 20 Dec 2023 13:53:47 +0300 -Subject: [PATCH 3/5] [IPA] Fix fails on checked build and comments from review - ---- - gcc/ipa-prefetch.c | 24 ++++++++++++++++++++++-- - gcc/params.opt | 4 ++-- - 2 files changed, 24 insertions(+), 4 deletions(-) - -diff --git a/gcc/ipa-prefetch.c b/gcc/ipa-prefetch.c -index 93483a6e8..d8bb9a251 100644 ---- a/gcc/ipa-prefetch.c -+++ b/gcc/ipa-prefetch.c -@@ -167,6 +167,7 @@ analyse_cgraph () - } - - /* TODO: maybe remove loop info here. */ -+ n->get_body (); - push_cfun (DECL_STRUCT_FUNCTION (n->decl)); - calculate_dominance_info (CDI_DOMINATORS); - loop_optimizer_init (LOOPS_NORMAL); -@@ -1540,9 +1541,28 @@ optimize_function (cgraph_node *n, function *fn) - return 0; - } - else if (dump_file) -- fprintf (dump_file, "Dominator bb %d for MRs\n", dom_bb->index); -+ { -+ fprintf (dump_file, "Dominator bb %d for MRs:\n", dom_bb->index); -+ gimple_dump_bb (dump_file, dom_bb, 0, dump_flags); -+ fprintf (dump_file, "\n"); -+ } -+ -+ /* Try to find comp_mr's stmt in the dominator bb. */ -+ gimple *last_used = NULL; -+ for (gimple_stmt_iterator si = gsi_last_bb (dom_bb); !gsi_end_p (si); -+ gsi_prev (&si)) -+ if (comp_mr->stmts[0] == gsi_stmt (si)) -+ { -+ last_used = gsi_stmt (si); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Last used stmt in dominator bb:\n"); -+ print_gimple_stmt (dump_file, last_used, 0); -+ } -+ break; -+ } - -- split_block (dom_bb, (gimple *) NULL); -+ split_block (dom_bb, last_used); - gimple_stmt_iterator gsi = gsi_last_bb (dom_bb); - - /* Create new inc var. Insert new_var = old_var + step * factor. */ -diff --git a/gcc/params.opt b/gcc/params.opt -index ef7bea311..76ae925fd 100644 ---- a/gcc/params.opt -+++ b/gcc/params.opt -@@ -251,8 +251,8 @@ Common Joined UInteger Var(param_ipa_prefetch_distance_factor) Init(4) Param Opt - The factor represents the number of inductive variable incrementations to evaluate an indirect memory address for IPA prefetch. - - -param=ipa-prefetch-locality= --Common Joined UInteger Var(param_ipa_prefetch_locality) Init(3) Param Optimization --The flag represents temporal locality values in the following way: 0:pstl1strm, 1:pstl3keep, 2:pstl2keep, 3:pstl1keep. -+Common Joined UInteger Var(param_ipa_prefetch_locality) Init(3) IntegerRange(0, 3) Param Optimization -+The flag represents temporal locality value between 0 and 3, the higher value means the higher temporal locality in the data. - - -param=ira-loop-reserved-regs= - Common Joined UInteger Var(param_ira_loop_reserved_regs) Init(2) Param Optimization --- -2.33.0 - diff --git a/0176-Fix-bugs-in-ICP-src-openEuler-gcc-I8PYBF-I8PYLL.patch b/0176-Fix-bugs-in-ICP-src-openEuler-gcc-I8PYBF-I8PYLL.patch deleted file mode 100644 index 0b69fe7..0000000 --- a/0176-Fix-bugs-in-ICP-src-openEuler-gcc-I8PYBF-I8PYLL.patch +++ /dev/null @@ -1,61 +0,0 @@ -From d2742041454dbd4c4c3c3e0a27b5fb26d1e05832 Mon Sep 17 00:00:00 2001 -From: Diachkov Ilia WX1215920 -Date: Thu, 21 Dec 2023 11:14:06 +0300 -Subject: [PATCH 5/5] Fix bugs in ICP (src-openEuler/gcc: I8PYBF, I8PYLL) - ---- - gcc/ipa-devirt.c | 17 +++++++++++++++-- - 1 file changed, 15 insertions(+), 2 deletions(-) - -diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c -index fbde7eb94..a18cbe36a 100644 ---- a/gcc/ipa-devirt.c -+++ b/gcc/ipa-devirt.c -@@ -4399,6 +4399,11 @@ print_type_set(unsigned ftype_uid, type_alias_map *map) - if (!map->count (ftype_uid)) - return; - type_set* s = (*map)[ftype_uid]; -+ if (!s) -+ { -+ fprintf (dump_file, "%d (no set)", ftype_uid); -+ return; -+ } - for (type_set::const_iterator it = s->begin (); it != s->end (); it++) - fprintf (dump_file, it == s->begin () ? "%d" : ", %d", *it); - } -@@ -4966,7 +4971,8 @@ analyze_assign_stmt (gimple *stmt) - { - 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) -+ || TREE_CODE (rhs) == ARRAY_REF || TREE_CODE (rhs) == PARM_DECL -+ || TREE_CODE (rhs) == LABEL_DECL) - rhs_type = build_pointer_type (TREE_TYPE (rhs)); - else if (TREE_CODE (rhs) == COMPONENT_REF) - { -@@ -4980,7 +4986,12 @@ analyze_assign_stmt (gimple *stmt) - gcc_assert (POINTER_TYPE_P (rhs_type)); - } - else -- gcc_unreachable(); -+ { -+ fprintf (dump_file, "\nUnsupported rhs type %s in assign stmt: ", -+ get_tree_code_name (TREE_CODE (rhs))); -+ print_gimple_stmt (dump_file, stmt, 0); -+ gcc_unreachable (); -+ } - } - else - rhs_type = TREE_TYPE (rhs); -@@ -5678,6 +5689,8 @@ merge_fs_map_for_ftype_aliases () - decl_set *d_set = it1->second; - tree type = (*type_uid_map)[it1->first]; - type_set *set = (*fta_map)[it1->first]; -+ if (!set) -+ continue; - for (type_set::const_iterator it2 = set->begin (); - it2 != set->end (); it2++) - { --- -2.33.0 - diff --git a/0177-Fix-sqlite-build.patch b/0177-Fix-sqlite-build.patch deleted file mode 100644 index e04e604..0000000 --- a/0177-Fix-sqlite-build.patch +++ /dev/null @@ -1,168 +0,0 @@ -From 71a992aca88f63ec1afb1608619b82a857d8e297 Mon Sep 17 00:00:00 2001 -From: Diachkov Ilia -Date: Fri, 22 Dec 2023 10:11:24 +0800 -Subject: [PATCH 1/4] Fix sqlite build - ---- - gcc/ipa-prefetch.c | 71 ++++++++++++++++++++++++++-------------------- - gcc/ipa-sra.c | 7 +++++ - 2 files changed, 47 insertions(+), 31 deletions(-) - -diff --git a/gcc/ipa-prefetch.c b/gcc/ipa-prefetch.c -index d8bb9a251..371702ad8 100644 ---- a/gcc/ipa-prefetch.c -+++ b/gcc/ipa-prefetch.c -@@ -1092,6 +1092,15 @@ analyse_loops () - memref_t *mr = it->first, *mr2 = it->second; - if (mr2 == NULL || !(*fmrs_map)[fn]->count (mr)) - continue; -+ /* For now optimize only MRs that mem is MEM_REF. -+ TODO: support other MR types. */ -+ if (TREE_CODE (mr->mem) != MEM_REF) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Skip MR %d: unsupported tree code = %s\n", -+ mr->mr_id, get_tree_code_name (TREE_CODE (mr->mem))); -+ continue; -+ } - if (!optimize_mrs_map->count (fn)) - (*optimize_mrs_map)[fn] = new memref_set; - (*optimize_mrs_map)[fn]->insert (mr); -@@ -1104,7 +1113,7 @@ analyse_loops () - it != (*optimize_mrs_map)[fn]->end (); it++) - { - memref_t *mr = *it, *mr2 = (*mr_candidate_map)[mr]; -- fprintf (dump_file, "MRs %d,%d with incremental offset ", -+ fprintf (dump_file, "MRs %d, %d with incremental offset ", - mr->mr_id, mr2->mr_id); - print_generic_expr (dump_file, mr2->offset); - fprintf (dump_file, "\n"); -@@ -1437,6 +1446,27 @@ remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data) - return NULL_TREE; - } - -+/* Copy stmt and remap its operands. */ -+ -+static gimple * -+gimple_copy_and_remap (gimple *stmt) -+{ -+ gimple *copy = gimple_copy (stmt); -+ gcc_checking_assert (!is_gimple_debug (copy)); -+ -+ /* Remap all the operands in COPY. */ -+ struct walk_stmt_info wi; -+ memset (&wi, 0, sizeof (wi)); -+ wi.info = copy; -+ walk_gimple_op (copy, remap_gimple_op_r, &wi); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Stmt copy after remap:\n"); -+ print_gimple_stmt (dump_file, copy, 0); -+ } -+ return copy; -+} -+ - static void - create_cgraph_edge (cgraph_node *n, gimple *stmt) - { -@@ -1585,7 +1615,6 @@ optimize_function (cgraph_node *n, function *fn) - /* Create other new vars. Insert new stmts. */ - struct walk_stmt_info wi; - stmt_set processed_stmts; -- memref_tree_map mr_new_trees; - for (memref_set::const_iterator it = used_mrs.begin (); - it != used_mrs.end (); it++) - { -@@ -1606,23 +1635,10 @@ optimize_function (cgraph_node *n, function *fn) - } - /* Create a new copy of STMT and duplicate STMT's virtual - operands. */ -- gimple *copy = gimple_copy (mr->stmts[i]); -- gcc_checking_assert (!is_gimple_debug (copy)); -- -- /* Remap all the operands in COPY. */ -- memset (&wi, 0, sizeof (wi)); -- last_stmt = copy; -- wi.info = copy; -- walk_gimple_op (copy, remap_gimple_op_r, &wi); -- if (dump_file) -- { -- fprintf (dump_file, "Stmt %d after remap:\n",i); -- print_gimple_stmt (dump_file, copy, 0); -- } -- gimple_seq_add_stmt (&stmts, copy); -+ last_stmt = gimple_copy_and_remap (mr->stmts[i]); -+ gimple_seq_add_stmt (&stmts, last_stmt); - } - gcc_assert (last_stmt); -- mr_new_trees[mr] = gimple_assign_lhs (last_stmt); - if (dump_file) - { - fprintf (dump_file, "MR (%d) new mem: ", mr->mr_id); -@@ -1664,23 +1680,11 @@ optimize_function (cgraph_node *n, function *fn) - continue; - processed_stmts.insert (mr->stmts[i]); - -- gimple *copy = gimple_copy (mr->stmts[i]); -- gcc_checking_assert (!is_gimple_debug (copy)); -- -- /* Remap all the operands in COPY. */ -- memset (&wi, 0, sizeof (wi)); -- wi.info = copy; -- walk_gimple_op (copy, remap_gimple_op_r, &wi); -- if (dump_file) -- { -- fprintf (dump_file, "Stmt %d after remap:\n",i); -- print_gimple_stmt (dump_file, copy, 0); -- } -+ gimple *copy = gimple_copy_and_remap (mr->stmts[i]); - gimple_seq_add_stmt (&stmts, copy); - } - gimple *last_stmt = mr->stmts[0]; - gcc_assert (last_stmt); -- mr_new_trees[mr] = gimple_assign_lhs (last_stmt); - tree write_p = mr->is_store ? integer_one_node : integer_zero_node; - tree addr = get_mem_ref_address_ssa_name (mr->mem, NULL_TREE); - if (decl_map->count (addr)) -@@ -1689,6 +1693,11 @@ optimize_function (cgraph_node *n, function *fn) - 3, addr, write_p, local); - pcalls.safe_push (last_stmt); - gimple_seq_add_stmt (&stmts, last_stmt); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Insert %d prefetch stmt:\n", j); -+ print_gimple_stmt (dump_file, last_stmt, 0); -+ } - } - - gsi_insert_seq_after (&gsi, stmts, GSI_NEW_STMT); -@@ -1827,7 +1836,7 @@ pass_ipa_prefetch::gate (function *) - /* Don't bother doing anything if the program has errors. */ - && !seen_error () - && flag_lto_partition == LTO_PARTITION_ONE -- /* Only enable struct optimizations in lto or whole_program. */ -+ /* Only enable prefetch optimizations in lto or whole_program. */ - && (in_lto_p || flag_whole_program)); - } - -diff --git a/gcc/ipa-sra.c b/gcc/ipa-sra.c -index d7019ec42..ee927bf6a 100644 ---- a/gcc/ipa-sra.c -+++ b/gcc/ipa-sra.c -@@ -3448,6 +3448,13 @@ param_splitting_across_edge (cgraph_edge *cs) - gcc_checking_assert (from_ifs && from_ifs->m_parameters); - - isra_call_summary *csum = call_sums->get (cs); -+ /* TODO: implement better support for call edges inserted after summary -+ collection but before sra wpa invocation. */ -+ if (!csum) -+ { -+ csum = call_sums->get_create (cs); -+ csum->m_return_ignored = true; -+ } - gcc_checking_assert (csum); - unsigned args_count = csum->m_arg_flow.length (); - isra_func_summary *to_ifs = func_sums->get (callee); --- -2.33.0 - diff --git a/0178-Fix-freetype-build.patch b/0178-Fix-freetype-build.patch deleted file mode 100644 index bb1dd41..0000000 --- a/0178-Fix-freetype-build.patch +++ /dev/null @@ -1,52 +0,0 @@ -From b187b3043c5a7aa96e6d1106e4b0f37d14c914a6 Mon Sep 17 00:00:00 2001 -From: Diachkov Ilia -Date: Fri, 22 Dec 2023 11:39:09 +0800 -Subject: [PATCH 2/4] Fix freetype build - ---- - gcc/ipa-prefetch.c | 17 +++++++++++++++-- - 1 file changed, 15 insertions(+), 2 deletions(-) - -diff --git a/gcc/ipa-prefetch.c b/gcc/ipa-prefetch.c -index 371702ad8..f91ac3edc 100644 ---- a/gcc/ipa-prefetch.c -+++ b/gcc/ipa-prefetch.c -@@ -1522,6 +1522,13 @@ optimize_function (cgraph_node *n, function *fn) - "Skip the case.\n"); - return 0; - } -+ if (!tree_fits_shwi_p (inc_mr->step)) -+ { -+ if (dump_file) -+ fprintf (dump_file, "Cannot represent incremental MR's step as " -+ "integer. Skip the case.\n"); -+ return 0; -+ } - if (dump_file && !used_mrs.empty ()) - print_mrs_ids (used_mrs, "Common list of used mrs:\n"); - -@@ -1607,13 +1614,19 @@ optimize_function (cgraph_node *n, function *fn) - else - inc_code = PLUS_EXPR; - tree step = inc_mr->step; -- unsigned dist_val = tree_to_uhwi (step) * param_ipa_prefetch_distance_factor; -+ HOST_WIDE_INT dist_val = tree_to_shwi (step) -+ * param_ipa_prefetch_distance_factor; - tree dist = build_int_cst (TREE_TYPE (step), dist_val); - tree new_inc_var = gimple_build (&stmts, inc_code, var_type, inc_var, dist); - (*decl_map)[inc_var] = new_inc_var; -+ if (dump_file) -+ { -+ fprintf (dump_file, "New distance value: %ld, new inc var: ", dist_val); -+ print_generic_expr (dump_file, new_inc_var); -+ fprintf (dump_file, "\n"); -+ } - - /* Create other new vars. Insert new stmts. */ -- struct walk_stmt_info wi; - stmt_set processed_stmts; - for (memref_set::const_iterator it = used_mrs.begin (); - it != used_mrs.end (); it++) --- -2.33.0 - diff --git a/0180-Fix-issue-I8QD9H.patch b/0180-Fix-issue-I8QD9H.patch deleted file mode 100644 index fddd01f..0000000 --- a/0180-Fix-issue-I8QD9H.patch +++ /dev/null @@ -1,115 +0,0 @@ -From 25f1ebeb88a4eae247f58488cac9da878f188d9f Mon Sep 17 00:00:00 2001 -From: Diachkov Ilia -Date: Sat, 23 Dec 2023 10:05:10 +0800 -Subject: [PATCH 4/4] Fix issue I8QD9H - ---- - gcc/ipa-prefetch.c | 64 +++++++++++++++++++++++++++------------------- - 1 file changed, 37 insertions(+), 27 deletions(-) - -diff --git a/gcc/ipa-prefetch.c b/gcc/ipa-prefetch.c -index f91ac3edc..a471b118e 100644 ---- a/gcc/ipa-prefetch.c -+++ b/gcc/ipa-prefetch.c -@@ -1467,6 +1467,31 @@ gimple_copy_and_remap (gimple *stmt) - return copy; - } - -+/* Copy and remap stmts listed in MR in reverse order to last_idx, skipping -+ processed ones. Insert new stmts to the sequence. */ -+ -+static gimple * -+gimple_copy_and_remap_memref_stmts (memref_t *mr, gimple_seq &stmts, -+ int last_idx, stmt_set &processed) -+{ -+ gimple *last_stmt = NULL; -+ for (int i = mr->stmts.length () - 1; i >= last_idx ; i--) -+ { -+ if (processed.count (mr->stmts[i])) -+ continue; -+ processed.insert (mr->stmts[i]); -+ if (dump_file) -+ { -+ fprintf (dump_file, "Copy stmt %d from used MR (%d):\n", -+ i, mr->mr_id); -+ print_gimple_stmt (dump_file, mr->stmts[i], 0); -+ } -+ last_stmt = gimple_copy_and_remap (mr->stmts[i]); -+ gimple_seq_add_stmt (&stmts, last_stmt); -+ } -+ return last_stmt; -+} -+ - static void - create_cgraph_edge (cgraph_node *n, gimple *stmt) - { -@@ -1606,7 +1631,16 @@ optimize_function (cgraph_node *n, function *fn) - decl_map = new tree_map; - gcc_assert (comp_mr->stmts[0] && gimple_assign_single_p (comp_mr->stmts[0])); - tree inc_var = gimple_assign_lhs (comp_mr->stmts[0]); -+ /* If old_var definition dominates the current use, just use it, otherwise -+ evaluate it just before new inc var evaluation. */ - gimple_seq stmts = NULL; -+ stmt_set processed_stmts; -+ if (!dominated_by_p (CDI_DOMINATORS, dom_bb, gimple_bb (comp_mr->stmts[0]))) -+ { -+ gimple *tmp = gimple_copy_and_remap_memref_stmts (comp_mr, stmts, 0, -+ processed_stmts); -+ inc_var = gimple_assign_lhs (tmp); -+ } - tree var_type = TREE_TYPE (inc_var); - enum tree_code inc_code; - if (TREE_CODE (var_type) == POINTER_TYPE) -@@ -1627,30 +1661,14 @@ optimize_function (cgraph_node *n, function *fn) - } - - /* Create other new vars. Insert new stmts. */ -- stmt_set processed_stmts; - for (memref_set::const_iterator it = used_mrs.begin (); - it != used_mrs.end (); it++) - { - memref_t *mr = *it; -- gimple *last_stmt = NULL; - if (mr == comp_mr) - continue; -- for (int i = mr->stmts.length () - 1; i >= 0 ; i--) -- { -- if (processed_stmts.count (mr->stmts[i])) -- continue; -- processed_stmts.insert (mr->stmts[i]); -- if (dump_file) -- { -- fprintf (dump_file, "Copy stmt %d from used MR (%d):\n", -- i, mr->mr_id); -- print_gimple_stmt (dump_file, mr->stmts[i], 0); -- } -- /* Create a new copy of STMT and duplicate STMT's virtual -- operands. */ -- last_stmt = gimple_copy_and_remap (mr->stmts[i]); -- gimple_seq_add_stmt (&stmts, last_stmt); -- } -+ gimple *last_stmt = gimple_copy_and_remap_memref_stmts (mr, stmts, 0, -+ processed_stmts); - gcc_assert (last_stmt); - if (dump_file) - { -@@ -1687,15 +1705,7 @@ optimize_function (cgraph_node *n, function *fn) - memref_t *mr = vmrs[j]; - /* Don't need to copy the last stmt, since we insert prefetch insn - instead of it. */ -- for (int i = mr->stmts.length () - 1; i >= 1 ; i--) -- { -- if (processed_stmts.count (mr->stmts[i])) -- continue; -- processed_stmts.insert (mr->stmts[i]); -- -- gimple *copy = gimple_copy_and_remap (mr->stmts[i]); -- gimple_seq_add_stmt (&stmts, copy); -- } -+ gimple_copy_and_remap_memref_stmts (mr, stmts, 1, processed_stmts); - gimple *last_stmt = mr->stmts[0]; - gcc_assert (last_stmt); - tree write_p = mr->is_store ? integer_one_node : integer_zero_node; --- -2.33.0 - diff --git a/0180-add-optimization-level-requirement-to-the-gate.patch b/0180-add-optimization-level-requirement-to-the-gate.patch new file mode 100644 index 0000000..068d36f --- /dev/null +++ b/0180-add-optimization-level-requirement-to-the-gate.patch @@ -0,0 +1,25 @@ +From aa66bcf2b684655d0fbcc6b4543ffef1b2e37288 Mon Sep 17 00:00:00 2001 +From: vchernon +Date: Thu, 28 Dec 2023 10:44:35 +0800 +Subject: [PATCH] add optimization level requirement to the gate. + +--- + gcc/crypto-accel.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/gcc/crypto-accel.c b/gcc/crypto-accel.c +index f4e810a6b..e7766a585 100644 +--- a/gcc/crypto-accel.c ++++ b/gcc/crypto-accel.c +@@ -2391,7 +2391,7 @@ public: + /* opt_pass methods: */ + virtual bool gate (function *) + { +- if (flag_crypto_accel_aes <= 0) ++ if (flag_crypto_accel_aes <= 0 || optimize < 1) + return false; + return targetm.get_v16qi_mode + && targetm.gen_rev32v16qi +-- +2.33.0 + diff --git a/gcc.spec b/gcc.spec index 54b21db..59c9929 100644 --- a/gcc.spec +++ b/gcc.spec @@ -61,7 +61,7 @@ Summary: Various compilers (C, C++, Objective-C, ...) Name: gcc Version: %{gcc_version} -Release: 48 +Release: 49 License: GPLv3+ and GPLv3+ with exceptions and GPLv2+ with exceptions and LGPLv2+ and BSD URL: https://gcc.gnu.org @@ -263,9 +263,7 @@ Patch152: 0152-Add-LLC-Allocation-Pass.patch Patch153: 0153-LLC-add-extending-outer-loop.patch Patch154: 0154-Loop-CRC32-Judge-null-on-pointers-and-solving-coding.patch Patch155: 0155-Add-maxmin-and-uzp1-uzp2-combining.patch -Patch156: 0156-add-icp-optimization.patch Patch157: 0157-Add-split-complex-instructions-pass.patch -Patch158: 0158-Implement-IPA-prefetch-optimization.patch Patch159: 0159-Implement-AES-pattern-matching.patch Patch160: 0160-AES-Add-lost-files.patch Patch161: 0161-Fix-lost-ftree-fold-phiopt-option-in-tests.patch @@ -280,14 +278,9 @@ Patch169: 0169-Struct-Reorg-Fix-several-bugs.patch Patch170: 0170-DFE-Add-escape-check.patch Patch171: 0171-phiopt-testsuite-Add-ftree-fold-phiopt-option-to-5-t.patch Patch172: 0172-minmax-Move-minmax-pattern-to-gimple.patch -Patch173: 0173-IPA-Fix-test-completion-1.c.patch -Patch174: 0174-IPA-Fix-fails-on-checked-build-and-comments-from-rev.patch Patch175: 0175-split-ldp-stp-Extending-and-refactoring-of-pass_spli.patch -Patch176: 0176-Fix-bugs-in-ICP-src-openEuler-gcc-I8PYBF-I8PYLL.patch -Patch177: 0177-Fix-sqlite-build.patch -Patch178: 0178-Fix-freetype-build.patch Patch179: 0179-rtl-ifcvt-refuse-to-rename-def-in-the-last-instructi.patch -Patch180: 0180-Fix-issue-I8QD9H.patch +Patch180: 0180-add-optimization-level-requirement-to-the-gate.patch %global gcc_target_platform %{_arch}-linux-gnu @@ -896,9 +889,7 @@ not stable, so plugins must be rebuilt any time GCC is updated. %patch153 -p1 %patch154 -p1 %patch155 -p1 -%patch156 -p1 %patch157 -p1 -%patch158 -p1 %patch159 -p1 %patch160 -p1 %patch161 -p1 @@ -913,12 +904,7 @@ not stable, so plugins must be rebuilt any time GCC is updated. %patch170 -p1 %patch171 -p1 %patch172 -p1 -%patch173 -p1 -%patch174 -p1 %patch175 -p1 -%patch176 -p1 -%patch177 -p1 -%patch178 -p1 %patch179 -p1 %patch180 -p1 @@ -2945,6 +2931,13 @@ end %doc rpm.doc/changelogs/libcc1/ChangeLog* %changelog +* Thu Dec 28 2023 Xiong Zhou - 10.3.1-49 +- Type:Revert & sync +- ID:NA +- SUG:NA +- DESC: Revert ICP and IPA prefetch related patches. + Sync patch from openeuler/gcc. + * Sat Dec 23 2023 Chenhui Zheng - 10.3.1-48 - Type:Sync - ID:NA -- Gitee