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++) 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); +} 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) "" 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