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

Reply via email to