This patch adds Link-Time Optimization support for APEX custom instructions
by serializing pragma-registered builtin metadata across compilation units.

Since APEX intrinsics are registered dynamically via #pragma intrinsic
rather than predefined in the compiler, their metadata (name, mnemonic,
opcode, format) must be explicitly streamed to survive LTO.  The
implementation adds a custom LTO section (LTO_section_riscv_apex) and
hooks into the LTO write/read phases.

During output (arcv_apex_lto_write_section), referenced APEX builtins are
serialized to the LTO stream.  During input (arcv_apex_lto_read_section),
builtin metadata from all compilation units is deserialized and re-registered.
The arcv_apex_lto_lookup_builtin function detects conflicting definitions
across translation units and reports errors.

However, LTO exposes a latent hash-equality inconsistency in fold-const.cc
for BUILT_IN_MD and BUILT_IN_FRONTEND builtins: operand_equal_p considers
two builtin FUNCTION_DECLs equal when they share DECL_BUILT_IN_CLASS and
DECL_UNCHECKED_FUNCTION_CODE, but hash_operand only canonicalizes
BUILT_IN_NORMAL builtins and falls back to hashing DECL_UID for other
classes.  When APEX builtins are re-registered across TUs with distinct
FUNCTION_DECL nodes but identical builtin metadata, verify_hash_value
asserts due to the hash/equality mismatch.

This patch also fixes the issue by hashing non-BUILT_IN_NORMAL builtins using
(DECL_BUILT_IN_CLASS, DECL_UNCHECKED_FUNCTION_CODE), matching what
operand_equal_p compares.

gcc/ChangeLog:

        * config/riscv/arcv-builtins.cc (arcv_apex_lto_lookup_builtin): New
        function to find existing builtin and detect conflicts.
        (arcv_apex_lto_register_builtin): New function to register builtin
        during LTO read, skipping duplicates and reporting conflicts.
        (arcv_apex_lto_write_section): New function to serialize referenced
        APEX builtins to LTO stream.
        (arcv_apex_lto_read_section): New function to deserialize and
        re-register APEX builtins from all LTO input files.
        * config/riscv/riscv-builtins.cc (riscv_builtin_decl): Remove
        RISCV_BUILTIN_APEX sorry diagnostic for LTO.
        * config/riscv/riscv.h (TARGET_RISCV_APEX): Define.
        * lto-section-in.cc (lto_section_name): Add "riscv.apex" section name.
        * lto-streamer-out.cc (produce_asm_for_decls): Call
        arcv_apex_lto_write_section.
        * lto-streamer.h (enum lto_section_type): Add LTO_section_riscv_apex.
        (arcv_apex_lto_write_section): Declare.
        (arcv_apex_lto_read_section): Declare.

gcc/lto/ChangeLog:

        * lto-common.cc (read_cgraph_and_symbols): Call
        arcv_apex_lto_read_section after reading all input files.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/apex/apex.exp: Add LTO test support.
        * gcc.target/riscv/apex/arcv-apex-lto-err1_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-err1_1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-err2_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-err2_1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-err3_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-err3_1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-err4_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-err4_1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test1_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test1_1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test2_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test2_1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test3_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test3_1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test4_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test4_1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test5_0.c: New test.
        * gcc.target/riscv/apex/arcv-apex-lto-test5_1.c: New test.

Signed-off-by: Luis Silva <[email protected]>
---
 gcc/config/riscv/arcv-builtins.cc             | 233 ++++++++++++++++++
 gcc/config/riscv/riscv-builtins.cc            |   8 +-
 gcc/config/riscv/riscv.h                      |   2 +
 gcc/fold-const.cc                             |  12 +
 gcc/lto-section-in.cc                         |   1 +
 gcc/lto-streamer-out.cc                       |   6 +
 gcc/lto-streamer.h                            |   6 +
 gcc/lto/lto-common.cc                         |   6 +
 gcc/testsuite/gcc.target/riscv/apex/apex.exp  |  27 +-
 .../riscv/apex/arcv-apex-lto-err1_0.c         |  16 ++
 .../riscv/apex/arcv-apex-lto-err1_1.c         |   9 +
 .../riscv/apex/arcv-apex-lto-err2_0.c         |  17 ++
 .../riscv/apex/arcv-apex-lto-err2_1.c         |   9 +
 .../riscv/apex/arcv-apex-lto-err3_0.c         |  17 ++
 .../riscv/apex/arcv-apex-lto-err3_1.c         |   9 +
 .../riscv/apex/arcv-apex-lto-err4_0.c         |  19 ++
 .../riscv/apex/arcv-apex-lto-err4_1.c         |   9 +
 .../riscv/apex/arcv-apex-lto-test1_0.c        |  49 ++++
 .../riscv/apex/arcv-apex-lto-test1_1.c        |  25 ++
 .../riscv/apex/arcv-apex-lto-test2_0.c        |  36 +++
 .../riscv/apex/arcv-apex-lto-test2_1.c        |  15 ++
 .../riscv/apex/arcv-apex-lto-test3_0.c        |  49 ++++
 .../riscv/apex/arcv-apex-lto-test3_1.c        |  25 ++
 .../riscv/apex/arcv-apex-lto-test4_0.c        |  25 ++
 .../riscv/apex/arcv-apex-lto-test4_1.c        |  12 +
 .../riscv/apex/arcv-apex-lto-test5_0.c        |  52 ++++
 .../riscv/apex/arcv-apex-lto-test5_1.c        |  20 ++
 27 files changed, 708 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err1_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err1_1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err2_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err2_1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err3_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err3_1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err4_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err4_1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test1_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test1_1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test2_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test2_1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test3_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test3_1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test4_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test4_1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test5_0.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test5_1.c

diff --git a/gcc/config/riscv/arcv-builtins.cc 
b/gcc/config/riscv/arcv-builtins.cc
index d8a633fe56c..fee8146ec6f 100644
--- a/gcc/config/riscv/arcv-builtins.cc
+++ b/gcc/config/riscv/arcv-builtins.cc
@@ -40,6 +40,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "backend.h"
 #include "gimple.h"
 #include "gimple-iterator.h"
+#include "cgraph.h"
+#include "lto-streamer.h"
+#include "ipa-utils.h"
+#include "data-streamer.h"
 
 /* Specifies how a built-in function should be converted into rtl.  */
 enum riscv_builtin_type {
@@ -394,6 +398,235 @@ arcv_apex_register_builtin (tree fndecl, const char 
*fn_name,
   arcv_apex_emit_ext_directive (mnemonic, opcode, format_flags);
 }
 
+/* Check if an APEX builtin with the given characteristics already exists.
+   Returns the index if found with matching parameters, -1 if not found,
+   or -2 if found but with conflicting parameters.  */
+
+static int
+arcv_apex_lto_lookup_builtin (const char *fn_name, const char *mnemonic,
+                           unsigned int opcode, unsigned int format_flags,
+                           location_t loc)
+{
+  for (int i = 0; i < arcv_apex_builtin_index; i++)
+    {
+      const struct arcv_apex_builtin_description *d = &arcv_apex_builtins[i];
+
+      /* Skip if function name doesn't match.  */
+      if (strcmp (d->name, fn_name) != 0)
+       continue;
+
+      /* Function name matches - validate all parameters.  */
+      bool mismatch = false;
+
+      if (strcmp (d->mnemonic, mnemonic) != 0)
+       {
+         warning_at (loc, 0, "APEX builtin %qs already registered with "
+                     "different mnemonic: %qs vs %qs",
+                     fn_name, d->mnemonic, mnemonic);
+         mismatch = true;
+       }
+
+      if (d->opcode != opcode)
+       {
+         warning_at (loc, 0, "APEX builtin %qs already registered with "
+                     "different opcode: 0x%x vs 0x%x",
+                     fn_name, d->opcode, opcode);
+         mismatch = true;
+       }
+
+      if (d->format_flags != format_flags)
+       {
+         warning_at (loc, 0, "APEX builtin %qs already registered with "
+                     "different instruction formats: 0x%x vs 0x%x",
+                     fn_name, d->format_flags, format_flags);
+         mismatch = true;
+       }
+
+      /* Return -2 for conflicting registration, or index if match.  */
+      return mismatch ? -2 : i;
+    }
+
+  return -1;
+}
+
+static void
+arcv_apex_lto_register_builtin (const char *fn_name, const char *mnemonic,
+                             unsigned int opcode, unsigned int format_flags,
+                             bool emit_directive_p, tree fndecl)
+{
+  location_t loc = DECL_SOURCE_LOCATION (fndecl);
+
+  int existing_idx = arcv_apex_lto_lookup_builtin (fn_name, mnemonic, opcode,
+                                             format_flags, loc);
+
+  if (existing_idx == -2)
+    {
+      error_at (loc, "APEX builtin %qs has conflicting definitions across "
+            "compilation units", fn_name);
+      return;
+    }
+
+  if (existing_idx >= 0)
+    {
+      fndecl->function_decl.function_code
+       = (existing_idx << RISCV_BUILTIN_SHIFT) + RISCV_BUILTIN_APEX;
+      return;
+    }
+
+  gcc_assert (arcv_apex_builtin_index < arcv_apex_builtins_limit);
+
+  enum riscv_builtin_type builtin_type
+       = (format_flags & APEX_VOID) ? RISCV_BUILTIN_DIRECT_NO_TARGET
+                                    : RISCV_BUILTIN_DIRECT;
+
+  enum insn_code icode = arcv_apex_icode (format_flags);
+
+  /* Store APEX insn information.  */
+  arcv_apex_builtins[arcv_apex_builtin_index]
+    = { icode, xstrdup (fn_name), xstrdup (mnemonic), opcode,
+       builtin_type, format_flags };
+
+  fndecl->function_decl.function_code
+    = (arcv_apex_builtin_index << RISCV_BUILTIN_SHIFT) + RISCV_BUILTIN_APEX;
+
+  arcv_apex_builtin_index++;
+
+  if (emit_directive_p)
+    arcv_apex_emit_ext_directive (mnemonic, opcode, format_flags);
+}
+
+/* LTO serialization for APEX intrinsics.
+
+   APEX intrinsics are registered dynamically via #pragma intrinsic,
+   so their metadata must be explicitly serialized to survive LTO.
+   See lto-streamer-out.cc (produce_asm_for_decls) and
+   lto/lto-common.cc (read_cgraph_and_symbols) for the call sites.  */
+
+/* Serialize all referenced APEX intrinsics to the LTO stream.  */
+
+void
+arcv_apex_lto_write_section (void)
+{
+  if (arcv_apex_builtin_index == 0)
+    return;
+
+  auto_vec<int> used_indices;
+  for (int i = 0; i < arcv_apex_builtin_index; i++)
+    {
+      const struct arcv_apex_builtin_description *d = &arcv_apex_builtins[i];
+      gcc_assert (d->name);
+
+      symtab_node *snode
+       = symtab_node::get_for_asmname (get_identifier (d->name));
+
+      if (snode && snode->referred_to_p ())
+       used_indices.safe_push (i);
+    }
+
+  if (used_indices.is_empty ())
+    return;
+
+  struct lto_simple_output_block *ob
+    = lto_create_simple_output_block (LTO_section_riscv_apex);
+
+  if (!ob)
+    return;
+
+  streamer_write_uhwi_stream (ob->main_stream, used_indices.length ());
+
+  for (unsigned int idx = 0; idx < used_indices.length (); idx++)
+    {
+      const struct arcv_apex_builtin_description *d
+       = &arcv_apex_builtins[used_indices[idx]];
+
+      gcc_assert (d->name && d->mnemonic);
+
+      size_t name_len = strlen (d->name);
+      streamer_write_uhwi_stream (ob->main_stream, name_len);
+      for (size_t j = 0; j < name_len; j++)
+       streamer_write_char_stream (ob->main_stream, d->name[j]);
+
+      size_t mnemonic_len = strlen (d->mnemonic);
+      streamer_write_uhwi_stream (ob->main_stream, mnemonic_len);
+      for (size_t j = 0; j < mnemonic_len; j++)
+       streamer_write_char_stream (ob->main_stream, d->mnemonic[j]);
+
+      streamer_write_uhwi_stream (ob->main_stream, d->opcode);
+      streamer_write_uhwi_stream (ob->main_stream, d->format_flags);
+    }
+
+  lto_destroy_simple_output_block (ob);
+}
+
+/* Deserialize and re-register APEX intrinsics from all LTO input files.  */
+
+void
+arcv_apex_lto_read_section (void)
+{
+  struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  struct lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      const char *data;
+      size_t len;
+      class lto_input_block *ib
+       = lto_create_simple_input_block (file_data, LTO_section_riscv_apex,
+                                        &data, &len);
+
+      if (!ib)
+       continue;
+
+      unsigned int apex_count = streamer_read_uhwi (ib);
+      unsigned int registered_count = 0;
+
+      for (unsigned int i = 0; i < apex_count; i++)
+       {
+         unsigned int fn_name_len = streamer_read_uhwi (ib);
+         char *fn_name = XNEWVEC (char, fn_name_len + 1);
+         for (unsigned int k = 0; k < fn_name_len; k++)
+           fn_name[k] = streamer_read_uchar (ib);
+         fn_name[fn_name_len] = '\0';
+
+         unsigned int mnemonic_len = streamer_read_uhwi (ib);
+         char *mnemonic = XNEWVEC (char, mnemonic_len + 1);
+         for (unsigned int k = 0; k < mnemonic_len; k++)
+           mnemonic[k] = streamer_read_uchar (ib);
+         mnemonic[mnemonic_len] = '\0';
+
+         unsigned int opcode = streamer_read_uhwi (ib);
+         unsigned int format_flags = streamer_read_uhwi (ib);
+
+         symtab_node *snode
+           = symtab_node::get_for_asmname (get_identifier (fn_name));
+
+         cgraph_node *node = dyn_cast<cgraph_node *> (snode);
+         if (node)
+           {
+             tree fndecl = node->decl;
+             if (fndecl && TREE_CODE (fndecl) == FUNCTION_DECL)
+               {
+                 arcv_apex_lto_register_builtin (fn_name, mnemonic, opcode,
+                                                 format_flags, !flag_wpa,
+                                                 fndecl);
+                 registered_count++;
+               }
+           }
+
+         XDELETEVEC (fn_name);
+         XDELETEVEC (mnemonic);
+       }
+
+      if (registered_count != apex_count)
+       warning (0, "APEX LTO: expected %u intrinsics but registered %u",
+                apex_count, registered_count);
+
+      lto_destroy_simple_input_block (file_data, LTO_section_riscv_apex,
+                                     ib, data, len);
+    }
+}
+
 /* Validate the immediate argument passed to an APEX intrinsic.
    Used during builtin expansion.  */
 
diff --git a/gcc/config/riscv/riscv-builtins.cc 
b/gcc/config/riscv/riscv-builtins.cc
index d58915799f1..5a189fc6014 100644
--- a/gcc/config/riscv/riscv-builtins.cc
+++ b/gcc/config/riscv/riscv-builtins.cc
@@ -317,10 +317,10 @@ riscv_builtin_decl (unsigned int code, bool initialize_p 
ATTRIBUTE_UNUSED)
       return riscv_vector::builtin_decl (subcode, initialize_p);
 
     case RISCV_BUILTIN_APEX:
-      /* APEX intrinsics are pragma-registered and don't have
-        persistent decls.  */
-      sorry ("APEX intrinsics are not supported with LTO yet");
-      return error_mark_node;
+      /* Trick GCC to think that the function is defined.
+        The actual fndecl used is created after this
+        validation from the GIMPLE representation in LTO.  */
+      return integer_zero_node;
     }
   return error_mark_node;
 }
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index f9a5bb02337..ca8f9090660 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -1349,6 +1349,8 @@ typedef enum {
 #ifndef RISCV_APEX_H
 #define RISCV_APEX_H
 
+#define TARGET_RISCV_APEX 1
+
 /* APEX instruction format flags.  */
 enum apex_insn_format {
   APEX_NONE    = 0,
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 352083e146b..f7e30c11f3d 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -4118,6 +4118,18 @@ operand_compare::hash_operand (const_tree t, 
inchash::hash &hstate,
          t = builtin_decl_explicit (DECL_FUNCTION_CODE (t));
          code = TREE_CODE (t);
        }
+      /* For non-BUILT_IN_NORMAL builtins (e.g. BUILT_IN_MD), we cannot
+        canonicalize to a single representative decl, but we must still
+        hash consistently with operand_equal_p which compares by
+        built_in_class + function_code.  Hash those fields instead of
+        DECL_UID so that distinct FUNCTION_DECLs (e.g. from different
+        LTO translation units) that compare equal also hash equal.  */
+      else if (fndecl_built_in_p (t))
+       {
+         hstate.add_int (DECL_BUILT_IN_CLASS (t));
+         hstate.add_hwi (DECL_UNCHECKED_FUNCTION_CODE (t));
+         return;
+       }
       /* FALL THROUGH */
     default:
       if (POLY_INT_CST_P (t))
diff --git a/gcc/lto-section-in.cc b/gcc/lto-section-in.cc
index c57f84f02bd..892ce06bafd 100644
--- a/gcc/lto-section-in.cc
+++ b/gcc/lto-section-in.cc
@@ -57,6 +57,7 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "ipa_sra",
   "odr_types",
   "ipa_modref",
+  "riscv_apex",
 };
 
 /* Hooks so that the ipa passes can call into the lto front end to get
diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc
index 5338a952cb0..cc746e1c5aa 100644
--- a/gcc/lto-streamer-out.cc
+++ b/gcc/lto-streamer-out.cc
@@ -3474,6 +3474,12 @@ produce_asm_for_decls (void)
   /* Write command line opts.  */
   lto_write_options ();
 
+#ifdef TARGET_RISCV_APEX
+  /* RISC-V APEX intrinsics are registered dynamically via pragmas and
+     need explicit serialization to survive LTO.  See arcv-builtins.cc.  */
+  arcv_apex_lto_write_section ();
+#endif
+
   /* Deallocate memory and clean up.  */
   for (idx = 0; idx < num_fns; idx++)
     {
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index f8aa8465b7c..8e86816ec0a 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -225,6 +225,7 @@ enum lto_section_type
   LTO_section_ipa_sra,
   LTO_section_odr_types,
   LTO_section_ipa_modref,
+  LTO_section_riscv_apex,
   LTO_N_SECTION_TYPES          /* Must be last.  */
 };
 
@@ -966,6 +967,11 @@ void cl_optimization_stream_in (class data_in *,
 /* In lto-opts.cc.  */
 extern void lto_write_options (void);
 
+#ifdef TARGET_RISCV_APEX
+/* In config/riscv/arcv-builtins.cc.  */
+extern void arcv_apex_lto_write_section (void);
+extern void arcv_apex_lto_read_section (void);
+#endif
 
 /* Statistics gathered during LTO, WPA and LTRANS.  */
 extern struct lto_stats_d lto_stats;
diff --git a/gcc/lto/lto-common.cc b/gcc/lto/lto-common.cc
index cc513b84ef1..50047bec95b 100644
--- a/gcc/lto/lto-common.cc
+++ b/gcc/lto/lto-common.cc
@@ -2970,6 +2970,12 @@ read_cgraph_and_symbols (unsigned nfiles, const char 
**fnames)
   else
     ipa_read_summaries ();
 
+#ifdef TARGET_RISCV_APEX
+  /* RISC-V APEX intrinsics are registered dynamically via pragmas and
+     need explicit deserialization after LTO input.  See arcv-builtins.cc.  */
+  arcv_apex_lto_read_section ();
+#endif
+
   ggc_grow ();
 
   for (i = 0; all_file_decl_data[i]; i++)
diff --git a/gcc/testsuite/gcc.target/riscv/apex/apex.exp 
b/gcc/testsuite/gcc.target/riscv/apex/apex.exp
index 07a23718671..44e3acc2232 100644
--- a/gcc/testsuite/gcc.target/riscv/apex/apex.exp
+++ b/gcc/testsuite/gcc.target/riscv/apex/apex.exp
@@ -23,8 +23,31 @@ if ![istarget riscv*-*-*] then {
 
 # Load support libraries.
 load_lib gcc-dg.exp
+load_lib standard.exp
+load_lib lto.exp
+load_lib scanltrans.exp
 
-# Run tests.
+# Run regular single-file tests.
 dg-init
-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] "" ""
+set tests [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]]
+set non_lto_tests [list]
+foreach test $tests {
+    if {![string match "*arcv-apex-lto-*" $test]} {
+        lappend non_lto_tests $test
+    }
+}
+dg-runtest $non_lto_tests "" ""
 dg-finish
+
+# Run multi-file LTO tests if supported.
+if [check_effective_target_lto] {
+    lto_init no-mathlib
+
+    foreach src [lsort [find $srcdir/$subdir arcv-apex-lto-*_0.c]] {
+        if [runtest_file_p $runtests $src] {
+            lto-execute $src "riscv_apex_lto"
+        }
+    }
+
+    lto_finish
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err1_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err1_0.c
new file mode 100644
index 00000000000..263eec7f545
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err1_0.c
@@ -0,0 +1,16 @@
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O2 } } } */
+
+int apex_conflict (int, int);
+#pragma intrinsic (apex_conflict, "conflict", 50, "XS")
+
+extern int use_conflict (int, int);
+
+int
+main (void)
+{
+  return apex_conflict (1, 2) + use_conflict (5, 10);
+}
+
+/* { dg-lto-warning "APEX builtin 'apex_conflict' already registered with 
different opcode: 0x32 vs 0x33" "" { target *-*-* } 4 } */
+/* { dg-lto-error "APEX builtin 'apex_conflict' has conflicting definitions 
across compilation units" "" { target *-*-* } 4 } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err1_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err1_1.c
new file mode 100644
index 00000000000..9ec7e518ccb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err1_1.c
@@ -0,0 +1,9 @@
+
+int apex_conflict (int, int);
+#pragma intrinsic (apex_conflict, "conflict", 51, "XS")  /* Different opcode: 
51 vs 50 */
+
+int
+use_conflict (int a, int b)
+{
+  return apex_conflict (a, b);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err2_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err2_0.c
new file mode 100644
index 00000000000..757419626dc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err2_0.c
@@ -0,0 +1,17 @@
+/* Test LTO error detection for instruction name mismatch */
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O2 } } } */
+
+int apex_insn_mismatch (int, int);
+#pragma intrinsic (apex_insn_mismatch, "insn_a", 100, "XD")
+
+extern int use_insn_mismatch (int, int);
+
+int
+main (void)
+{
+  return apex_insn_mismatch (1, 2) + use_insn_mismatch (5, 10);
+}
+
+/* { dg-lto-warning "APEX builtin 'apex_insn_mismatch' already registered with 
different mnemonic: 'insn_a' vs 'insn_b'" "" { target *-*-* } 5 } */
+/* { dg-lto-error "APEX builtin 'apex_insn_mismatch' has conflicting 
definitions across compilation units" "" { target *-*-* } 5 } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err2_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err2_1.c
new file mode 100644
index 00000000000..7df812a4cd8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err2_1.c
@@ -0,0 +1,9 @@
+
+int apex_insn_mismatch (int, int);
+#pragma intrinsic (apex_insn_mismatch, "insn_b", 100, "XD")  /* Different 
instruction name */
+
+int
+use_insn_mismatch (int a, int b)
+{
+  return apex_insn_mismatch (a, b);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err3_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err3_0.c
new file mode 100644
index 00000000000..b005b5525e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err3_0.c
@@ -0,0 +1,17 @@
+/* Test LTO error detection for instruction format mismatch */
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O2 } } } */
+
+int apex_format_mismatch (int, int);
+#pragma intrinsic (apex_format_mismatch, "format_test", 20, "XS")
+
+extern int use_format_mismatch (int);
+
+int
+main (void)
+{
+  return apex_format_mismatch (1, 2) + use_format_mismatch (5);
+}
+
+/* { dg-lto-warning "APEX builtin 'apex_format_mismatch' already registered 
with different instruction formats: 0x2 vs 0x8" "" { target *-*-* } 5 } */
+/* { dg-lto-error "APEX builtin 'apex_format_mismatch' has conflicting 
definitions across compilation units" "" { target *-*-* } 5 } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err3_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err3_1.c
new file mode 100644
index 00000000000..4efb805f31f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err3_1.c
@@ -0,0 +1,9 @@
+
+int apex_format_mismatch (int, int);
+#pragma intrinsic (apex_format_mismatch, "format_test", 20, "XC")  /* 
Different format: XC vs XS */
+
+int
+use_format_mismatch (int a)
+{
+  return apex_format_mismatch (a, 10);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err4_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err4_0.c
new file mode 100644
index 00000000000..e9b3e8f742a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err4_0.c
@@ -0,0 +1,19 @@
+/* Test LTO error detection for multiple mismatches */
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O2 } } } */
+
+int apex_multi_mismatch (int, int);
+#pragma intrinsic (apex_multi_mismatch, "insn_original", 20, "XD")
+
+extern int use_multi_mismatch (int);
+
+int
+main (void)
+{
+  return apex_multi_mismatch (1, 2) + use_multi_mismatch (5);
+}
+
+/* { dg-lto-warning "APEX builtin 'apex_multi_mismatch' already registered 
with different mnemonic: 'insn_original' vs 'insn_different'" "" { target *-*-* 
} 5 } */
+/* { dg-lto-warning "APEX builtin 'apex_multi_mismatch' already registered 
with different opcode: 0x14 vs 0x15" "" { target *-*-* } 5 } */
+/* { dg-lto-warning "APEX builtin 'apex_multi_mismatch' already registered 
with different instruction formats: 0x1 vs 0x2" "" { target *-*-* } 5 } */
+/* { dg-lto-error "APEX builtin 'apex_multi_mismatch' has conflicting 
definitions across compilation units" "" { target *-*-* } 5 } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err4_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err4_1.c
new file mode 100644
index 00000000000..1134e68e355
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-err4_1.c
@@ -0,0 +1,9 @@
+
+int apex_multi_mismatch (int, int);
+#pragma intrinsic (apex_multi_mismatch, "insn_different", 21, "XS")  /* All 
different */
+
+int
+use_multi_mismatch (int a)
+{
+  return apex_multi_mismatch (a, 10);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test1_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test1_0.c
new file mode 100644
index 00000000000..fb0ae276394
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test1_0.c
@@ -0,0 +1,49 @@
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O2 -fdump-tree-optimized -save-temps } } } */
+
+int func_apex_xd (int, int);
+#pragma intrinsic (func_apex_xd, "insn_apex_xd", 50, "XD")
+
+int func_apex_xs (int, int);
+#pragma intrinsic (func_apex_xs, "insn_apex_xs", 32, "XS")
+
+int func_apex_xi (int);
+#pragma intrinsic (func_apex_xi, "insn_apex_xi", 20, "XI")
+
+int func_apex_xc (int, int);
+#pragma intrinsic (func_apex_xc, "insn_apex_xc", 21, "XC")
+
+extern int foo (int);
+
+int
+main (void)
+{
+  int x = func_apex_xd (1,2);
+  int y = func_apex_xs (x, 3);
+  int z = func_apex_xi (4);
+  int w = func_apex_xc (y, 5);
+  
+  return foo (y) + w + z;
+}
+
+/* Verify that APEX intrinsic calls survive LTO optimization. */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xd" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xs" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xi" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xc" "optimized" } } */
+
+/* Verify that the correct custom instructions are emitted in assembly. */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xd,50,XD" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xs,32,XS" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xi,20,XI" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xc,21,XC" } 
} */
+
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xd\\s+a\[0-9\]+,a\[0-9\]+,a\[0-9\]+" 2 } } */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xs\\s+a\[0-9\]+,a\[0-9\]+,3" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times "insn_apex_xi\\s+a\[0-9\]+,4" 1 } 
} */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xc\\s+a\[0-9\]+,a\[0-9\]+,5" 1 } } */
+
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xs\\s+a\[0-9\]+,a\[0-9\]+,8" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times "insn_apex_xi\\s+a\[0-9\]+,9" 1 } 
} */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xc\\s+a\[0-9\]+,a\[0-9\]+,10" 1 } } */
+
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test1_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test1_1.c
new file mode 100644
index 00000000000..e02a0cd5b1e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test1_1.c
@@ -0,0 +1,25 @@
+
+int func_apex_xd (int, int);
+#pragma intrinsic (func_apex_xd, "insn_apex_xd", 50, "XD")
+
+int func_apex_xs (int, int);
+#pragma intrinsic (func_apex_xs, "insn_apex_xs", 32, "XS")
+
+int func_apex_xi (int);
+#pragma intrinsic (func_apex_xi, "insn_apex_xi", 20, "XI")
+
+int func_apex_xc (int, int);
+#pragma intrinsic (func_apex_xc, "insn_apex_xc", 21, "XC")
+
+extern int foo (int);
+
+int
+foo (int val)
+{
+  int a = func_apex_xd (6, 7);
+  int b = func_apex_xs (val, 8);
+  int c = func_apex_xi (9);
+  int d = func_apex_xc (val, 10);
+  return a + b + c + d;
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test2_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test2_0.c
new file mode 100644
index 00000000000..c384a504425
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test2_0.c
@@ -0,0 +1,36 @@
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O2 -fdump-tree-optimized -save-temps } } } */
+
+int func_apex_xd (int, int);
+#pragma intrinsic (func_apex_xd, "insn_apex_xd", 50, "XD")
+
+int func_apex_xs (int, int);
+#pragma intrinsic (func_apex_xs, "insn_apex_xs", 32, "XS")
+
+extern int bar (int, int);
+
+int
+main (void)
+{
+  int x = func_apex_xd (1, 2);
+  int y = func_apex_xs (x, 3);
+  
+  return bar (x, y);
+}
+
+/* Verify that APEX intrinsic calls survive LTO optimization. */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xd" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xs" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xi" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xc" "optimized" } } */
+
+/* Verify that the correct custom instructions are emitted in assembly. */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xd,50,XD" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xs,32,XS" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xc,21,XC" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xi,20,XI" } 
} */
+
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xd\\s+a\[0-9\]+,a\[0-9\]+,a\[0-9\]+" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xs\\s+a\[0-9\]+,a\[0-9\]+,3" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times "insn_apex_xi\\s+a\[0-9\]+,4" 1 } 
} */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xc\\s+a\[0-9\]+,a\[0-9\]+,5" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test2_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test2_1.c
new file mode 100644
index 00000000000..493318bb53d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test2_1.c
@@ -0,0 +1,15 @@
+
+int func_apex_xc (int, int);
+#pragma intrinsic (func_apex_xc, "insn_apex_xc", 21, "XC")
+
+int func_apex_xi (int);
+#pragma intrinsic (func_apex_xi, "insn_apex_xi", 20, "XI")
+
+int
+bar (int a, int b)
+{
+  int c = func_apex_xi (4);
+  int d = func_apex_xc (a, 5);
+  return b + c + d;
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test3_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test3_0.c
new file mode 100644
index 00000000000..3d8de370c54
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test3_0.c
@@ -0,0 +1,49 @@
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O2 -fdump-tree-optimized -save-temps } } } */
+
+/* Pragma order in _0.c: XD, XS, XI, XC */
+int func_apex_xd (int, int);
+#pragma intrinsic (func_apex_xd, "insn_apex_xd", 50, "XD")
+
+int func_apex_xs (int, int);
+#pragma intrinsic (func_apex_xs, "insn_apex_xs", 32, "XS")
+
+int func_apex_xi (int);
+#pragma intrinsic (func_apex_xi, "insn_apex_xi", 20, "XI")
+
+int func_apex_xc (int, int);
+#pragma intrinsic (func_apex_xc, "insn_apex_xc", 21, "XC")
+
+extern int foo (int);
+
+int
+main (void)
+{
+  int x = func_apex_xd (1, 2);
+  int y = func_apex_xs (x, 3);
+  int z = func_apex_xi (4);
+  int w = func_apex_xc (y, 5);
+  
+  return foo (y) + w + z;
+}
+
+/* Verify that APEX intrinsic calls survive LTO optimization. */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xd" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xs" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xi" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xc" "optimized" } } */
+
+/* Verify that the correct custom instructions are emitted in assembly. */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xd,50,XD" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xs,32,XS" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xi,20,XI" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xc,21,XC" } 
} */
+
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xd\\s+a\[0-9\]+,a\[0-9\]+,a\[0-9\]+" 2 } } */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xs\\s+a\[0-9\]+,a\[0-9\]+,3" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times "insn_apex_xi\\s+a\[0-9\]+,4" 1 } 
} */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xc\\s+a\[0-9\]+,a\[0-9\]+,5" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xs\\s+a\[0-9\]+,a\[0-9\]+,8" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times "insn_apex_xi\\s+a\[0-9\]+,9" 1 } 
} */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xc\\s+a\[0-9\]+,a\[0-9\]+,10" 1 } } */
+
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test3_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test3_1.c
new file mode 100644
index 00000000000..9401957a0f9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test3_1.c
@@ -0,0 +1,25 @@
+
+int func_apex_xc (int, int);
+#pragma intrinsic (func_apex_xc, "insn_apex_xc", 21, "XC")
+
+int func_apex_xi (int);
+#pragma intrinsic (func_apex_xi, "insn_apex_xi", 20, "XI")
+
+int func_apex_xs (int, int);
+#pragma intrinsic (func_apex_xs, "insn_apex_xs", 32, "XS")
+
+int func_apex_xd (int, int);
+#pragma intrinsic (func_apex_xd, "insn_apex_xd", 50, "XD")
+
+extern int foo (int);
+
+int
+foo (int val)
+{
+  int a = func_apex_xd (6, 7);
+  int b = func_apex_xs (val, 8);
+  int c = func_apex_xi (9);
+  int d = func_apex_xc (val, 10);
+  return a + b + c + d;
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test4_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test4_0.c
new file mode 100644
index 00000000000..bcd9f68765a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test4_0.c
@@ -0,0 +1,25 @@
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O0 -fdump-tree-optimized -save-temps } } } */
+
+int func_apex_xc (int, int);
+#pragma intrinsic (func_apex_xc, "insn_apex_xc", 1, "XC")
+
+extern int foo (int);
+
+int
+main (void)
+{
+  int x = func_apex_xc (1, 2);
+  return foo (x);
+}
+
+/* Verify that APEX intrinsic calls survive LTO optimization. */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xc" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_xs" "optimized" } } */
+
+/* Verify that the correct custom instructions are emitted in assembly. */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xc,1,XC" } 
} */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_xs,2,XS" } 
} */
+
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xc\\s+a\[0-9\]+,+a\[0-9\]+,2" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_xs\\s+a\[0-9\]+,a\[0-9\]+,3" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test4_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test4_1.c
new file mode 100644
index 00000000000..66757f6bbe5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test4_1.c
@@ -0,0 +1,12 @@
+
+int func_apex_xs (int, int);
+#pragma intrinsic (func_apex_xs, "insn_apex_xs", 2, "XS")
+
+extern int foo (int);
+
+int
+foo (int val)
+{
+  return func_apex_xs (val, 3);
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test5_0.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test5_0.c
new file mode 100644
index 00000000000..ac522c3101c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test5_0.c
@@ -0,0 +1,52 @@
+/* { dg-lto-do link } */
+/* { dg-lto-options { { -flto -O2 -fdump-tree-optimized -save-temps } } } */
+
+/* Test that intrinsics optimized away don't cause crashes during LTO.
+   This test registers multiple intrinsics but only uses some of them,
+   allowing the optimizer to remove the unused ones. */
+
+int func_apex_used1 (int, int);
+#pragma intrinsic (func_apex_used1, "insn_apex_used1", 1, "XC")
+
+int func_apex_unused (int, int);
+#pragma intrinsic (func_apex_unused, "insn_apex_unused", 2, "XS")
+
+int func_apex_used2 (int, int);
+#pragma intrinsic (func_apex_used2, "insn_apex_used2", 3, "XC")
+
+extern int bar (int);
+
+static int
+func_apex_unused_helper (int a, int b)
+{
+  return func_apex_unused (a, b);
+}
+
+int
+main (void)
+{
+  /* Only use func_apex_used1 and func_apex_used2.
+     func_apex_unused should be optimized away. */
+  int x = func_apex_used1 (5, 10);
+  int y = func_apex_used2 (x, 15);
+  return bar (y);
+}
+
+/* Verify that only the used intrinsics appear in the optimized output. */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_used1" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_used2" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump "func_apex_from_other" "optimized" } } */
+
+/* Verify that the unused intrinsic is NOT in the optimized output. */
+/* { dg-final { scan-ltrans-tree-dump-not "func_apex_unused" "optimized" } } */
+/* { dg-final { scan-ltrans-tree-dump-not "func_apex_also_unused" "optimized" 
} } */
+
+/* Verify that only used intrinsics have .extInstruction directives. */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_used1,1,XC" 
} } */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction insn_apex_used2,3,XC" 
} } */
+/* { dg-final { scan-ltrans-assembler "\\.extInstruction 
insn_apex_from_other,4,XS" } } */
+
+/* Verify the actual instruction usage in assembly. */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_used1\\s+a\[0-9\]+,a\[0-9\]+,10" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_used2\\s+a\[0-9\]+,a\[0-9\]+,15" 1 } } */
+/* { dg-final { scan-ltrans-assembler-times 
"insn_apex_from_other\\s+a\[0-9\]+,a\[0-9\]+,20" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test5_1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test5_1.c
new file mode 100644
index 00000000000..9915f2306bf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-lto-test5_1.c
@@ -0,0 +1,20 @@
+
+int func_apex_from_other (int, int);
+#pragma intrinsic (func_apex_from_other, "insn_apex_from_other", 4, "XS")
+
+int func_apex_also_unused (int, int);
+#pragma intrinsic (func_apex_also_unused, "insn_apex_also_unused", 5, "XC")
+
+static int
+func_apex_also_unused_helper (int a, int b)
+{
+  return func_apex_also_unused (a, b);
+}
+
+int
+bar (int val)
+{
+  /* Only use func_apex_from_other.
+     func_apex_also_unused should be optimized away. */
+  return func_apex_from_other (val, 20);
+}
-- 
2.34.0

Reply via email to