[1/1] RISC-V: Run smart multilib match even when generic matcher picked a dir

Message ID 20260603010310.3070271-1-jim@andestech.com
State Committed
Delegated to: Kito Cheng
Headers
Series [1/1] RISC-V: Run smart multilib match even when generic matcher picked a dir |

Checks

Context Check Description
rivoscibot/toolchain-ci-rivos-apply-patch success Patch applied
rivoscibot/toolchain-ci-rivos-lint success Lint passed
rivoscibot/toolchain-ci-rivos-build--newlib-rv64gcv-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gc_zba_zbb_zbc_zbs-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gcv-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-test success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap success Build passed

Commit Message

Jim Lin June 3, 2026, 1:03 a.m. UTC
  From: Jim Tsung-Chun Lin <jim@andestech.com>

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
  

Comments

Kito Cheng June 15, 2026, 1:24 p.m. UTC | #1
Hi Jim:

Thanks for the patch! this indeed a great improvement on multilib
stuffs, especially improving silently fail part, some time it will
result linker error, but it won't got error if lucky enough (okay, or
we could say unlucky...), the only thing I would like to improve is
the test, it's only able to test with specific multilib configuration,
I guess this may improve by using external spec files to override the
default multilib spec, but I think that's definitely not a blocker for
this patch.

LGTM, and pushed to trunk :)
  

Patch

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