https://gcc.gnu.org/g:a7fe8f83bf89704e278c1db30f51d6bb26bf0e69
commit r16-3565-ga7fe8f83bf89704e278c1db30f51d6bb26bf0e69 Author: Kito Cheng <kito.ch...@sifive.com> Date: Thu Jul 10 15:28:30 2025 +0800 RISC-V: Always register vector built-in functions during LTO [PR110812] Previously, vector built-in functions were not properly registered during the LTO pipeline, causing link failures when vector intrinsics were used in LTO builds with mixed architecture options. This patch ensures all vector built-in functions are always registered during LTO compilation. The key changes include: - Moving pragma intrinsic flag manipulation from riscv-c.cc to riscv-vector-builtins.cc for better encapsulation - Registering all vector built-in functions regardless of current ISA extensions, deferring the actual extension checking to expansion time - Adding proper support for built-in type registration during LTO This approach is safe because we already perform extension requirement checking at expansion time. The trade-off is a slight increase in bootstrap time for LTO builds due to registering more built-in functions. PR target/110812 gcc/ChangeLog: * config/riscv/riscv-c.cc (pragma_intrinsic_flags): Remove struct. (riscv_pragma_intrinsic_flags_pollute): Remove function. (riscv_pragma_intrinsic_flags_restore): Remove function. (riscv_pragma_intrinsic): Simplify to only call handle_pragma_vector. * config/riscv/riscv-vector-builtins.cc (pragma_intrinsic_flags): Move struct definition here from riscv-c.cc. (riscv_pragma_intrinsic_flags_pollute): Move and adapt from riscv-c.cc, add zvfbfmin, zvfhmin and vector_elen_bf_16 support. (riscv_pragma_intrinsic_flags_restore): Move from riscv-c.cc. (rvv_switcher::rvv_switcher): Add pollute_flags parameter to control flag manipulation. (rvv_switcher::~rvv_switcher): Restore flags conditionally. (register_builtin_types): Use rvv_switcher without polluting flags. (get_required_extensions): Remove function. (check_required_extensions): Simplify to only check type validity. (function_instance::function_returns_void_p): Move implementation from header. (function_builder::add_function): Register placeholder for LTO. (init_builtins): Simplify and handle LTO case. (reinit_builtins): Remove function. (handle_pragma_vector): Remove extension checking. * config/riscv/riscv-vector-builtins.h (function_instance::function_returns_void_p): Add declaration. (function_call_info::function_returns_void_p): Remove inline implementation. gcc/testsuite/ChangeLog: * gcc.target/riscv/lto/pr110812_0.c: New test. * gcc.target/riscv/lto/pr110812_1.c: New test. * gcc.target/riscv/lto/riscv-lto.exp: New test driver. * gcc.target/riscv/lto/riscv_vector.h: New header wrapper. Diff: --- gcc/config/riscv/riscv-c.cc | 84 --------- gcc/config/riscv/riscv-vector-builtins.cc | 211 ++++++++++++---------- gcc/config/riscv/riscv-vector-builtins.h | 10 +- gcc/testsuite/gcc.target/riscv/lto/pr110812_0.c | 9 + gcc/testsuite/gcc.target/riscv/lto/pr110812_1.c | 9 + gcc/testsuite/gcc.target/riscv/lto/riscv-lto.exp | 61 +++++++ gcc/testsuite/gcc.target/riscv/lto/riscv_vector.h | 11 ++ 7 files changed, 211 insertions(+), 184 deletions(-) diff --git a/gcc/config/riscv/riscv-c.cc b/gcc/config/riscv/riscv-c.cc index d2c0af359550..4fc052817824 100644 --- a/gcc/config/riscv/riscv-c.cc +++ b/gcc/config/riscv/riscv-c.cc @@ -34,77 +34,6 @@ along with GCC; see the file COPYING3. If not see #define builtin_define(TXT) cpp_define (pfile, TXT) -struct pragma_intrinsic_flags -{ - int intrinsic_riscv_isa_flags; - - int intrinsic_riscv_vector_elen_flags; - int intrinsic_riscv_zvl_subext; - int intrinsic_riscv_zvb_subext; - int intrinsic_riscv_zvk_subext; -}; - -static void -riscv_pragma_intrinsic_flags_pollute (struct pragma_intrinsic_flags *flags) -{ - flags->intrinsic_riscv_isa_flags = riscv_isa_flags; - flags->intrinsic_riscv_vector_elen_flags = riscv_vector_elen_flags; - flags->intrinsic_riscv_zvl_subext = riscv_zvl_subext; - flags->intrinsic_riscv_zvb_subext = riscv_zvb_subext; - flags->intrinsic_riscv_zvk_subext = riscv_zvk_subext; - - riscv_isa_flags = riscv_isa_flags - | MASK_VECTOR; - - riscv_zvl_subext = riscv_zvl_subext - | MASK_ZVL32B - | MASK_ZVL64B - | MASK_ZVL128B - | MASK_ZVL256B - | MASK_ZVL512B - | MASK_ZVL1024B - | MASK_ZVL2048B - | MASK_ZVL4096B; - - riscv_vector_elen_flags = riscv_vector_elen_flags - | MASK_VECTOR_ELEN_32 - | MASK_VECTOR_ELEN_64 - | MASK_VECTOR_ELEN_FP_16 - | MASK_VECTOR_ELEN_FP_32 - | MASK_VECTOR_ELEN_FP_64; - - riscv_zvb_subext = riscv_zvb_subext - | MASK_ZVBB - | MASK_ZVBC - | MASK_ZVKB; - - riscv_zvk_subext = riscv_zvk_subext - | MASK_ZVKG - | MASK_ZVKNED - | MASK_ZVKNHA - | MASK_ZVKNHB - | MASK_ZVKSED - | MASK_ZVKSH - | MASK_ZVKN - | MASK_ZVKNC - | MASK_ZVKNG - | MASK_ZVKS - | MASK_ZVKSC - | MASK_ZVKSG - | MASK_ZVKT; -} - -static void -riscv_pragma_intrinsic_flags_restore (struct pragma_intrinsic_flags *flags) -{ - riscv_isa_flags = flags->intrinsic_riscv_isa_flags; - - riscv_vector_elen_flags = flags->intrinsic_riscv_vector_elen_flags; - riscv_zvl_subext = flags->intrinsic_riscv_zvl_subext; - riscv_zvb_subext = flags->intrinsic_riscv_zvb_subext; - riscv_zvk_subext = flags->intrinsic_riscv_zvk_subext; -} - static int riscv_ext_version_value (unsigned major, unsigned minor) { @@ -278,20 +207,7 @@ riscv_pragma_intrinsic (cpp_reader *) || strcmp (name, "xtheadvector") == 0 || strcmp (name, "xsfvcp") == 0) { - struct pragma_intrinsic_flags backup_flags; - - riscv_pragma_intrinsic_flags_pollute (&backup_flags); - - riscv_option_override (); - init_adjust_machine_modes (); - riscv_vector::reinit_builtins (); riscv_vector::handle_pragma_vector (); - - riscv_pragma_intrinsic_flags_restore (&backup_flags); - - /* Re-initialize after the flags are restored. */ - riscv_option_override (); - init_adjust_machine_modes (); } else error ("unknown %<#pragma riscv intrinsic%> option %qs", name); diff --git a/gcc/config/riscv/riscv-vector-builtins.cc b/gcc/config/riscv/riscv-vector-builtins.cc index 0db7549fc5ca..00a81578567d 100644 --- a/gcc/config/riscv/riscv-vector-builtins.cc +++ b/gcc/config/riscv/riscv-vector-builtins.cc @@ -3474,6 +3474,71 @@ static hash_table<registered_function_hasher> *function_table; static hash_table<non_overloaded_registered_function_hasher> *non_overloaded_function_table; +struct pragma_intrinsic_flags +{ + int intrinsic_riscv_isa_flags; + int intrinsic_riscv_base_subext; + + int intrinsic_riscv_vector_elen_flags; + int intrinsic_riscv_zvl_subext; + int intrinsic_riscv_zvf_subext; +}; + +static void +riscv_pragma_intrinsic_flags_pollute (struct pragma_intrinsic_flags *flags) +{ + /* We already defer the required extension checking to expantion time, so we + only need to pollute those flags that might affect the type registration. + + e.g. zvfbmin and zvfhmin are required to define the vector bf16 and f16, + and VECTOR_ELEN* also required for vector interger and floating + type. */ + flags->intrinsic_riscv_isa_flags = riscv_isa_flags; + flags->intrinsic_riscv_base_subext = riscv_base_subext; + flags->intrinsic_riscv_vector_elen_flags = riscv_vector_elen_flags; + flags->intrinsic_riscv_zvl_subext = riscv_zvl_subext; + flags->intrinsic_riscv_zvf_subext = riscv_zvf_subext; + + riscv_zvf_subext = riscv_zvf_subext + | MASK_ZVFBFMIN + | MASK_ZVFHMIN; + + riscv_isa_flags = riscv_isa_flags + | MASK_VECTOR; + + riscv_base_subext = riscv_base_subext + | MASK_MUL; + + riscv_zvl_subext = riscv_zvl_subext + | MASK_ZVL32B + | MASK_ZVL64B + | MASK_ZVL128B + | MASK_ZVL256B + | MASK_ZVL512B + | MASK_ZVL1024B + | MASK_ZVL2048B + | MASK_ZVL4096B; + + riscv_vector_elen_flags = riscv_vector_elen_flags + | MASK_VECTOR_ELEN_32 + | MASK_VECTOR_ELEN_64 + | MASK_VECTOR_ELEN_FP_16 + | MASK_VECTOR_ELEN_FP_32 + | MASK_VECTOR_ELEN_FP_64 + | MASK_VECTOR_ELEN_BF_16; +} + +static void +riscv_pragma_intrinsic_flags_restore (struct pragma_intrinsic_flags *flags) +{ + riscv_isa_flags = flags->intrinsic_riscv_isa_flags; + riscv_base_subext = flags->intrinsic_riscv_base_subext; + + riscv_vector_elen_flags = flags->intrinsic_riscv_vector_elen_flags; + riscv_zvl_subext = flags->intrinsic_riscv_zvl_subext; + riscv_zvf_subext = flags->intrinsic_riscv_zvf_subext; +} + /* RAII class for enabling enough RVV features to define the built-in types and implement the riscv_vector.h pragma. @@ -3486,21 +3551,42 @@ static hash_table<non_overloaded_registered_function_hasher> class rvv_switcher { public: - rvv_switcher (); + rvv_switcher (bool pollute_flags = true); ~rvv_switcher (); private: bool m_old_have_regs_of_mode[MAX_MACHINE_MODE]; + struct pragma_intrinsic_flags backup_flags; + bool m_pollute_flags; }; -rvv_switcher::rvv_switcher () +static void +register_builtin_types_on_null (); + +rvv_switcher::rvv_switcher (bool pollute_flags) + : m_pollute_flags (pollute_flags) { + if (m_pollute_flags) + { + riscv_pragma_intrinsic_flags_pollute (&backup_flags); + riscv_option_override (); + } + /* Set have_regs_of_mode before targetm.init_builtins (). */ memcpy (m_old_have_regs_of_mode, have_regs_of_mode, sizeof (have_regs_of_mode)); for (int i = 0; i < NUM_MACHINE_MODES; ++i) if (riscv_v_ext_vector_mode_p ((machine_mode) i)) have_regs_of_mode[i] = true; + + /* Not necessary to adjust mode and register type if we don't pollute + flags. */ + if (m_pollute_flags) + { + init_adjust_machine_modes (); + + register_builtin_types_on_null (); + } } rvv_switcher::~rvv_switcher () @@ -3508,6 +3594,15 @@ rvv_switcher::~rvv_switcher () /* Recover back have_regs_of_mode. */ memcpy (have_regs_of_mode, m_old_have_regs_of_mode, sizeof (have_regs_of_mode)); + + if (m_pollute_flags) + { + riscv_pragma_intrinsic_flags_restore (&backup_flags); + + /* Re-initialize after the flags are restored. */ + riscv_option_override (); + init_adjust_machine_modes (); + } } /* Add attribute NAME to ATTRS. */ @@ -3664,26 +3759,10 @@ register_tuple_type (vector_type_index type, vector_type_index subpart_type, static void register_builtin_types () { - /* Get type node from get_typenode_from_name to prevent we have different type - node define in different target libraries, e.g. int32_t defined as - `long` in RV32/newlib-stdint, but `int` for RV32/glibc-stdint.h. - NOTE: uint[16|32|64]_type_node already defined in tree.h. */ - tree int8_type_node = get_typenode_from_name (INT8_TYPE); - tree uint8_type_node = get_typenode_from_name (UINT8_TYPE); - tree int16_type_node = get_typenode_from_name (INT16_TYPE); - tree int32_type_node = get_typenode_from_name (INT32_TYPE); - tree int64_type_node = get_typenode_from_name (INT64_TYPE); - - machine_mode mode; -#define DEF_RVV_TYPE(NAME, NCHARS, ABI_NAME, SCALAR_TYPE, VECTOR_MODE, \ - ARGS...) \ - mode = VECTOR_MODE##mode; \ - register_builtin_type (VECTOR_TYPE_##NAME, SCALAR_TYPE##_type_node, mode); -#define DEF_RVV_TUPLE_TYPE(NAME, NCHARS, ABI_NAME, SUBPART_TYPE, SCALAR_TYPE, \ - NF, VECTOR_SUFFIX) \ - register_tuple_type (VECTOR_TYPE_##NAME, VECTOR_TYPE_##SUBPART_TYPE, \ - SCALAR_TYPE##_type_node, NF); -#include "riscv-vector-builtins.def" + /* Don't pollute flags at this stage to make sure we only register type with + what we want so far, we will register all type if necessary later. */ + rvv_switcher rvv (/* pollute_flags */ false); + register_builtin_types_on_null (); } /* Similar as register_builtin_types but perform the registration if and @@ -3795,25 +3874,15 @@ required_extensions_p (enum rvv_base_type type) gcc_unreachable (); } -static uint64_t -get_required_extensions (vector_type_index type_idx) -{ - for (unsigned int i = 0; all_ops[i].index != NUM_VECTOR_TYPES; i++) - if (type_idx == all_ops[i].index) - return all_ops[i].required_extensions; - for (unsigned int i = 0; b_ops[i].index != NUM_VECTOR_TYPES; i++) - if (type_idx == b_ops[i].index) - return b_ops[i].required_extensions; - gcc_unreachable (); -} - /* Check whether all the RVV_REQUIRE_* values in REQUIRED_EXTENSIONS are - enabled. */ + enabled. + TODO: We defer the required extensions to expantion time, this function is + only doing the legality now, and we may rename this function and moving + to another layer. */ static bool check_required_extensions (const function_instance &instance) { rvv_type_info type_info = instance.type; - uint64_t required_extensions = type_info.required_extensions; const rvv_op_info *op_info = instance.op_info; if (required_extensions_p (op_info->ret.base_type)) @@ -3822,48 +3891,18 @@ check_required_extensions (const function_instance &instance) = op_info->ret.get_function_type_index (type_info.index); if (ret_type_idx == NUM_VECTOR_TYPES) return false; - required_extensions |= get_required_extensions (ret_type_idx); } for (unsigned i = 0; op_info->args[i].base_type != NUM_BASE_TYPES; ++i) { if (!required_extensions_p (op_info->args[i].base_type)) continue; - enum vector_type_index vector_type = op_info->args[i].get_function_type_index (type_info.index); if (vector_type == NUM_VECTOR_TYPES) return false; - required_extensions |= get_required_extensions (vector_type); - - /* According to RVV ISA, EEW=64 index of indexed loads/stores require - XLEN = 64. */ - if (op_info->args[i].base_type == RVV_BASE_eew64_index) - required_extensions |= RVV_REQUIRE_RV64BIT; } - uint64_t isa_flags = 0; - - if (TARGET_VECTOR_ELEN_BF_16) - isa_flags |= RVV_REQUIRE_ELEN_BF_16; - if (TARGET_VECTOR_ELEN_FP_16) - isa_flags |= RVV_REQUIRE_ELEN_FP_16; - if (TARGET_VECTOR_ELEN_FP_32) - isa_flags |= RVV_REQUIRE_ELEN_FP_32; - if (TARGET_VECTOR_ELEN_FP_64) - isa_flags |= RVV_REQUIRE_ELEN_FP_64; - if (TARGET_VECTOR_ELEN_64) - isa_flags |= RVV_REQUIRE_ELEN_64; - if (TARGET_64BIT) - isa_flags |= RVV_REQUIRE_RV64BIT; - if (TARGET_FULL_V) - isa_flags |= RVV_REQUIRE_FULL_V; - if (TARGET_MIN_VLEN > 32) - isa_flags |= RVV_REQUIRE_MIN_VLEN_64; - - uint64_t missing_extensions = required_extensions & ~isa_flags; - if (missing_extensions != 0) - return false; return true; } @@ -4044,6 +4083,12 @@ function_instance::get_return_type () const return op_info->ret.get_tree_type (type.index); } +bool +function_instance::function_returns_void_p () const +{ + return get_return_type () == void_type_node; +} + tree function_instance::get_arg_type (unsigned opno) const { @@ -4316,7 +4361,7 @@ function_builder::add_function (const function_instance &instance, nodes and remove the target hook. For now, however, we need to appease the validation and return a non-NULL, non-error_mark_node node, so we arbitrarily choose integer_zero_node. */ - tree decl = placeholder_p + tree decl = placeholder_p || in_lto_p ? integer_zero_node : simulate_builtin_function_decl (input_location, name, fntype, code, NULL, attrs); @@ -5094,28 +5139,15 @@ builtin_type_p (const_tree type) void init_builtins () { - rvv_switcher rvv; - if (!TARGET_VECTOR) - return; - register_builtin_types (); - if (in_lto_p) - handle_pragma_vector (); -} - -/* Reinitialize builtins similar to init_builtins, but only the null - builtin types will be registered. */ -void -reinit_builtins () -{ - rvv_switcher rvv; - - if (!TARGET_VECTOR) - return; - - register_builtin_types_on_null (); - if (in_lto_p) + /* "pragma vector" will register type during the process. */ handle_pragma_vector (); + else + { + if (!TARGET_VECTOR) + return; + register_builtin_types (); + } } /* Implement TARGET_VERIFY_TYPE_CONTEXT for RVV types. */ @@ -5281,10 +5313,7 @@ handle_pragma_vector () function_table = new hash_table<registered_function_hasher> (1023); function_builder builder; for (unsigned int i = 0; i < ARRAY_SIZE (function_groups); ++i) - { - if (function_groups[i].match (function_groups[i].required_extensions)) - builder.register_function_group (function_groups[i]); - } + builder.register_function_group (function_groups[i]); } /* Return the function decl with RVV function subcode CODE, or error_mark_node diff --git a/gcc/config/riscv/riscv-vector-builtins.h b/gcc/config/riscv/riscv-vector-builtins.h index 86d81154db93..12a07a195c9e 100644 --- a/gcc/config/riscv/riscv-vector-builtins.h +++ b/gcc/config/riscv/riscv-vector-builtins.h @@ -377,6 +377,7 @@ public: bool any_type_float_p () const; tree get_return_type () const; + bool function_returns_void_p () const; tree get_arg_type (unsigned opno) const; /* The properties of the function. (The explicit "enum"s are required @@ -432,8 +433,6 @@ class function_call_info : public function_instance public: function_call_info (location_t, const function_instance &, tree); - bool function_returns_void_p (); - /* The location of the call. */ location_t location; @@ -441,13 +440,6 @@ public: tree fndecl; }; -/* Return true if the function has no return value. */ -inline bool -function_call_info::function_returns_void_p () -{ - return TREE_TYPE (TREE_TYPE (fndecl)) == void_type_node; -} - /* A class for folding a gimple function call. */ class gimple_folder : public function_call_info { diff --git a/gcc/testsuite/gcc.target/riscv/lto/pr110812_0.c b/gcc/testsuite/gcc.target/riscv/lto/pr110812_0.c new file mode 100644 index 000000000000..a2f70928b162 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/lto/pr110812_0.c @@ -0,0 +1,9 @@ +/* { dg-lto-do link } */ +/* { dg-lto-options { { -flto -march=rv64gc -mabi=lp64d } } } */ +/* { dg-extra-ld-options "-nostdlib" } */ + +void foo(); + +int _start(){ + foo(); +} diff --git a/gcc/testsuite/gcc.target/riscv/lto/pr110812_1.c b/gcc/testsuite/gcc.target/riscv/lto/pr110812_1.c new file mode 100644 index 000000000000..34d7cbbbcbbb --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/lto/pr110812_1.c @@ -0,0 +1,9 @@ +/* { dg-options { -flto -march=rv64gcv -mabi=lp64d } } */ + +#include "riscv_vector.h" +uint8_t *x, *y; +void foo () { + int vl = __riscv_vsetvl_e8m8 (100); + vint8m8_t a = __riscv_vle8_v_i8m8 (x, 100); + __riscv_vse8_v_i8m8 (y, a, 100); +} diff --git a/gcc/testsuite/gcc.target/riscv/lto/riscv-lto.exp b/gcc/testsuite/gcc.target/riscv/lto/riscv-lto.exp new file mode 100644 index 000000000000..459cb766ac0a --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/lto/riscv-lto.exp @@ -0,0 +1,61 @@ +# Copyright (C) 2025 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 +# <http://www.gnu.org/licenses/>. + +# Exit immediately if this isn't an RISC-V target. +if ![istarget riscv*-*-*] then { + return +} + +# Test link-time optimization across multiple files. +# +# Programs are broken into multiple files. Each one is compiled +# separately with LTO information. The final executable is generated +# by collecting all the generated object files using regular LTO or WHOPR. + +if $tracelevel then { + strace $tracelevel +} + +# Load procedures from common libraries. +load_lib standard.exp +load_lib gcc.exp + +# Load the language-independent compabibility support procedures. +load_lib lto.exp + +# If LTO has not been enabled, bail. +if { ![check_effective_target_lto] } { + return +} + +gcc_init +lto_init no-mathlib + +# Define an identifier for use with this suite to avoid name conflicts +# with other lto tests running at the same time. +set sid "c_lto" + +# Main loop. +foreach src [lsort [find $srcdir/$subdir *_0.c]] { + # If we're only testing specific files and this isn't one of them, skip it. + if ![runtest_file_p $runtests $src] then { + continue + } + + lto-execute $src $sid +} + +lto_finish diff --git a/gcc/testsuite/gcc.target/riscv/lto/riscv_vector.h b/gcc/testsuite/gcc.target/riscv/lto/riscv_vector.h new file mode 100644 index 000000000000..fbb4858fc867 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/lto/riscv_vector.h @@ -0,0 +1,11 @@ +/* Wrapper of riscv_vector.h, prevent riscv_vector.h including stdint.h from + C library, that might cause problem on testing RV32 related testcase when + we disable multilib. */ +#ifndef _RISCV_VECTOR_WRAP_H + +#define _GCC_WRAP_STDINT_H +#include "stdint-gcc.h" +#include_next <riscv_vector.h> +#define _RISCV_VECTOR_WRAP_H + +#endif