summaryrefslogtreecommitdiff
path: root/0304-Add-multi-version-lto-symbol-parse-cross-lto-units-i.patch
diff options
context:
space:
mode:
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.patch963
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 (&lto_file.symtab, &lto_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
+