https://gcc.gnu.org/g:c553a2c1b1143cf97979eddd581127b3762a2232
commit r17-2013-gc553a2c1b1143cf97979eddd581127b3762a2232 Author: Jim Tsung-Chun Lin <[email protected]> Date: Wed Jun 3 09:03:10 2026 +0800 RISC-V: Run smart multilib match even when generic matcher picked a dir The generic textual matcher in gcc.cc:set_multilib_dir does not understand RISC-V arch supersetting and treats MULTILIB_DEFAULTS entries as if they were on the command line via default_arg(). When the user passes a -march= that is a superset of one of MULTILIB_OPTIONS' arches but does not textually match, default_arg can rescue the wrong entry and pick a multilib that is not the closest match. riscv_compute_multilib used to early-return whenever multilib_dir was already set, accepting that incorrect generic pick. Drop the early return and run the match-score-based selection unconditionally. There is no need to fall back to the generic-matched multilib_dir after the smart matcher runs: the default "." multilib is parsed into multilib_infos with the compiler's default arch/abi, so the smart matcher handles every case the generic matcher can reach. If it still returns NULL the request is genuinely incompatible with all configured multilibs and riscv_multi_lib_check fires the proper "Cannot find suitable multilib" diagnostic instead of silently linking against incompatible default-arch libraries. Reproduce with: ./configure --enable-multilib --with-abi=lp64d --with-arch=rv64gc \ --with-multilib-generator="rv64gc-lp64f--;rv64g_zcmp_zcmt-lp64f--" With the pre-fix driver, "-march=rv64g_zba_zcmp_zcmt -mabi=lp64f" selects the rv64gc multilib (textual default rescue); after the fix the smart matcher correctly picks the rv64g_zcmp_zcmt multilib. gcc/ChangeLog: * common/config/riscv/riscv-common.cc (riscv_select_multilib): Don't set riscv_no_matched_multi_lib here; let the caller own the flag. (riscv_compute_multilib): Drop the early return that accepted the generic-matched multilib_dir; always run the smart matcher and set riscv_no_matched_multi_lib when it finds no candidate. gcc/testsuite/ChangeLog: * gcc.target/riscv/multilib.exp: New test. (cherry picked from commit 6dbcda7f978ce7fba81e81971911d00b2d1ccab0) Diff: --- gcc/common/config/riscv/riscv-common.cc | 42 +++++++---- gcc/testsuite/gcc.target/riscv/multilib.exp | 109 ++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 14 deletions(-) diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc index 74929381a06b..828e39261cae 100644 --- a/gcc/common/config/riscv/riscv-common.cc +++ b/gcc/common/config/riscv/riscv-common.cc @@ -2070,12 +2070,9 @@ riscv_select_multilib ( } if (best_match_multi_lib == -1) - { - riscv_no_matched_multi_lib = true; - return NULL; - } - else - return xstrdup (multilib_infos[best_match_multi_lib].path.c_str ()); + return NULL; + + return xstrdup (multilib_infos[best_match_multi_lib].path.c_str ()); } #ifndef RISCV_USE_CUSTOMISED_MULTI_LIB @@ -2111,9 +2108,17 @@ riscv_compute_multilib ( std::string option_cond; riscv_multi_lib_info_t multilib_info; - /* Already found suitable, multi-lib, just use that. */ - if (multilib_dir != NULL) - return multilib_dir; + /* The generic textual matcher in gcc.cc:set_multilib_dir does not + understand RISC-V arch supersetting and treats MULTILIB_DEFAULTS + entries as if they were on the command line via default_arg (). + That can pick the wrong multilib when the user's -march is a + superset of one of MULTILIB_OPTIONS' arches but does not textually + match. Run our own match-score-based selection regardless. The + default "." multilib is included in multilib_infos with the + compiler's default arch/abi, so the smart matcher handles every + case the generic matcher can reach; if it still returns NULL the + request is genuinely incompatible and riscv_multi_lib_check will + emit the proper diagnostic. */ /* Find march. */ riscv_current_arch_str = @@ -2203,19 +2208,28 @@ riscv_compute_multilib ( p++; } + const char *selected = NULL; switch (select_kind) { case select_by_abi: - return riscv_select_multilib_by_abi (riscv_current_abi_str, - multilib_infos); + selected = riscv_select_multilib_by_abi (riscv_current_abi_str, + multilib_infos); + break; case select_by_abi_arch_cmodel: - return riscv_select_multilib (riscv_current_abi_str, subset_list, - switches, n_switches, multilib_infos); + selected = riscv_select_multilib (riscv_current_abi_str, subset_list, + switches, n_switches, multilib_infos); + break; case select_by_builtin: - gcc_unreachable (); default: gcc_unreachable (); } + + /* Either select function may return NULL when nothing is compatible; + ensure the flag is set so riscv_multi_lib_check fires its + "Cannot find suitable multilib" diagnostic. */ + if (selected == NULL) + riscv_no_matched_multi_lib = true; + return selected; } #undef TARGET_COMPUTE_MULTILIB diff --git a/gcc/testsuite/gcc.target/riscv/multilib.exp b/gcc/testsuite/gcc.target/riscv/multilib.exp new file mode 100644 index 000000000000..1917aea7c432 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/multilib.exp @@ -0,0 +1,109 @@ +# Copyright (C) 2026 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/>. +# +# gcc.cc:set_multilib_dir does textual matching of MULTILIB_OPTIONS +# against the command line plus MULTILIB_DEFAULTS, which doesn't +# understand RISC-V arch supersetting. When -march is a superset of +# one of the configured multilibs' arches but does not textually match, +# the textual matcher can pick the wrong (default) multilib. Before +# the fix, riscv_compute_multilib early-returned that wrong pick; +# afterwards it runs the smart, score-based matcher unconditionally +# and selects the closest superset multilib. +# +# To exercise the bug a toolchain must be configured so that both +# rv64gc/lp64f and rv64g_zcmp_zcmt/lp64f are present, e.g.: +# ./configure --enable-multilib --with-abi=lp64d --with-arch=rv64gc \ +# --with-multilib-generator="rv64gc-lp64f--;rv64g_zcmp_zcmt-lp64f--" +# The test only inspects --print-multi-lib output to detect that the +# two relevant multilibs exist, and is a no-op on any toolchain that +# is not configured this way. + +if ![istarget riscv*-*-*] then { + return +} + +load_lib gcc-dg.exp + +dg-init + +if ![gcc_parallel_test_run_p options] { + return +} +gcc_parallel_test_enable 0 + +proc gcc_run_with_flags { flags } { + global tool + set options [list "additional_flags=$flags"] + return [${tool}_target_compile "" "" "none" $options] +} + +proc gcc_print_multi_dir { opts } { + return [string trim [gcc_run_with_flags [concat --print-multi-directory $opts]]] +} + +proc check_multi_dir { gcc_opts multi_dir } { + set got [gcc_print_multi_dir $gcc_opts] + if { $got eq $multi_dir } { + pass "multilibdir $gcc_opts -> $multi_dir" + } else { + fail "multilibdir $gcc_opts -> $multi_dir (got '$got')" + } +} + +# Detect the configured multilibs. Each line of --print-multi-lib is +# "<dir>;<flags>"; the default multilib appears as ".;". +set zcmp_dir "" +set rv64gc_dir "" +foreach line [split [gcc_run_with_flags --print-multi-lib] "\n"] { + set line [string trim $line] + if { $line eq "" || [string match ".;*" $line] } { + continue + } + set dir [lindex [split $line ";"] 0] + if { [string match "*zcmp_zcmt*/lp64f" $dir] } { + set zcmp_dir $dir + } elseif { [string match "*zcd*/lp64f" $dir] + && ![string match "*zcmp_zcmt*" $dir] } { + set rv64gc_dir $dir + } +} + +if { $zcmp_dir eq "" || $rv64gc_dir eq "" } { + verbose "skipping: rv64gc/lp64f and rv64g_zcmp_zcmt/lp64f multilibs not both configured" 1 + gcc_parallel_test_enable 1 + return +} + +# Sanity: exact arches must each select their own multilib. +check_multi_dir {-march=rv64gc -mabi=lp64f} $rv64gc_dir +check_multi_dir {-march=rv64g_zcmp_zcmt -mabi=lp64f} $zcmp_dir + +# An arch that is a strict superset of rv64g_zcmp_zcmt but not of +# rv64gc (it has Zcmp/Zcmt and lacks Zcd) must select the Zc* multilib. +# Before the fix, the generic textual matcher in gcc.cc rescued the +# rv64gc default and riscv_compute_multilib accepted it; the smart +# matcher now runs unconditionally and overrides that pick. +check_multi_dir {-march=rv64g_zba_zcmp_zcmt -mabi=lp64f} $zcmp_dir +check_multi_dir {-march=rv64g_zicond_zcmp_zcmt -mabi=lp64f} $zcmp_dir + +# Reverse direction: an arch that is a superset of rv64gc but not of +# rv64g_zcmp_zcmt (it has Zcd and lacks Zcmp/Zcmt) must still select +# the rv64gc multilib. Guards against a future change that would +# unconditionally favour the longer Zc*-rich multilib. +check_multi_dir {-march=rv64gc_zba -mabi=lp64f} $rv64gc_dir +check_multi_dir {-march=rv64gc_zicond -mabi=lp64f} $rv64gc_dir + +gcc_parallel_test_enable 1
