diff options
author | CoprDistGit <infra@openeuler.org> | 2025-02-28 10:03:49 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2025-02-28 10:03:49 +0000 |
commit | 73127104a245052cd5cf29cdaaca3e5c32c70348 (patch) | |
tree | 8e28b63e478c43c252f18b49836dff7313affe54 /0304-Add-multi-version-lto-symbol-parse-cross-lto-units-i.patch | |
parent | 49d3feaf4665cdb07576fc1a2382a4d82a612d35 (diff) |
automatic import of gccopeneuler24.03_LTS_SP1
Diffstat (limited to '0304-Add-multi-version-lto-symbol-parse-cross-lto-units-i.patch')
-rw-r--r-- | 0304-Add-multi-version-lto-symbol-parse-cross-lto-units-i.patch | 963 |
1 files changed, 963 insertions, 0 deletions
diff --git a/0304-Add-multi-version-lto-symbol-parse-cross-lto-units-i.patch b/0304-Add-multi-version-lto-symbol-parse-cross-lto-units-i.patch new file mode 100644 index 0000000..cdd28b1 --- /dev/null +++ b/0304-Add-multi-version-lto-symbol-parse-cross-lto-units-i.patch @@ -0,0 +1,963 @@ +From f81a5b294711e3a420fe66702f0d9221332271c4 Mon Sep 17 00:00:00 2001 +From: h00564365 <huangxiaoquan1@huawei.com> +Date: Wed, 13 Nov 2024 17:18:01 +0800 +Subject: [PATCH 2/2] Add multi-version lto symbol parse, cross lto units + ipa-inline extension, and lto compression algorithm specified. + +--- + gcc/common.opt | 20 +++ + gcc/config/aarch64/aarch64.cc | 41 ++++++ + gcc/doc/tm.texi | 6 + + gcc/doc/tm.texi.in | 2 + + gcc/ipa-inline.cc | 141 ++++++++++++++++++- + gcc/lto-compress.cc | 6 +- + gcc/lto-section-in.cc | 5 + + gcc/lto-streamer-out.cc | 7 +- + gcc/lto-wrapper.cc | 4 + + gcc/optc-save-gen.awk | 57 ++++++++ + gcc/opth-gen.awk | 3 + + gcc/opts.cc | 46 ++++++ + gcc/target.def | 10 ++ + gcc/testsuite/gcc.dg/lto/binary-inline-1_0.c | 15 ++ + gcc/testsuite/gcc.dg/lto/binary-inline-1_1.c | 6 + + gcc/testsuite/gcc.dg/lto/binary-inline-2_0.c | 15 ++ + gcc/testsuite/gcc.dg/lto/binary-inline-2_1.c | 5 + + gcc/testsuite/gcc.dg/lto/binary-inline-3_0.c | 15 ++ + gcc/testsuite/gcc.dg/lto/binary-inline-3_1.c | 10 ++ + gcc/tree-streamer-in.cc | 58 +++++++- + lto-plugin/lto-plugin.c | 83 +++++++++++ + 21 files changed, 547 insertions(+), 8 deletions(-) + create mode 100644 gcc/testsuite/gcc.dg/lto/binary-inline-1_0.c + create mode 100644 gcc/testsuite/gcc.dg/lto/binary-inline-1_1.c + create mode 100644 gcc/testsuite/gcc.dg/lto/binary-inline-2_0.c + create mode 100644 gcc/testsuite/gcc.dg/lto/binary-inline-2_1.c + create mode 100644 gcc/testsuite/gcc.dg/lto/binary-inline-3_0.c + create mode 100644 gcc/testsuite/gcc.dg/lto/binary-inline-3_1.c + +diff --git a/gcc/common.opt b/gcc/common.opt +index be5fcc681..78cfc333a 100644 +--- a/gcc/common.opt ++++ b/gcc/common.opt +@@ -1928,6 +1928,21 @@ finline-atomics + Common Var(flag_inline_atomics) Init(1) Optimization + Inline __atomic operations when a lock free instruction sequence is available. + ++fmulti-version-lib= ++Common Joined Var(multi_version_lib_string) ++Use specify LTO stream in mode for specified target (object or lib). If there ++are multiple target files, use commas (,) to separate them and without spaces. ++ ++finline-force ++Common Var(flag_inline_force) Init(0) Optimization ++Force perform ipa inline when march options are incompatible between functions. ++ ++finline-force= ++Common Joined Var(force_inline_targets_string) ++Force perform ipa inline specified target(object or lib) when march options are ++incompatible between functions. If there are multiple target files, use commas ++(,) to separate them and without spaces. ++ + fcf-protection + Common RejectNegative Alias(fcf-protection=,full) + +@@ -2168,6 +2183,11 @@ flto-partition= + Common Joined RejectNegative Enum(lto_partition_model) Var(flag_lto_partition) Init(LTO_PARTITION_BALANCED) + Specify the algorithm to partition symbols and vars at linktime. + ++flto-compression-algorithm= ++Common Joined Var(lto_compression_algorithm) ++-flto-compression-algorithm=<format> Generate lto compression in zlib/zstd ++format <format>. ++ + ; The initial value of -1 comes from Z_DEFAULT_COMPRESSION in zlib.h. + flto-compression-level= + Common Joined RejectNegative UInteger Var(flag_lto_compression_level) Init(-1) IntegerRange(0, 19) +diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc +index 025a3c478..f095f17aa 100644 +--- a/gcc/config/aarch64/aarch64.cc ++++ b/gcc/config/aarch64/aarch64.cc +@@ -20829,6 +20829,44 @@ aarch64_option_print (FILE *file, int indent, struct cl_target_option *ptr) + arch->name, extension.c_str ()); + } + ++/* Implement TARGET_OPTION_PRINT_DIFF. */ ++ ++static void ++aarch64_option_print_diff (FILE *file, int indent, ++ struct cl_target_option *ptr1, ++ struct cl_target_option *ptr2) ++{ ++ const char *const cpu1 ++ = aarch64_get_tune_cpu (ptr1->x_selected_tune)->name; ++ const struct processor *arch1 = aarch64_get_arch (ptr1->x_selected_arch); ++ std::string extension1 ++ = aarch64_get_extension_string_for_isa_flags (ptr1->x_aarch64_isa_flags, ++ arch1->flags); ++ ++ const char *const cpu2 ++ = aarch64_get_tune_cpu (ptr2->x_selected_tune)->name; ++ const struct processor *arch2 = aarch64_get_arch (ptr2->x_selected_arch); ++ std::string extension2 ++ = aarch64_get_extension_string_for_isa_flags (ptr2->x_aarch64_isa_flags, ++ arch2->flags); ++ ++ if (cpu1 != cpu2 && (!cpu1 || !cpu2 || strcmp (cpu1, cpu2))) ++ fprintf (file, "%*s%s (%s/%s)\n", indent, "", ++ "cpu", cpu1 ? cpu1 : "(null)", cpu2 ? cpu2 : "(null)"); ++ ++ if (arch1->name != arch2->name ++ && (!arch1->name || !arch2->name || strcmp (arch1->name, arch2->name))) ++ fprintf (file, "%*s%s (%s/%s)\n", indent, "", ++ "arch", arch1->name ? arch1->name : "(null)", ++ arch2->name ? arch2->name : "(null)"); ++ ++ if (extension1 != extension2) ++ fprintf (file, "%*s%s (%s/%s)\n", indent, "", ++ "extension", ++ extension1.empty () ? "(null)" : extension1.c_str (), ++ extension2.empty () ? "(null)" : extension2.c_str ()); ++} ++ + static GTY(()) tree aarch64_previous_fndecl; + + void +@@ -31161,6 +31199,9 @@ aarch64_libgcc_floating_mode_supported_p + #undef TARGET_OPTION_PRINT + #define TARGET_OPTION_PRINT aarch64_option_print + ++#undef TARGET_OPTION_PRINT_DIFF ++#define TARGET_OPTION_PRINT_DIFF aarch64_option_print_diff ++ + #undef TARGET_OPTION_VALID_ATTRIBUTE_P + #define TARGET_OPTION_VALID_ATTRIBUTE_P aarch64_option_valid_attribute_p + +diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi +index 1e96521e6..50bbbbc42 100644 +--- a/gcc/doc/tm.texi ++++ b/gcc/doc/tm.texi +@@ -10589,6 +10589,12 @@ information in the @code{struct cl_target_option} structure for + function-specific options. + @end deftypefn + ++@deftypefn {Target Hook} void TARGET_OPTION_PRINT_DIFF (FILE *@var{file}, int @var{indent}, struct cl_target_option *@var{ptr1}, struct cl_target_option *@var{ptr2}) ++This hook is called to print diff additional target-specific ++information in the ptr1 and ptr2 @code{struct cl_target_option} structure for ++function-specific options. ++@end deftypefn ++ + @deftypefn {Target Hook} bool TARGET_OPTION_PRAGMA_PARSE (tree @var{args}, tree @var{pop_target}) + This target hook parses the options for @code{#pragma GCC target}, which + sets the target-specific options for functions that occur later in the +diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in +index 2dd515659..cfda60304 100644 +--- a/gcc/doc/tm.texi.in ++++ b/gcc/doc/tm.texi.in +@@ -6985,6 +6985,8 @@ on this implementation detail. + + @hook TARGET_OPTION_PRINT + ++@hook TARGET_OPTION_PRINT_DIFF ++ + @hook TARGET_OPTION_PRAGMA_PARSE + + @hook TARGET_OPTION_OVERRIDE +diff --git a/gcc/ipa-inline.cc b/gcc/ipa-inline.cc +index f8bb072c4..8d5cc9a84 100644 +--- a/gcc/ipa-inline.cc ++++ b/gcc/ipa-inline.cc +@@ -90,6 +90,8 @@ along with GCC; see the file COPYING3. If not see + the need for offline copy of the function. */ + + #include "config.h" ++#define INCLUDE_SET ++#define INCLUDE_STRING + #include "system.h" + #include "coretypes.h" + #include "backend.h" +@@ -127,6 +129,7 @@ typedef fibonacci_node <sreal, cgraph_edge> edge_heap_node_t; + static int overall_size; + static profile_count max_count; + static profile_count spec_rem; ++static std::set<std::string> force_inline_targets; + + /* Return false when inlining edge E would lead to violating + limits on function unit growth or stack usage growth. +@@ -222,6 +225,38 @@ caller_growth_limits (struct cgraph_edge *e) + return true; + } + ++/* Warn and prompt the user, and output only once for the file pair where ++ the function is located. */ ++ ++static void ++prompt_inline_failed_target_option_reason (struct cgraph_edge *e) ++{ ++ static std::set<std::pair<void*, void*>> address_pair_set; ++ if (e->inline_failed == CIF_TARGET_OPTION_MISMATCH ++ && !cl_target_option_eq_major (target_opts_for_fn (e->caller->decl), ++ target_opts_for_fn (e->callee->ultimate_alias_target ()->decl)) ++ && e->caller->lto_file_data ++ && e->callee->ultimate_alias_target ()->lto_file_data) ++ { ++ std::pair<void*, void*> addr_pair ++ = std::make_pair (&e->caller->lto_file_data, ++ &e->callee->ultimate_alias_target ()->lto_file_data); ++ if (address_pair_set.find (addr_pair) != address_pair_set.end ()) ++ return; ++ ++ address_pair_set.insert (addr_pair); ++ warning (0, "LTO objects caller in: %s, callee in: %s, not inlinable: %s." ++ " Try to use -finline-force=callee_object_or_lib_name to force " ++ "inline", e->caller->lto_file_data->file_name, ++ e->callee->ultimate_alias_target ()->lto_file_data->file_name, ++ cgraph_inline_failed_string (CIF_TARGET_OPTION_MISMATCH)); ++ ++ cl_target_option_print_diff ++ (stderr, 2, target_opts_for_fn (e->caller->decl), ++ target_opts_for_fn (e->callee->ultimate_alias_target ()->decl)); ++ } ++} ++ + /* Dump info about why inlining has failed. */ + + static void +@@ -254,6 +289,8 @@ report_inline_failed_reason (struct cgraph_edge *e) + (dump_file, 2, opts_for_fn (e->caller->decl), + opts_for_fn (e->callee->ultimate_alias_target ()->decl)); + } ++ ++ prompt_inline_failed_target_option_reason (e); + } + + /* Decide whether sanitizer-related attributes allow inlining. */ +@@ -310,6 +347,77 @@ sanitize_attrs_match_for_inline_p (const_tree caller, const_tree callee) + (opts_for_fn (caller->decl)->x_##flag \ + != opts_for_fn (callee->decl)->x_##flag) + ++/* find related node that has lto_file_data. */ ++ ++static cgraph_node * ++find_related_node_lto_file_data (cgraph_node *node) ++{ ++ cgraph_node *cur = node; ++ ++ while (cur->clone_of) ++ { ++ /* Switch to original node, for example xxx.constprop.x function. */ ++ cur = cur->clone_of; ++ if (cur->lto_file_data) ++ return cur; ++ ++ /* Find the lto_file_data information of referring. */ ++ struct ipa_ref *ref = NULL; ++ for (int i = 0; cur->iterate_referring (i, ref); i++) ++ { ++ struct cgraph_node *cnode = dyn_cast <cgraph_node *> (ref->referring); ++ if (cnode && cnode->lto_file_data) ++ return cnode; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* Determines whether to force inline or force inline only the specified ++ object. Use for 3 inline extensions: ++ 1) CIF_TARGET_OPTION_MISMATCH: cancel the restriction that the target options ++ of different compilation units are different. ++ 2) CIF_OVERWRITABLE: indicates that the function is available, which is ++ similar to the "inline" keyword indication. ++ 3) CIF_OPTIMIZATION_MISMATCH: cancel the check in the case of fp_expressions, ++ which is similar to the "always_inline" attribute. ++ */ ++ ++static bool ++can_force_inline_p (cgraph_node *callee) ++{ ++ if (!in_lto_p) ++ return false; ++ if (flag_inline_force) ++ return true; ++ if (force_inline_targets_string) ++ { ++ cgraph_node * node = callee; ++ std::string name = ""; ++ if (callee->ultimate_alias_target () == NULL ++ || callee->ultimate_alias_target ()->lto_file_data == NULL) ++ { ++ node = find_related_node_lto_file_data (callee); ++ if (node && node->lto_file_data) ++ name = node->lto_file_data->file_name; ++ } ++ else ++ name = node->ultimate_alias_target ()->lto_file_data->file_name; ++ while (!name.empty () && name.back () == '/') ++ name.erase (name.length () - 1); ++ if (name.empty ()) ++ return false; ++ size_t last_slash_pos = name.find_last_of ('/'); ++ if (last_slash_pos != std::string::npos ++ && last_slash_pos != name.length () - 1) ++ name = name.substr (last_slash_pos + 1); ++ if (force_inline_targets.find (name) != force_inline_targets.end ()) ++ return true; ++ } ++ return false; ++} ++ + /* Decide if we can inline the edge and possibly update + inline_failed reason. + We check whether inlining is possible at all and whether +@@ -352,7 +460,7 @@ can_inline_edge_p (struct cgraph_edge *e, bool report, + e->inline_failed = CIF_USES_COMDAT_LOCAL; + inlinable = false; + } +- else if (avail <= AVAIL_INTERPOSABLE) ++ else if (avail <= AVAIL_INTERPOSABLE && !can_force_inline_p (callee)) + { + e->inline_failed = CIF_OVERWRITABLE; + inlinable = false; +@@ -378,8 +486,8 @@ can_inline_edge_p (struct cgraph_edge *e, bool report, + inlinable = false; + } + /* Check compatibility of target optimization options. */ +- else if (!targetm.target_option.can_inline_p (caller->decl, +- callee->decl)) ++ else if (!can_force_inline_p (callee) ++ && !targetm.target_option.can_inline_p (caller->decl, callee->decl)) + { + e->inline_failed = CIF_TARGET_OPTION_MISMATCH; + inlinable = false; +@@ -495,7 +603,8 @@ can_inline_edge_by_limits_p (struct cgraph_edge *e, bool report, + bool always_inline = + (DECL_DISREGARD_INLINE_LIMITS (callee->decl) + && lookup_attribute ("always_inline", +- DECL_ATTRIBUTES (callee->decl))); ++ DECL_ATTRIBUTES (callee->decl))) ++ || can_force_inline_p (callee); + ipa_fn_summary *caller_info = ipa_fn_summaries->get (caller); + ipa_fn_summary *callee_info = ipa_fn_summaries->get (callee); + +@@ -2652,6 +2761,27 @@ flatten_remove_node_hook (struct cgraph_node *node, void *data) + removed->add (node); + } + ++/* Parse string that specify forced inlining, separated by commas. */ ++ ++static void ++parse_force_inline_targets_string (const char* s) ++{ ++ std::string target_string (s); ++ std::string delim = ","; ++ size_t start = 0; ++ size_t end = target_string.find (delim); ++ if (target_string.substr (start, end - start) == "") ++ return; ++ ++ while (end != std::string::npos) ++ { ++ force_inline_targets.insert (target_string.substr (start, end - start)); ++ start = end + delim.size (); ++ end = target_string.find (delim, start); ++ } ++ force_inline_targets.insert (target_string.substr (start, end - start)); ++} ++ + /* Decide on the inlining. We do so in the topological order to avoid + expenses on updating data structures. */ + +@@ -2665,6 +2795,9 @@ ipa_inline (void) + int cold; + bool remove_functions = false; + ++ if (force_inline_targets_string) ++ parse_force_inline_targets_string (force_inline_targets_string); ++ + order = XCNEWVEC (struct cgraph_node *, symtab->cgraph_count); + + if (dump_file) +diff --git a/gcc/lto-compress.cc b/gcc/lto-compress.cc +index 27f0992a8..f9d0722a9 100644 +--- a/gcc/lto-compress.cc ++++ b/gcc/lto-compress.cc +@@ -305,7 +305,11 @@ void + lto_end_compression (struct lto_compression_stream *stream) + { + #ifdef HAVE_ZSTD_H +- lto_compression_zstd (stream); ++ if (lto_compression_algorithm ++ && strcmp (lto_compression_algorithm, "zstd") == 0) ++ lto_compression_zstd (stream); ++ else ++ lto_compression_zlib (stream); + #else + lto_compression_zlib (stream); + #endif +diff --git a/gcc/lto-section-in.cc b/gcc/lto-section-in.cc +index ba87c7276..947f8eb15 100644 +--- a/gcc/lto-section-in.cc ++++ b/gcc/lto-section-in.cc +@@ -448,6 +448,11 @@ lto_free_function_in_decl_state_for_node (symtab_node *node) + lto_free_function_in_decl_state (*slot); + node->lto_file_data->function_decl_states->clear_slot (slot); + } ++ ++ /* In force inline case, keep lto file path information. */ ++ if (in_lto_p && (flag_inline_force || force_inline_targets_string)) ++ return; ++ + node->lto_file_data = NULL; + } + +diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc +index 471f35c31..a574f0f1e 100644 +--- a/gcc/lto-streamer-out.cc ++++ b/gcc/lto-streamer-out.cc +@@ -2666,7 +2666,12 @@ produce_lto_section () + free (section_name); + + #ifdef HAVE_ZSTD_H +- lto_compression compression = ZSTD; ++ lto_compression compression = ZLIB; ++ if (lto_compression_algorithm ++ && strcmp (lto_compression_algorithm, "zstd") == 0) ++ compression = ZSTD; ++ else ++ compression = ZLIB; + #else + lto_compression compression = ZLIB; + #endif +diff --git a/gcc/lto-wrapper.cc b/gcc/lto-wrapper.cc +index 155ccce57..2b1994652 100644 +--- a/gcc/lto-wrapper.cc ++++ b/gcc/lto-wrapper.cc +@@ -491,6 +491,8 @@ merge_and_complain (vec<cl_decoded_option> &decoded_options, + || decoded_options[j].opt_index == OPT_fpic) + { + /* -fno-pic in one unit implies -fno-pic everywhere. */ ++ /* The -fno-pic adjustment here should provide some information hints, ++ but may affect the use case test of deja. */ + if (decoded_options[j].value == 0) + j++; + /* If we have no pic option or merge in -fno-pic, we still may turn +@@ -534,6 +536,8 @@ merge_and_complain (vec<cl_decoded_option> &decoded_options, + || decoded_options[j].opt_index == OPT_fpie) + { + /* -fno-pie in one unit implies -fno-pie everywhere. */ ++ /* The -fno-pie adjustment here should provide some information hints, ++ but may affect the use case test of deja. */ + if (decoded_options[j].value == 0) + j++; + /* If we have no pie option or merge in -fno-pie, we still preserve +diff --git a/gcc/optc-save-gen.awk b/gcc/optc-save-gen.awk +index 7c012dd4e..94b85b331 100644 +--- a/gcc/optc-save-gen.awk ++++ b/gcc/optc-save-gen.awk +@@ -1043,6 +1043,10 @@ for (i = 0; i < n_target_string; i++) { + print ""; + } + ++print ""; ++print " if (targetm.target_option.print_diff)"; ++print " targetm.target_option.print_diff (file, indent, ptr1, ptr2);"; ++ + print "}"; + + print ""; +@@ -1160,6 +1164,59 @@ print " return true;"; + + print "}"; + ++print ""; ++print "/* Compare two target major options. */"; ++print "bool"; ++print "cl_target_option_eq_major (struct cl_target_option const *ptr1 ATTRIBUTE_UNUSED,"; ++print " struct cl_target_option const *ptr2 ATTRIBUTE_UNUSED)"; ++print "{"; ++n_target_val_major = 0; ++ ++for (i = 0; i < n_target_save; i++) { ++ var = target_save_decl[i]; ++ sub (" *=.*", "", var); ++ name = var; ++ type = var; ++ sub("^.*[ *]", "", name) ++ sub(" *" name "$", "", type) ++ if (target_save_decl[i] ~ "^const char \\*+[_" alnum "]+$") ++ continue; ++ if (target_save_decl[i] ~ " .*\\[.+\\]+$") ++ continue; ++ ++ var_target_val_major[n_target_val_major++] = name; ++} ++if (have_save) { ++ for (i = 0; i < n_opts; i++) { ++ if (flag_set_p("Save", flags[i])) { ++ name = var_name(flags[i]) ++ if(name == "") ++ name = "target_flags"; ++ ++ if(name in var_list_seen) ++ continue; ++ ++ var_list_seen[name]++; ++ otype = var_type_struct(flags[i]) ++ if (otype ~ "^const char \\**$") ++ continue; ++ var_target_val_major[n_target_val_major++] = "x_" name; ++ } ++ } ++} else { ++ var_target_val_major[n_target_val_major++] = "x_target_flags"; ++} ++ ++for (i = 0; i < n_target_val_major; i++) { ++ name = var_target_val_major[i] ++ print " if (ptr1->" name" != ptr2->" name ")"; ++ print " return false;"; ++} ++ ++print " return true;"; ++ ++print "}"; ++ + print ""; + print "/* Hash target options */"; + print "hashval_t"; +diff --git a/gcc/opth-gen.awk b/gcc/opth-gen.awk +index 8bba8ec45..cb016e85d 100644 +--- a/gcc/opth-gen.awk ++++ b/gcc/opth-gen.awk +@@ -330,6 +330,9 @@ print ""; + print "/* Compare two target option variables from a structure. */"; + print "extern bool cl_target_option_eq (const struct cl_target_option *, const struct cl_target_option *);"; + print ""; ++print "/* Compare two target major option variables from a structure. */"; ++print "extern bool cl_target_option_eq_major (const struct cl_target_option *, const struct cl_target_option *);"; ++print ""; + print "/* Free heap memory used by target option variables. */"; + print "extern void cl_target_option_free (struct cl_target_option *);"; + print ""; +diff --git a/gcc/opts.cc b/gcc/opts.cc +index d97f6079f..d9de8747c 100644 +--- a/gcc/opts.cc ++++ b/gcc/opts.cc +@@ -2611,6 +2611,32 @@ print_help (struct gcc_options *opts, unsigned int lang_mask, + lang_mask); + } + ++/* Checks whether the input forced inline string complies with the ++ restriction. */ ++ ++void ++check_force_inline_targets_string (const char *arg, location_t loc) ++{ ++ const int MAX_FORCE_INLINE_TARGET_LEN = 10000; ++ const int MAX_NUM_TARGET = 100; ++ __SIZE_TYPE__ length = strlen (arg); ++ int target_num = 1; ++ if (length > MAX_FORCE_INLINE_TARGET_LEN) ++ error_at (loc, ++ "input string exceeds %d characters to %<-finline_force=%> " ++ "option: %qs", MAX_FORCE_INLINE_TARGET_LEN, arg); ++ for (__SIZE_TYPE__ i = 0; i < length; i++) ++ { ++ if (arg[i] == ',') ++ { ++ target_num++; ++ if (target_num > MAX_NUM_TARGET) ++ error_at (loc, "input target exceeds %d to %<-finline_force=%> " ++ "option: %qs", MAX_NUM_TARGET, arg); ++ } ++ } ++} ++ + /* Handle target- and language-independent options. Return zero to + generate an "unknown option" message. Only options that need + extra handling need to be listed here; if you simply want +@@ -2952,6 +2978,14 @@ common_handle_option (struct gcc_options *opts, + value / 2); + break; + ++ case OPT_finline_force: ++ opts->x_force_inline_targets_string = value ? "" : NULL; ++ break; ++ ++ case OPT_finline_force_: ++ check_force_inline_targets_string (arg, loc); ++ break; ++ + case OPT_finstrument_functions_exclude_function_list_: + add_comma_separated_to_vector + (&opts->x_flag_instrument_functions_exclude_functions, arg); +@@ -3226,6 +3260,18 @@ common_handle_option (struct gcc_options *opts, + "unrecognized argument to %<-flto=%> option: %qs", arg); + break; + ++ case OPT_flto_compression_algorithm_: ++ if (atoi (arg) == 0 ++ && strcmp (arg, "zlib") != 0 ++#ifdef HAVE_ZSTD_H ++ && strcmp (arg, "zstd") != 0 ++#endif ++ ) ++ error_at (loc, ++ "unrecognized argument to %<-flto-compression-algorithm=%> " ++ "option: %qs", arg); ++ break; ++ + case OPT_w: + dc->dc_inhibit_warnings = true; + break; +diff --git a/gcc/target.def b/gcc/target.def +index 7183f363d..142858fa3 100644 +--- a/gcc/target.def ++++ b/gcc/target.def +@@ -6644,6 +6644,16 @@ information in the @code{struct cl_target_option} structure for\n\ + function-specific options.", + void, (FILE *file, int indent, struct cl_target_option *ptr), NULL) + ++/* Function to print any extra target state from the target options ++ structure. */ ++DEFHOOK ++(print_diff, ++ "This hook is called to print diff additional target-specific\n\ ++information in the ptr1 and ptr2 @code{struct cl_target_option} structure for\n\ ++function-specific options.", ++ void, (FILE *file, int indent, struct cl_target_option *ptr1, ++ struct cl_target_option *ptr2), NULL) ++ + /* Function to parse arguments to be validated for #pragma target, and to + change the state if the options are valid. If the first argument is + NULL, the second argument specifies the default options to use. Return +diff --git a/gcc/testsuite/gcc.dg/lto/binary-inline-1_0.c b/gcc/testsuite/gcc.dg/lto/binary-inline-1_0.c +new file mode 100644 +index 000000000..0b5cd5953 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/lto/binary-inline-1_0.c +@@ -0,0 +1,15 @@ ++/* { dg-lto-do link } */ ++/* { dg-require-effective-target shared } */ ++/* { dg-extra-ld-options {-shared -finline-force=c_lto_binary-inline-1_1.o} } */ ++/* { dg-lto-options {{-O3 -flto -march=armv8.2-a -fdump-ipa-inline-details}} } */ ++ ++extern double multi_op(float x); ++ ++double func_a (float x) ++{ ++ double res = 0; ++ res = multi_op (x); ++ return res; ++} ++ ++/* { dg-final { scan-wpa-ipa-dump "Inlined 1 calls" "inline" } } */ +diff --git a/gcc/testsuite/gcc.dg/lto/binary-inline-1_1.c b/gcc/testsuite/gcc.dg/lto/binary-inline-1_1.c +new file mode 100644 +index 000000000..8181384b7 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/lto/binary-inline-1_1.c +@@ -0,0 +1,6 @@ ++/* { dg-options "-march=armv8.3-a+sve+f64mm+crc+crypto+fp16+i8mm+simd" } */ ++ ++double multi_op (float x) ++{ ++ return x * 2 + 10; ++} +diff --git a/gcc/testsuite/gcc.dg/lto/binary-inline-2_0.c b/gcc/testsuite/gcc.dg/lto/binary-inline-2_0.c +new file mode 100644 +index 000000000..e873937d3 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/lto/binary-inline-2_0.c +@@ -0,0 +1,15 @@ ++/* { dg-lto-do link } */ ++/* { dg-require-effective-target shared } */ ++/* { dg-extra-ld-options {-shared -finline-force=c_lto_binary-inline-2_1.o} } */ ++/* { dg-lto-options {{-O3 -flto -fPIC -fdump-ipa-inline-details}} } */ ++ ++extern double multi_op(float x); ++ ++double func_a (float x) ++{ ++ double res = 0; ++ res = multi_op (x); ++ return res; ++} ++ ++/* { dg-final { scan-wpa-ipa-dump "Inlined 1 calls" "inline" } } */ +diff --git a/gcc/testsuite/gcc.dg/lto/binary-inline-2_1.c b/gcc/testsuite/gcc.dg/lto/binary-inline-2_1.c +new file mode 100644 +index 000000000..dc7c4fd9f +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/lto/binary-inline-2_1.c +@@ -0,0 +1,5 @@ ++ ++double multi_op (float x) ++{ ++ return x * 2 + 10; ++} +diff --git a/gcc/testsuite/gcc.dg/lto/binary-inline-3_0.c b/gcc/testsuite/gcc.dg/lto/binary-inline-3_0.c +new file mode 100644 +index 000000000..c78ba066d +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/lto/binary-inline-3_0.c +@@ -0,0 +1,15 @@ ++/* { dg-lto-do link } */ ++/* { dg-require-effective-target shared } */ ++/* { dg-extra-ld-options {-shared -finline-force=c_lto_binary-inline-3_1.o} } */ ++/* { dg-lto-options {{-O3 -flto -fdump-ipa-inline-details}} } */ ++ ++extern double multi_op(double x); ++ ++double func_a (double x) ++{ ++ double res = 0; ++ res = multi_op (x); ++ return res; ++} ++ ++/* { dg-final { scan-wpa-ipa-dump "Inlined 1 calls" "inline" } } */ +diff --git a/gcc/testsuite/gcc.dg/lto/binary-inline-3_1.c b/gcc/testsuite/gcc.dg/lto/binary-inline-3_1.c +new file mode 100644 +index 000000000..8b505fa0c +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/lto/binary-inline-3_1.c +@@ -0,0 +1,10 @@ ++/* { dg-options "-O2 -fno-math-errno" } */ ++ ++#include <math.h> ++ ++double multi_op (double x) ++{ ++ double a = 0; ++ a = sqrt (x); ++ return a * 2 + 10; ++} +diff --git a/gcc/tree-streamer-in.cc b/gcc/tree-streamer-in.cc +index a35a810f4..79f819ad8 100644 +--- a/gcc/tree-streamer-in.cc ++++ b/gcc/tree-streamer-in.cc +@@ -20,6 +20,9 @@ along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + + #include "config.h" ++#include <cstdio> ++#define INCLUDE_SET ++#define INCLUDE_STRING + #include "system.h" + #include "coretypes.h" + #include "backend.h" +@@ -36,6 +39,47 @@ along with GCC; see the file COPYING3. If not see + #include "asan.h" + #include "opts.h" + ++/* Parse string that specify forced inlining, separated by commas. */ ++static std::set<std::string> multi_version_libs; ++static void ++parse_multi_version_lib_string (const char* s) ++{ ++ std::string target_string (s); ++ std::string delim = ","; ++ size_t start = 0; ++ size_t end = target_string.find (delim); ++ if (target_string.substr (start, end - start) == "") ++ return; ++ ++ while (end != std::string::npos) ++ { ++ multi_version_libs.insert (target_string.substr (start, end - start)); ++ start = end + delim.size (); ++ end = target_string.find (delim, start); ++ } ++ multi_version_libs.insert (target_string.substr (start, end - start)); ++} ++ ++static bool ++target_lib_p (std::string name) ++{ ++ if (multi_version_libs.empty () && multi_version_lib_string) ++ parse_multi_version_lib_string (multi_version_lib_string); ++ if (multi_version_lib_string) ++ { ++ while (!name.empty () && name.back () == '/') ++ name.erase (name.length () - 1); ++ if (name.empty ()) ++ return false; ++ size_t last_slash_pos = name.find_last_of ('/'); ++ if (last_slash_pos != std::string::npos ++ && last_slash_pos != name.length () - 1) ++ name = name.substr (last_slash_pos + 1); ++ if (multi_version_libs.find (name) != multi_version_libs.end ()) ++ return true; ++ } ++ return false; ++} + + /* Read a STRING_CST from the string table in DATA_IN using input + block IB. */ +@@ -555,7 +599,12 @@ streamer_read_tree_bitfields (class lto_input_block *ib, + unpack_ts_translation_unit_decl_value_fields (data_in, &bp, expr); + + if (CODE_CONTAINS_STRUCT (code, TS_OPTIMIZATION)) +- cl_optimization_stream_in (data_in, &bp, TREE_OPTIMIZATION (expr)); ++ { ++ if (target_lib_p (data_in->file_data->file_name)) ++ cl_optimization_stream_in_prev (data_in, &bp, TREE_OPTIMIZATION (expr)); ++ else ++ cl_optimization_stream_in (data_in, &bp, TREE_OPTIMIZATION (expr)); ++ } + + if (CODE_CONTAINS_STRUCT (code, TS_CONSTRUCTOR)) + { +@@ -569,7 +618,12 @@ streamer_read_tree_bitfields (class lto_input_block *ib, + #ifndef ACCEL_COMPILER + if (CODE_CONTAINS_STRUCT (code, TS_TARGET_OPTION)) + { +- cl_target_option_stream_in (data_in, &bp, TREE_TARGET_OPTION (expr)); ++ if (target_lib_p (data_in->file_data->file_name)) ++ cl_target_option_stream_in_prev ( ++ data_in, &bp, TREE_TARGET_OPTION (expr)); ++ else ++ cl_target_option_stream_in (data_in, &bp, TREE_TARGET_OPTION (expr)); ++ + if (targetm.target_option.post_stream_in) + targetm.target_option.post_stream_in (TREE_TARGET_OPTION (expr)); + } +diff --git a/lto-plugin/lto-plugin.c b/lto-plugin/lto-plugin.c +index 33d49571d..b3301a8a4 100644 +--- a/lto-plugin/lto-plugin.c ++++ b/lto-plugin/lto-plugin.c +@@ -89,6 +89,10 @@ along with this program; see the file COPYING3. If not see + + #define LTO_SEGMENT_NAME "__GNU_LTO" + ++#define GCC_major_version 12 ++#define LTO_major_version GCC_major_version ++#define LTO_minor_version 0 ++ + /* Return true if STR string starts with PREFIX. */ + + static inline bool +@@ -118,6 +122,18 @@ struct plugin_symtab + unsigned long long id; + }; + ++/* Structure that represents LTO ELF section with information ++ about the format. */ ++ ++struct lto_section ++{ ++ int16_t major_version; ++ int16_t minor_version; ++ unsigned char slim_object; ++ unsigned char _padding; ++ uint16_t flags; ++}; ++ + /* Encapsulates object file data during symbol scan. */ + struct plugin_objfile + { +@@ -126,6 +142,7 @@ struct plugin_objfile + simple_object_read *objfile; + struct plugin_symtab *out; + const struct ld_plugin_input_file *file; ++ struct lto_section version; + }; + + /* All that we have to remember about a file. */ +@@ -216,6 +233,8 @@ static int gold_version = -1; + (in fact, only first letter of style arg is checked.) */ + static enum symbol_style sym_style = ss_none; + ++static bool multi_version_lto_parse = false; ++ + static void + check_1 (int gate, enum ld_plugin_level level, const char *text) + { +@@ -1078,6 +1097,59 @@ err: + return 0; + } + ++/* Process version section of an object file. */ ++ ++static int ++process_lto_version (void *data, const char *name, off_t offset, off_t length) ++{ ++ struct plugin_objfile *obj = (struct plugin_objfile *)data; ++ char *s; ++ char *secdatastart, *secdata; ++ ++ if (!startswith (name, ".gnu.lto_.lto")) ++ return 1; ++ ++ s = strrchr (name, '.'); ++ if (s) ++ sscanf (s, ".%" PRI_LL "x", &obj->out->id); ++ secdata = secdatastart = xmalloc (length); ++ offset += obj->file->offset; ++ if (offset != lseek (obj->file->fd, offset, SEEK_SET)) ++ goto err; ++ ++ do ++ { ++ ssize_t got = read (obj->file->fd, secdata, length); ++ if (got == 0) ++ break; ++ else if (got > 0) ++ { ++ secdata += got; ++ length -= got; ++ } ++ else if (errno != EINTR) ++ goto err; ++ } ++ while (length > 0); ++ if (length > 0) ++ goto err; ++ ++ struct lto_section *lto_info = (struct lto_section *)secdatastart; ++ obj->version = *lto_info; ++ ++ obj->found++; ++ free (secdatastart); ++ return 1; ++ ++err: ++ if (message) ++ message (LDPL_FATAL, "%s: corrupt object file", obj->file->name); ++ /* Force claim_file_handler to abandon this file. */ ++ obj->found = 0; ++ free (secdatastart); ++ return 0; ++} ++ + /* Process one section of an object file. */ + + static int +@@ -1223,6 +1295,15 @@ claim_file_handler (const struct ld_plugin_input_file *file, int *claimed) + if (obj.found == 0 && obj.offload == 0) + goto err; + ++ if (multi_version_lto_parse) ++ { ++ simple_object_find_sections (obj.objfile, process_lto_version, &obj, ++ &err); ++ if (obj.version.major_version != LTO_major_version ++ || obj.version.minor_version != LTO_minor_version) ++ goto err; ++ } ++ + if (obj.found > 1) + resolve_conflicts (<o_file.symtab, <o_file.conflicts); + +@@ -1366,6 +1447,8 @@ process_option (const char *option) + } + else if (startswith (option, "-ltrans-objects=")) + ltrans_objects = xstrdup (option + strlen ("-ltrans-objects=")); ++ else if (strcmp (option, "-multi-version-lto-parse") == 0) ++ multi_version_lto_parse = true; + else + { + int size; +-- +2.25.1 + |