From: Jim Tsung-Chun Lin <[email protected]>
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.
---
gcc/common/config/riscv/riscv-common.cc | 42 +++++---
gcc/testsuite/gcc.target/riscv/multilib.exp | 109 ++++++++++++++++++++
2 files changed, 137 insertions(+), 14 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/multilib.exp
diff --git a/gcc/common/config/riscv/riscv-common.cc
b/gcc/common/config/riscv/riscv-common.cc
index 5d3d37c7a7b..f6cac606acd 100644
--- a/gcc/common/config/riscv/riscv-common.cc
+++ b/gcc/common/config/riscv/riscv-common.cc
@@ -2013,12 +2013,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
@@ -2054,9 +2051,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 =
@@ -2146,19 +2151,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 00000000000..1917aea7c43
--- /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
--
2.43.5