From aee1f348515f0e990fb375416480abfc4807374b Mon Sep 17 00:00:00 2001 From: Luis Silva Date: Mon, 20 Oct 2025 16:11:37 +0100 Subject: [PATCH 1/4] arcv: apex: Add LTO support for APEX intrinsics. APEX (ARC Processor Extension) intrinsics are registered dynamically via #pragma intrinsic directives rather than being statically defined. This creates a challenge for LTO where intrinsic definitions from different translation units must be preserved and made available during link-time optimization. This patch implements LTO serialization for APEX intrinsics by: 1. Creating a dedicated .gnu.lto_riscv_apex section to store APEX intrinsic metadata (name, mnemonic, opcode, instruction formats) 2. Writing all registered APEX intrinsics during the compilation phase 3. Reading and re-registering all APEX intrinsics during the LTO phase 4. Integrating with the LTO streamer infrastructure Without this support, LTO would lose APEX intrinsic definitions, causing "unavailable intrinsics" errors during link-time optimization. Signed-off-by: Luis Silva --- gcc/config.gcc | 2 +- gcc/config/riscv/riscv-apex-lto.cc | 254 +++++++++++++++++++++++++++++ gcc/config/riscv/riscv-builtins.cc | 158 ++++++++++++++++++ gcc/config/riscv/riscv-protos.h | 6 + gcc/config/riscv/t-riscv | 7 + gcc/lto-section-in.cc | 1 + gcc/lto-streamer-out.cc | 5 + gcc/lto-streamer.h | 6 + gcc/lto/lto-common.cc | 5 + 9 files changed, 443 insertions(+), 1 deletion(-) create mode 100644 gcc/config/riscv/riscv-apex-lto.cc diff --git a/gcc/config.gcc b/gcc/config.gcc index 6af7eadaaab4..744729503169 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -557,7 +557,7 @@ riscv*) extra_objs="riscv-builtins.o riscv-c.o riscv-sr.o riscv-shorten-memrefs.o riscv-selftests.o riscv-string.o" extra_objs="${extra_objs} riscv-v.o riscv-vsetvl.o riscv-vector-costs.o riscv-avlprop.o" extra_objs="${extra_objs} riscv-vector-builtins.o riscv-vector-builtins-shapes.o riscv-vector-builtins-bases.o sifive-vector-builtins-bases.o" - extra_objs="${extra_objs} thead.o riscv-target-attr.o riscv-zicfilp.o" + extra_objs="${extra_objs} thead.o riscv-target-attr.o riscv-zicfilp.o riscv-apex-lto.o" d_target_objs="riscv-d.o" extra_headers="riscv_vector.h riscv_crypto.h riscv_bitmanip.h riscv_th_vector.h sifive_vector.h" target_gtfiles="$target_gtfiles \$(srcdir)/config/riscv/riscv-vector-builtins.cc" diff --git a/gcc/config/riscv/riscv-apex-lto.cc b/gcc/config/riscv/riscv-apex-lto.cc new file mode 100644 index 000000000000..2b2952daddc4 --- /dev/null +++ b/gcc/config/riscv/riscv-apex-lto.cc @@ -0,0 +1,254 @@ +/* LTO serialization for RISC-V APEX intrinsics. + Copyright (C) 2025 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* RISC-V APEX (ARC Processor Extension) intrinsics are unique in GCC + because they are registered dynamically at compile-time via #pragma intrinsic + directives, rather than being statically defined like normal target builtins. + + This creates a challenge for LTO (Link Time Optimization): when compiling + with -flto, each translation unit may register different APEX intrinsics via + pragmas. During the link-time optimization phase, all these intrinsic + definitions must be preserved and made available for code generation. + + This file implements LTO serialization support for APEX intrinsics by: + + 1. Writing Phase (produce_asm_for_decls): + - Iterates through all registered APEX intrinsics + - Serializes their metadata (name, mnemonic, opcode, instruction formats) + - Writes to a dedicated .gnu.lto_riscv_apex section in object files + + 2. Reading Phase (read_cgraph_and_symbols): + - Reads .gnu.lto_riscv_apex sections from all input object files + - Reconstructs and re-registers all APEX intrinsics + - Makes them available for optimization and code generation + + Without this support, LTO would lose APEX intrinsic definitions, causing + unavailable intrinsics errors during link-time optimization. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "cgraph.h" +#include "lto-streamer.h" +#include "ipa-utils.h" +#include "data-streamer.h" +#include "stringpool.h" +#include "attribs.h" + +/* Declarations from riscv-builtins.cc for accessing + APEX builtin information. */ +extern int arcv_apex_get_builtin_count (void); +extern void arcv_apex_get_builtin_info (int, const char **, const char **, + unsigned int *, unsigned int *); +extern void arcv_apex_lto_register_builtin (const char *, const char *, + unsigned int, unsigned int, bool, + tree); +extern const char *arcv_apex_get_fn_name (unsigned int); + +/* Write RISC-V APEX intrinsic information to the LTO bytecode stream. + + This function is called during the compilation phase when producing LTO + bytecode. It serializes all APEX intrinsics that were registered via + #pragma directives in the current translation unit. + + The serialization format for each intrinsic is: + - Function name length (uhwi) + - Function name characters + - Instruction name length (uhwi) + - Instruction name characters + - Opcode (uhwi) + - Instruction format flags (uhwi) */ + +void +arcv_apex_lto_write_section (void) +{ + /* Get the number of registered APEX builtins in this compilation unit. */ + int apex_count = arcv_apex_get_builtin_count (); + + /* If no APEX builtins were registered via pragmas, skip section creation. + This is common for translation units that don't use APEX intrinsics. */ + if (apex_count == 0) + return; + + /* Collect indices of intrinsics that are actually used and not optimized + away. Use an auto_vec to avoid manual memory management. */ + auto_vec used_indices; + for (int i = 0; i < apex_count; i++) + { + const char *fn_name = arcv_apex_get_fn_name (i); + gcc_assert (fn_name); + + /* Check if the intrinsic is still referenced in the program. */ + symtab_node *snode = symtab_node::get_for_asmname ( + get_identifier (fn_name)); + + /* Only keep intrinsics that exist and are actually used. + Check if the symbol is referred to anywhere in the program. */ + if (snode && snode->referred_to_p ()) + used_indices.safe_push (i); + } + + /* If all intrinsics were optimized away, skip section creation. */ + if (used_indices.is_empty ()) + return; + + /* Create a new LTO section for APEX intrinsics. */ + struct lto_simple_output_block *ob + = lto_create_simple_output_block (LTO_section_riscv_apex); + + if (!ob) + return; + + /* Write the number of used APEX builtins so the reader knows + how many to expect. */ + streamer_write_uhwi_stream (ob->main_stream, used_indices.length ()); + + /* Serialize only the intrinsics that are still used. */ + for (unsigned int idx = 0; idx < used_indices.length (); idx++) + { + int i = used_indices[idx]; + const char *fn_name = NULL; + const char *insn_name = NULL; + unsigned int opcode = 0; + unsigned int insn_formats = 0; + + /* Get builtin information from the registry. */ + arcv_apex_get_builtin_info (i, &fn_name, &insn_name, + &opcode, &insn_formats); + + /* Function and instruction names must exist. */ + gcc_assert (fn_name && insn_name); + + /* Write function name as length-prefixed string. */ + size_t name_len = strlen (fn_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, fn_name[j]); + + /* Write instruction name as length-prefixed string. */ + size_t insn_name_len = strlen (insn_name); + streamer_write_uhwi_stream (ob->main_stream, insn_name_len); + for (size_t j = 0; j < insn_name_len; j++) + streamer_write_char_stream (ob->main_stream, insn_name[j]); + + /* Write opcode value. */ + streamer_write_uhwi_stream (ob->main_stream, opcode); + + /* Write instruction format flags. */ + streamer_write_uhwi_stream (ob->main_stream, insn_formats); + } + + lto_destroy_simple_output_block (ob); +} + +/* Read RISC-V APEX intrinsic information from the LTO bytecode stream. + + This function is called during the link-time optimization phase. It reads + the .gnu.lto_riscv_apex sections from all input object files and + re-registers all APEX intrinsics so they are available for optimization + and code generation in the LTRANS phase. + + The function iterates over all input files, reads their APEX sections, + and re-registers each intrinsic by calling riscv_register_apex_builtin. */ + +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; + + /* Process each input file's APEX section. */ + 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); + + /* Skip files that don't have an APEX section + (did not use APEX intrinsics). */ + if (!ib) + continue; + + /* Read the count of APEX builtins in this file. */ + unsigned int apex_count = streamer_read_uhwi (ib); + unsigned int registered_count = 0; + + /* Deserialize each APEX intrinsic. */ + for (unsigned int i = 0; i < apex_count; i++) + { + /* Read function name. */ + 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'; + + /* Read instruction name. */ + unsigned int insn_name_len = streamer_read_uhwi (ib); + char *insn_name = XNEWVEC (char, insn_name_len + 1); + for (unsigned int k = 0; k < insn_name_len; k++) + insn_name[k] = streamer_read_uchar (ib); + insn_name[insn_name_len] = '\0'; + + /* Read opcode and instruction format flags. */ + unsigned int opcode = streamer_read_uhwi (ib); + unsigned int insn_formats = streamer_read_uhwi (ib); + + /* Look up the function declaration in the merged symbol table. + During LTO, all function declarations from all compilation units + are merged into a single global symbol table. */ + symtab_node *snode = symtab_node::get_for_asmname ( + get_identifier (fn_name)); + + cgraph_node *node = dyn_cast (snode); + if (node) + { + tree fndecl = node->decl; + if (fndecl && TREE_CODE (fndecl) == FUNCTION_DECL) + { + /* Re-register the intrinsic so it's available for code generation. + The !flag_wpa parameter controls whether to print .extInstruction + directives (only needed in final LTRANS phase, not WPA phase). */ + arcv_apex_lto_register_builtin (fn_name, insn_name, opcode, + insn_formats, !flag_wpa, fndecl); + registered_count++; + } + } + + /* Free allocated memory. */ + XDELETEVEC (fn_name); + XDELETEVEC (insn_name); + } + + /* Verify we successfully re-registered all APEX intrinsics + from the section. If this fails, the LTO section is + likely corrupted. */ + gcc_assert (registered_count == apex_count); + + lto_destroy_simple_input_block (file_data, LTO_section_riscv_apex, + ib, data, len); + } +} + diff --git a/gcc/config/riscv/riscv-builtins.cc b/gcc/config/riscv/riscv-builtins.cc index 51f47e4bdf87..0cf09d9f6c0a 100644 --- a/gcc/config/riscv/riscv-builtins.cc +++ b/gcc/config/riscv/riscv-builtins.cc @@ -373,6 +373,12 @@ riscv_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED) case RISCV_BUILTIN_VECTOR: return riscv_vector::builtin_decl (subcode, initialize_p); + + case RISCV_BUILTIN_APEX: + /* 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; } @@ -606,6 +612,15 @@ riscv_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update) *update = NULL_TREE; } + +/* Return the function name associated with a given subcode. */ + +const char* +arcv_apex_get_fn_name (unsigned int subcode) +{ + return arcv_apex_builtins[subcode].name; +} + /* Return the APEX instruction name associated with a given subcode operand. The subcode is an unsigned integer extracted from `op` that indexes into @@ -648,6 +663,61 @@ arcv_apex_format_supports_p (unsigned int subcode, unsigned int insn_format) return (d->insn_formats & insn_format); } +/* 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_find_builtin (const char *fn_name, const char *insn_name, + unsigned int opcode, unsigned int insn_formats, + location_t loc) +{ + for (int i = 0; i < arcv_apex_builtin_index; i++) + { + const struct arcv_apex_builtin_description *d = &arcv_apex_builtins[i]; + + /* Check if function name matches. */ + if (strcmp (d->name, fn_name) == 0) + { + /* Validate all parameters match. */ + bool mismatch = false; + + if (strcmp (d->insn_name, insn_name) != 0) + { + warning_at (loc, 0, "APEX builtin %qs already registered with different " + "instruction name: %qs vs %qs", + fn_name, d->insn_name, insn_name); + 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->insn_formats != insn_formats) + { + warning_at (loc, 0, "APEX builtin %qs already registered with different " + "instruction formats: 0x%x vs 0x%x", + fn_name, d->insn_formats, insn_formats); + mismatch = true; + } + + /* Return special value for conflicting registration. */ + if (mismatch) + return -2; + + /* Found matching registration. */ + return i; + } + } + + return -1; /* Not found. */ +} + /* Set APEX operand flags for a built-in function. This function inspects the function prototype in FNDECL and sets the appropriate operand flags in INSN_FORMAT: @@ -1023,3 +1093,91 @@ arcv_apex_init_builtin (tree fndecl, const char *fn_name, arcv_apex_builtin_index++; } + + +void +arcv_apex_lto_register_builtin (const char *fn_name, const char *insn_name, + unsigned int opcode, unsigned int insn_formats, + bool wpa_p, tree fndecl) +{ + /* Get source location from function declaration. */ + location_t loc = DECL_SOURCE_LOCATION (fndecl); + + /* Check if this APEX builtin is already registered. */ + int existing_idx = arcv_apex_lto_find_builtin (fn_name, insn_name, opcode, + insn_formats, loc); + + if (existing_idx >= 0) + { + /* Assert that the function code is the same as the existing index. */ + gcc_assert (fndecl->function_decl.function_code + == (unsigned) ((existing_idx << RISCV_BUILTIN_SHIFT) + + RISCV_BUILTIN_APEX)); + return; + } + + if (existing_idx == -2) + { + /* Conflicting registration - warnings already issued. */ + error_at (loc, "APEX builtin %qs has conflicting definitions across " + "compilation units", fn_name); + return; + } + + /* Not registered yet - proceed with new registration. */ + + /* Check for array overflow before storing. */ + gcc_assert (arcv_apex_builtin_index < arcv_apex_builtins_limit); + + /* Calculate instruction suffix for auto-resolved formats. */ + bool suffix_p = (insn_formats & APEX_XD) + && (insn_formats & (APEX_XS | APEX_XI | APEX_XC)); + const char *insn_suffix = suffix_p ? "i" : ""; + + /* Print .extInstruction section during WPA phase. */ + if (wpa_p) + arcv_apex_print_insn_section (insn_name, insn_suffix, opcode, insn_formats); + + /* Determine whether this builtin has a destination operand. */ + enum riscv_builtin_type builtin_type + = (insn_formats & APEX_DEST) ? RISCV_BUILTIN_DIRECT : + RISCV_BUILTIN_DIRECT_NO_TARGET; + + /* Store APEX builtin information. */ + arcv_apex_builtins[arcv_apex_builtin_index] = { + arcv_apex_get_icode (insn_formats), xasprintf ("%s", fn_name), + xasprintf ("%s", insn_name), opcode, builtin_type, insn_formats, + xasprintf ("%s", insn_suffix) /* TODO: Remove insn_suffix from struct. */ + }; + + /* Set function code for this builtin. */ + fndecl->function_decl.function_code + = (arcv_apex_builtin_index << RISCV_BUILTIN_SHIFT) + RISCV_BUILTIN_APEX; + + arcv_apex_builtin_index++; +} + +/* Get the number of registered APEX builtins. */ + +int +arcv_apex_get_builtin_count (void) +{ + return arcv_apex_builtin_index; +} + +/* Get information about a specific APEX builtin by index. + Returns the information about the builtin. */ + +void +arcv_apex_get_builtin_info (int index, const char **fn_name, + const char **insn_name, unsigned int *opcode, + unsigned int *insn_formats) +{ + gcc_assert (index >= 0 && index < arcv_apex_builtins_limit); + const struct arcv_apex_builtin_description *d = &arcv_apex_builtins[index]; + + *fn_name = d->name; + *insn_name = d->insn_name; + *opcode = d->opcode; + *insn_formats = d->insn_formats; +} diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h index 886f297ddca7..78835ee595f6 100644 --- a/gcc/config/riscv/riscv-protos.h +++ b/gcc/config/riscv/riscv-protos.h @@ -814,6 +814,12 @@ extern const char* arcv_apex_get_insn_suffix (rtx); extern bool arcv_apex_format_supports_p (unsigned int, unsigned int); extern void arcv_apex_init_builtin (tree, const char *, const char *, unsigned int, unsigned int); +extern void arcv_apex_lto_register_builtin (const char *, const char *, + unsigned int, unsigned int, + bool, tree); +extern int arcv_apex_get_builtin_count (void); +extern void arcv_apex_get_builtin_info (int, const char **, const char **, + unsigned int *, unsigned int *); #ifdef RTX_CODE extern const char* diff --git a/gcc/config/riscv/t-riscv b/gcc/config/riscv/t-riscv index d9f2f4aa84ce..c52d5bc2a06b 100644 --- a/gcc/config/riscv/t-riscv +++ b/gcc/config/riscv/t-riscv @@ -18,6 +18,13 @@ riscv-builtins.o: $(srcdir)/config/riscv/riscv-builtins.cc $(CONFIG_H) \ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ $(srcdir)/config/riscv/riscv-builtins.cc +riscv-apex-lto.o: $(srcdir)/config/riscv/riscv-apex-lto.cc \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(BACKEND_H) $(TREE_H) $(GIMPLE_H) \ + $(CGRAPH_H) $(LTO_STREAMER_H) $(IPA_UTILS_H) $(DATA_STREAMER_H) \ + stringpool.h attribs.h + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + $(srcdir)/config/riscv/riscv-apex-lto.cc + riscv-vector-builtins.o: $(srcdir)/config/riscv/riscv-vector-builtins.cc \ $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(RTL_H) $(TM_P_H) \ memmodel.h insn-codes.h $(OPTABS_H) $(RECOG_H) $(DIAGNOSTIC_H) $(EXPR_H) \ diff --git a/gcc/lto-section-in.cc b/gcc/lto-section-in.cc index 1dd9520137a1..2785268be4d2 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 8efda29f7676..06fa5583605c 100644 --- a/gcc/lto-streamer-out.cc +++ b/gcc/lto-streamer-out.cc @@ -3452,6 +3452,11 @@ produce_asm_for_decls (void) /* Write command line opts. */ lto_write_options (); +#ifdef RISCV_APEX + /* Write RISC-V APEX intrinsic information. */ + 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 8c1d2d4947d6..edfe0ca03144 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 RISCV_APEX +/* In config/riscv/riscv-apex-lto.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 64631201939a..688b3c830fa3 100644 --- a/gcc/lto/lto-common.cc +++ b/gcc/lto/lto-common.cc @@ -2961,6 +2961,11 @@ read_cgraph_and_symbols (unsigned nfiles, const char **fnames) else ipa_read_summaries (); +#ifdef RISCV_APEX + /* Read RISC-V APEX intrinsic information. */ + arcv_apex_lto_read_section (); +#endif + ggc_grow (); for (i = 0; all_file_decl_data[i]; i++) From 7425c64e91a4bd87560132cd0df3714854b46ed5 Mon Sep 17 00:00:00 2001 From: Luis Silva Date: Thu, 30 Oct 2025 15:54:18 +0000 Subject: [PATCH 2/4] testsuite: Add scan-ltrans-assembler directives for LTO tests. This patch adds new DejaGnu test directives to scan assembly output from LTRANS units during LTO compilation. These procedures enable testing of code generation in the LTRANS phase, which is essential for verifying that target-specific features are correctly preserved through the LTO pipeline. The new directives scan .ltrans*.s files produced during LTO compilation: - scan-ltrans-assembler: Checks if a pattern exists in any LTRANS assembly file for the current test - scan-ltrans-assembler-times: Verifies a pattern appears exactly N times across all LTRANS assembly files Signed-off-by: Luis Silva --- gcc/testsuite/lib/scanltrans.exp | 120 +++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/gcc/testsuite/lib/scanltrans.exp b/gcc/testsuite/lib/scanltrans.exp index abfb2e4f4eb3..fd74669a7769 100644 --- a/gcc/testsuite/lib/scanltrans.exp +++ b/gcc/testsuite/lib/scanltrans.exp @@ -78,3 +78,123 @@ foreach ir { tree rtl } { } }] } + +# Assembly scanning procedures for LTRANS output. +# These scan the .ltrans*.s assembly files produced during LTO compilation. + +# Look for a pattern in all .ltrans*.s files produced by the LTO compiler. +# This combines results from all LTRANS units for the current test. +proc scan-ltrans-assembler { args } { + if { [llength $args] < 1 } { + error "scan-ltrans-assembler: too few arguments" + return + } + if { [llength $args] > 2 } { + error "scan-ltrans-assembler: too many arguments" + return + } + if { [llength $args] >= 2 } { + switch [dg-process-target [lindex $args 1]] { + "S" { } + "N" { return } + "F" { setup_xfail "*-*-*" } + "P" { } + } + } + + set testcase [testname-for-summary] + set pattern [lindex $args 0] + set pp_pattern [make_pattern_printable $pattern] + + # Get the testcase name (for LTO tests, this is the executable name like + # gcc-target-riscv-apex-arcv-apex-lto-test0-01.exe) + set testname [lindex $testcase 0] + + # Strip .exe extension if present + set basename [file rootname $testname] + + # Match only ltrans files for this specific test + # Pattern: basename.ltrans*.s (e.g., test-01.ltrans0.ltrans.s) + set output_files [dg_glob_remote "${basename}.ltrans*.s"] + + if { $output_files == "" } { + verbose -log "$testcase: no ${basename}.ltrans*.s files found" + unresolved "$testcase scan-ltrans-assembler $pp_pattern" + return + } + + set total_matches 0 + foreach file $output_files { + set fd [open $file r] + set text [read $fd] + close $fd + set matches [regexp -all -- $pattern $text] + set total_matches [expr $total_matches + $matches] + } + + if { $total_matches > 0 } { + pass "$testcase scan-ltrans-assembler $pp_pattern" + } else { + fail "$testcase scan-ltrans-assembler $pp_pattern" + } +} +set_required_options_for scan-ltrans-assembler + +# Check that a pattern appears exactly N times across all .ltrans.s files for this test. +proc scan-ltrans-assembler-times { args } { + if { [llength $args] < 2 } { + error "scan-ltrans-assembler-times: too few arguments" + return + } + if { [llength $args] > 3 } { + error "scan-ltrans-assembler-times: too many arguments" + return + } + if { [llength $args] >= 3 } { + switch [dg-process-target [lindex $args 2]] { + "S" { } + "N" { return } + "F" { setup_xfail "*-*-*" } + "P" { } + } + } + + set testcase [testname-for-summary] + set pattern [lindex $args 0] + set times [lindex $args 1] + set pp_pattern [make_pattern_printable $pattern] + + # Get the testcase name (for LTO tests, this is the executable name like + # gcc-target-riscv-apex-arcv-apex-lto-test0-01.exe) + set testname [lindex $testcase 0] + + # Strip .exe extension if present + set basename [file rootname $testname] + + # Match only ltrans files for this specific test + # Pattern: basename.ltrans*.s (e.g., test-01.ltrans0.ltrans.s) + set output_files [dg_glob_remote "${basename}.ltrans*.s"] + + if { $output_files == "" } { + verbose -log "$testcase: no ${basename}.ltrans*.s files found" + unresolved "$testcase scan-ltrans-assembler-times $pp_pattern $times" + return + } + + set total_matches 0 + foreach file $output_files { + set fd [open $file r] + set text [read $fd] + close $fd + set matches [regexp -all -- $pattern $text] + set total_matches [expr $total_matches + $matches] + } + + if { $total_matches == $times } { + pass "$testcase scan-ltrans-assembler-times $pp_pattern $times" + } else { + verbose -log "$testcase: $pp_pattern found $total_matches times" + fail "$testcase scan-ltrans-assembler-times $pp_pattern $times" + } +} +set_required_options_for scan-ltrans-assembler-times From d98cc046229a0442099126bf0589686e1742ea7b Mon Sep 17 00:00:00 2001 From: Luis Silva Date: Thu, 30 Oct 2025 15:52:19 +0000 Subject: [PATCH 3/4] testsuite: Add dg-lto-error directive support to lto.exp. The LTO test framework lacked support for testing expected link-time errors. Tests could use dg-lto-warning and dg-lto-message for diagnostics emitted during compilation, but there was no way to verify that certain code patterns correctly fail at link time under LTO. This patch adds dg-lto-error directive support, allowing tests to: 1. Mark expected link failures with dg-lto-error directives 2. Pass when the link fails as expected (no executable created or linker errors present) 3. Fail when the link succeeds but should have failed 4. Skip execution tests when link failure is expected The implementation tracks dg-lto-error presence via a global flag and performs custom link result checking when link failure is expected, rather than using the standard check_compile reporting. Signed-off-by: Luis Silva --- gcc/testsuite/lib/lto.exp | 42 +++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/gcc/testsuite/lib/lto.exp b/gcc/testsuite/lib/lto.exp index 81519b580e13..7eb8dd26e177 100644 --- a/gcc/testsuite/lib/lto.exp +++ b/gcc/testsuite/lib/lto.exp @@ -323,6 +323,7 @@ proc lto-link-and-maybe-run { testname objlist dest optall optfile optstr } { global tool global compile_type global board_info + global dg_lto_has_error upvar dg-messages-by-file dg-messages-by-file @@ -370,8 +371,31 @@ proc lto-link-and-maybe-run { testname objlist dest optall optfile optstr } { # Prune unimportant visibility warnings before checking output. set comp_output [lto_prune_warns $comp_output] - if ![${tool}_check_compile "$testcase $testname link" $optstr \ - $dest $comp_output] then { + # Check if link succeeded + # If we expect errors, check manually without reporting + if { [info exists dg_lto_has_error] && $dg_lto_has_error } { + verbose "lto.exp: dg-lto-error present, checking for expected link failure" 2 + # Check if link failed (executable not created or output has errors) + set link_failed [expr {![file_on_host exists $dest] || $comp_output != ""}] + if { $link_failed } { + # Link failure is expected due to dg-lto-error - pass the test + verbose "lto.exp: link failed as expected" 2 + pass "$testcase $testname link $optstr" + } else { + # Link succeeded when it should have failed + fail "$testcase $testname link $optstr (expected failure)" + } + if { ![string compare "execute" $compile_type] } { + unresolved "$testcase $testname execute $optstr" + } + return + } + + # Normal link checking (no dg-lto-error) + set link_ok [${tool}_check_compile "$testcase $testname link" $optstr \ + $dest $comp_output] + + if { !$link_ok } { if { ![string compare "execute" $compile_type] } { unresolved "$testcase $testname execute $optstr" } @@ -403,17 +427,25 @@ proc lto-can-handle-directive { op } { # dg-warning and dg-message append to dg-messages. upvar dg-messages dg-messages + # Track if we have dg-lto-error to expect link failure + global dg_lto_has_error # A list of directives to recognize, and a list of directives # to remap them to. # For example, "dg-lto-warning" is implemented by calling "dg-warning". - set directives { dg-lto-warning dg-lto-message dg-lto-note } - set remapped_directives { dg-warning dg-message dg-note } + set directives { dg-lto-warning dg-lto-message dg-lto-note dg-lto-error } + set remapped_directives { dg-warning dg-message dg-note dg-error } set idx [lsearch -exact $directives $cmd] if { $idx != -1 } { verbose "remapping from: $op" 4 + # Mark that we have a dg-lto-error directive + if { $cmd == "dg-lto-error" } { + set dg_lto_has_error 1 + verbose "lto.exp: dg-lto-error detected, expecting link failure" 2 + } + set remapped_cmd [lindex $remapped_directives $idx] set op [lreplace $op 0 0 $remapped_cmd] @@ -629,6 +661,7 @@ proc lto-execute-1 { src1 sid } { global LTO_OPTIONS global dg-final-code global testname_with_flags + global dg_lto_has_error # Get extra flags for this test from the primary source file, and # process other dg-* options that this suite supports. Warn about @@ -637,6 +670,7 @@ proc lto-execute-1 { src1 sid } { set compile_type "run" set dg-do-what [list ${dg-do-what-default} "" P] array set dg-messages-by-file [list] + set dg_lto_has_error 0 set extra_flags(0) [lto-get-options-main $src1] set compile_xfail(0) "" From 2468d20429d89069c267f760e31aa6484d10b347 Mon Sep 17 00:00:00 2001 From: Luis Silva Date: Thu, 30 Oct 2025 15:51:48 +0000 Subject: [PATCH 4/4] arcv: apex: Add LTO testsuite for APEX intrinsics. This patch adds tests for LTO support of APEX intrinsics, including both positive and negative test cases. The testsuite validates that APEX intrinsics declared via #pragma intrinsic directives in different translation units are correctly serialized, merged, and made available during link-time optimization. Test coverage includes: 1. Conflict Detection Tests (arcv-apex-lto-err*): - Opcode mismatch detection across compilation units - Instruction name mismatch detection - Instruction format mismatch detection - Multiple simultaneous conflicts 2. Successful LTO Tests (arcv-apex-lto-test*): - Multiple instruction formats (XD, XS, XI, XC) across files - Partial overlap of intrinsics between translation units - Different pragma declaration orders All tests verify that: - APEX intrinsic calls survive LTO optimization - Correct .extInstruction directives are emitted - Proper assembly instructions are generated - Conflicting definitions produce appropriate diagnostics Signed-off-by: Luis Silva --- gcc/testsuite/gcc.target/riscv/apex/apex.exp | 48 ++++++++++++++--- .../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 +++++++ 19 files changed, 453 insertions(+), 8 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/testsuite/gcc.target/riscv/apex/apex.exp b/gcc/testsuite/gcc.target/riscv/apex/apex.exp index 4c3ba9694775..d6c585a656bd 100644 --- a/gcc/testsuite/gcc.target/riscv/apex/apex.exp +++ b/gcc/testsuite/gcc.target/riscv/apex/apex.exp @@ -14,27 +14,59 @@ # along with GCC; see the file COPYING3. If not see # . -# GCC testsuite that uses the `dg.exp' driver. +# GCC testsuite for RISC-V APEX intrinsics. +# This file handles both regular single-file tests and multi-file LTO tests. # Exit immediately if this isn't a RISC-V target. if ![istarget riscv*-*-*] then { return } -# Load support procs. +# Load all required libraries. load_lib gcc-dg.exp +load_lib standard.exp +load_lib gcc.exp +load_lib lto.exp +load_lib scanltrans.exp global DEFAULT_CFLAGS if ![info exists DEFAULT_CFLAGS] then { set DEFAULT_CFLAGS " -ansi -pedantic-errors" } -# Initialize `dg'. +# +# Run regular single-file tests +# dg-init -# Main loop. -dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \ - "" $DEFAULT_CFLAGS +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 "" $DEFAULT_CFLAGS -# All done. -dg-finish \ No newline at end of file +dg-finish + +# +# Run multi-file LTO tests (if LTO is supported) +# +if { [check_effective_target_lto] } { + gcc_init + lto_init no-mathlib + + set sid "riscv_apex_lto" + + foreach src [lsort [find $srcdir/$subdir arcv-apex-lto-*_0.c]] { + if ![runtest_file_p $runtests $src] then { + continue + } + + lto-execute $src $sid + } + + 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 000000000000..263eec7f545b --- /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 000000000000..9ec7e518ccb6 --- /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 000000000000..c035780c9006 --- /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 instruction name: '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 000000000000..7df812a4cd8b --- /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 000000000000..866c7f509c23 --- /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: 0xe2 vs 0xe8" "" { 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 000000000000..4efb805f31f8 --- /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 000000000000..472459acba9e --- /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 instruction name: '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: 0xe1 vs 0xe2" "" { 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 000000000000..1134e68e3550 --- /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 000000000000..fb0ae276394f --- /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 000000000000..e02a0cd5b1e1 --- /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 000000000000..c384a5044259 --- /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 000000000000..493318bb53d3 --- /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 000000000000..3d8de370c544 --- /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 000000000000..9401957a0f9c --- /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 000000000000..bcd9f68765aa --- /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 000000000000..66757f6bbe5d --- /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 000000000000..ac522c3101cc --- /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 000000000000..9915f2306bfd --- /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); +}