Hi Luis: Really happy to see you address this issue correctly :)
I am not expert on the LTO part, but I would suggest you to split this into two part: 1) Implement a target hook to generalized the interface for target specific data for LTO section 2) Introduce APEX/RISC-V specific implementation I think introducing target specific on generic code is OK, but that should introduce via adding new hook. cc Richard B. and Jan H. for more input on this since this is out of RISC-V scope Luis Silva <[email protected]> 於 2026年5月25日週一 下午10:26寫道: > > 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 >
