[v1,2/6] LoongArch: improved target configuration interface

Message ID 20230814035707.11272-2-yangyujie@loongson.cn
State Unresolved
Headers
Series [v1,1/6] LoongArch: a symmetric multilib subdir layout |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Yang Yujie Aug. 14, 2023, 3:57 a.m. UTC
  The configure script and the GCC driver are updated so that
it is easier to customize and control GCC builds for targeting
different LoongArch implementations.

* Support options for LoongArch SIMD extensions:
  new configure options --with-simd={none,lsx,lasx};
  new driver options -m[no]-l[a]sx / -msimd={none,lsx,lasx}.

* Mark some LoongArch-specific "flags" with overlapping state-changing
  semantics as "driver deferred" so that they could be processed in the
  order they appear in the GCC driver. In this way, the final result can
  be canonicalized into "parameters" (options with "=") for reliable use in
  specs rules or by the compiler proper.

* Enforce the priority of configuration paths (for <parm>={fpu,tune,simd}):
  -m<parm> > -march-implied > --with-<parm> > --with-arch-implied.

* Allow the user to control the compiler options used when building
  GCC libraries for each multilib variant via --with-multilib-list
  and --with-multilib-default.  This could become more useful when
  we have 32-bit support later.

  Example 1: the following configure option
    --with-multilib-list=lp64d/la464/mno-strict-align/msimd=lsx,lp64s/mfpu=32
                          |     |            |         |
                    -mabi=ABI  -march=ARCH  a list of other options
                  (mandatory)  (optional)     (optional)

       builds two sets of libraries:
          lp64d ABI (the default ABI if no --with-abi is given,
                     built with "-march=la464 -mno-strict-align")
          lp64s ABI (built with "-march=abi-default -mfpu=32")

  Example 2: the following 3 configure options

    --with-arch=loongarch64
    --with-multilib-list=lp64d,lp64f,lp64s/la464
    --with-multilib-default=fixed/mno-strict-align/mfpu=64
                             |            |           |
                        -march=ARCH   a list of other options
                         (optional)        (optional)

    is equivalent to (in terms of building libraries):

    --with-multilib-list=\
    lp64d/loongarch64/mno-strict-align/mfpu=64,\
    lp64f/loongarch64/mno-strict-align/mfpu=64,\
    lp64s/la464

  Note:
    1. the GCC driver and compiler proper does not support
       "-march=fixed". "fixed" that appear here acts as a placeholder for
       "use whatever ARCH in --with-arch=ARCH" (or the default value
       of --with-arch=ARCH if --with-arch is not explicitly configured).

    2. if the ARCH part is omitted, "-march=abi-default"
       is used for building all library variants, which
       practically means enabling the minimal ISA features
       that can support the given ABI.

gcc/ChangeLog:

	* gcc/config.gcc: Add new configuration options --with-arch
        and --with-multilib-default; slightly adjust the handling
        of configure-time defaults.
	* gcc/config/loongarch/genopts/loongarch-strings: Add keyword
        "abi-default" as in "-march=abi-default".
	* gcc/config/loongarch/loongarch-str.h: Likewise
	* gcc/config/loongarch/genopts/loongarch.opt.in: Mark flags
        as "driver deferred" so that a state machine could be applied.
	* gcc/config/loongarch/loongarch.opt: Likewise.
	* gcc/config/loongarch/loongarch-cpu.cc: refactor code for
        the new internal representation of -march=native.
	* gcc/config/loongarch/loongarch-cpu.h: Likewise.
	* gcc/config/loongarch/loongarch-c.cc: Likewise.
	* gcc/config/loongarch/loongarch-def.c: add SIMD attributes
        for struct loongarch_target.
	* gcc/config/loongarch/loongarch-def.h: Likewise.
	* gcc/config/loongarch/loongarch-driver.cc: Implement a state
        machine to canonicalize flags into parameters.
	* gcc/config/loongarch/loongarch-driver.h: Likewise.
	* gcc/config/loongarch/loongarch-opts.cc: Use the new internal
        target representation as input as well as the output.
	* gcc/config/loongarch/loongarch-opts.h: Likewise.
	* gcc/config/loongarch/loongarch.cc: Likewise
	* gcc/config/loongarch/t-linux: Support building GCC libraries
        with customized compiler options using specs.
        * gcc/doc/invoke.texi: document -m[no-]l[a]sx and -msimd=.
        * gcc/doc/install.texi: document --with-multilib-default
        and --with-multilib list.
---
 gcc/config.gcc                                | 253 +++++++++----
 .../loongarch/genopts/loongarch-strings       |   8 +-
 gcc/config/loongarch/genopts/loongarch.opt.in |  62 +--
 gcc/config/loongarch/la464.md                 |  32 +-
 gcc/config/loongarch/loongarch-c.cc           |   4 +-
 gcc/config/loongarch/loongarch-cpu.cc         | 260 ++++++++-----
 gcc/config/loongarch/loongarch-cpu.h          |   3 +-
 gcc/config/loongarch/loongarch-def.c          |  55 ++-
 gcc/config/loongarch/loongarch-def.h          |  57 +--
 gcc/config/loongarch/loongarch-driver.cc      | 205 +++++-----
 gcc/config/loongarch/loongarch-driver.h       |  40 +-
 gcc/config/loongarch/loongarch-opts.cc        | 352 +++++++++++++-----
 gcc/config/loongarch/loongarch-opts.h         |  61 +--
 gcc/config/loongarch/loongarch-str.h          |   7 +-
 gcc/config/loongarch/loongarch.cc             |  87 +++--
 gcc/config/loongarch/loongarch.opt            |  60 +--
 gcc/config/loongarch/t-linux                  |  25 +-
 gcc/doc/install.texi                          |  44 ++-
 gcc/doc/invoke.texi                           |  29 +-
 19 files changed, 1058 insertions(+), 586 deletions(-)
  

Comments

Xi Ruoyao Aug. 14, 2023, 5:22 a.m. UTC | #1
On Mon, 2023-08-14 at 11:57 +0800, Yang Yujie wrote:
> The configure script and the GCC driver are updated so that
> it is easier to customize and control GCC builds for targeting
> different LoongArch implementations.
> 
> * Support options for LoongArch SIMD extensions:
>   new configure options --with-simd={none,lsx,lasx};
>   new driver options -m[no]-l[a]sx / -msimd={none,lsx,lasx}.

What's the relationship between -mlasx and -msimd=lasx?  What will
happen if the user specifies -mlasx -msimd=none or -mlasx -msimd=lsx?
  
Xi Ruoyao Aug. 14, 2023, 5:29 a.m. UTC | #2
On Mon, 2023-08-14 at 11:57 +0800, Yang Yujie wrote:
>                 loongarch64)
> -                       tune_pattern="loongarch64|la464"
> -                       tune_default="la464"
> +                       tune_pattern="native|abi-default|loongarch64|la464"

I think we can remove tune_pattern completely.  There is no reason to
limit --with-tune setting based on --with-arch setting.
  
Xi Ruoyao Aug. 14, 2023, 5:58 a.m. UTC | #3
On Mon, 2023-08-14 at 11:57 +0800, Yang Yujie wrote:
> * Support options for LoongArch SIMD extensions:
>   new configure options --with-simd={none,lsx,lasx};
>   new driver options -m[no]-l[a]sx / -msimd={none,lsx,lasx}.

I suggest to rename --with-simd= to --with-ext= and accept a comma-
separated ISA extension list, because we have non-SIMD ISA extensions. 
For example, "--with-ext=lasx,lbt" will make -mlasx, -mlsx (implied),
and -mlbt the default.  I prefer "-mlasx" over "-msimd=lasx" because "-
mlasx" is shorter anyway (if there is no real reason to make -mlasx and
-msimd=lasx two different things).
  
Xi Ruoyao Aug. 14, 2023, 6:01 a.m. UTC | #4
On Mon, 2023-08-14 at 13:58 +0800, Xi Ruoyao via Gcc-patches wrote:
> On Mon, 2023-08-14 at 11:57 +0800, Yang Yujie wrote:
> > * Support options for LoongArch SIMD extensions:
> >   new configure options --with-simd={none,lsx,lasx};
> >   new driver options -m[no]-l[a]sx / -msimd={none,lsx,lasx}.
> 
> I suggest to rename --with-simd= to --with-ext= and accept a comma-
> separated ISA extension list, because we have non-SIMD ISA extensions.
> For example, "--with-ext=lasx,lbt" will make -mlasx, -mlsx (implied),
> and -mlbt the default.  I prefer "-mlasx" over "-msimd=lasx" because "-
> mlasx" is shorter anyway (if there is no real reason to make -mlasx and
> -msimd=lasx two different things).

Perhaps just "--with-feature" or "--with-loongarch-feature", then we can
even fold -mstrict-align here, like "--with-feature=lbt,strict-align".
  
Yang Yujie Aug. 14, 2023, 8:44 a.m. UTC | #5
On Mon, Aug 14, 2023 at 01:58:24PM +0800, Xi Ruoyao wrote:
> On Mon, 2023-08-14 at 11:57 +0800, Yang Yujie wrote:
> > * Support options for LoongArch SIMD extensions:
> >   new configure options --with-simd={none,lsx,lasx};
> >   new driver options -m[no]-l[a]sx / -msimd={none,lsx,lasx}.
> 
> I suggest to rename --with-simd= to --with-ext= and accept a comma-
> separated ISA extension list, because we have non-SIMD ISA extensions. 
> For example, "--with-ext=lasx,lbt" will make -mlasx, -mlsx (implied),
> and -mlbt the default.  I prefer "-mlasx" over "-msimd=lasx" because "-
> mlasx" is shorter anyway (if there is no real reason to make -mlasx and
> -msimd=lasx two different things).
> 
> -- 
> Xi Ruoyao <xry111@xry111.site>
> School of Aerospace Science and Technology, Xidian University

Thanks for the suggestion, --with-ext seems a good idea.  I will
consider adding it later.

-msimd=lasx and -mlasx are the same if you only use them once, but things
gets complicated if you also want -mno-lsx to cancel the effect of -mlasx.

In short, -msimd= is *necessary* for a correct (and convenient) implementation
of the "legacy" -m[no-]l[a]sx options.  Let's hope I can explain this clearly:

I assume we all want:

 (1) -mlasx -mlsx -> enable LSX and LASX
 (2) -mlasx -mno-lsx -> disable LSX and LASX
 (3) -mno-lsx -mlasx -> enable LSX and LASX

Unless we declare -mlsx / -mlasx as driver deferred, AFAIK there is no other way for
us to know the actual order of appearnce of all -m[no-]l[a]sx options on the command
line.  All we know from GCC's option system would be a final on/off state of "lsx"
and a final on/off state of "lasx".

These states results from independent processing of -m[no-]lsx and -m[no-]lasx options
in the order they appear, respectively.  So we can't distinguish between (2) and (3).

Another seemingly viable approach is to declare three options -mlsx / -mlasx / -mno-lsx
and making them mutually exclusive.  In this way GCC would only consider the last one
appearing on the command line.  But we lost (1) in this case.

So, it seems that GCC is forbidding a compiler option to express a "state change"
rather than a independent configuration state itself.  This makes sense since
"-grecord-gcc-switches" and the LTO objects prefers that a target configuration
can be uniquely expressed in a "tuple of coordinates", rather than a series of
"connected vectors", so that they can be easily compared.

Now what we want is clear:

1. support convenient options like -mlsx / -mlasx (maybe something like
-m32 / -m64 in the future) that express state changes that interferes with
the effect of prior flags with different names.

2. the options passed to the compiler proper has a clear one-one
correspondence to the target configuration.


The implementation: canonicalize everything in the GCC driver, process the "flags"
that express state changes in the order they appear into a group of independent
parameters.  If the parameters they have overlapping semantics, the priority
is hard-coded, like -march and -msimd.

(This is also necessary for multilib since this is where the multilib selection
happens and final ABI needs to be decided before that.)

For this purpose, -msimd= is the canonicalized parameter form of the state
of SIMD extension, and -m[no-]l[a]sx are defined as convenient driver-only
flags.
  
Xi Ruoyao Aug. 14, 2023, 8:49 a.m. UTC | #6
On Mon, 2023-08-14 at 16:44 +0800, Yujie Yang wrote:
> I assume we all want:
> 
>  (1) -mlasx -mlsx -> enable LSX and LASX
>  (2) -mlasx -mno-lsx -> disable LSX and LASX
>  (3) -mno-lsx -mlasx -> enable LSX and LASX

Yes.

> Unless we declare -mlsx / -mlasx as driver deferred, AFAIK there is no other way for
> us to know the actual order of appearnce of all -m[no-]l[a]sx options on the command
> line.  All we know from GCC's option system would be a final on/off state of "lsx"
> and a final on/off state of "lasx".

But x86 does this correct;

$ echo __AVX__ + __AVX2__ | LANG= cpp -E -mno-avx -mavx2
# 0 "<stdin>"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "<stdin>"
1 + 1

so there must be a way to handle this...
  
Yang Yujie Aug. 14, 2023, 8:57 a.m. UTC | #7
On Mon, Aug 14, 2023 at 04:49:11PM +0800, Xi Ruoyao wrote:
> On Mon, 2023-08-14 at 16:44 +0800, Yujie Yang wrote:
> > I assume we all want:
> > 
> >  (1) -mlasx -mlsx -> enable LSX and LASX
> >  (2) -mlasx -mno-lsx -> disable LSX and LASX
> >  (3) -mno-lsx -mlasx -> enable LSX and LASX
> 
> Yes.
> 
> > Unless we declare -mlsx / -mlasx as driver deferred, AFAIK there is no other way for
> > us to know the actual order of appearnce of all -m[no-]l[a]sx options on the command
> > line.  All we know from GCC's option system would be a final on/off state of "lsx"
> > and a final on/off state of "lasx".
> 
> But x86 does this correct;
> 
> $ echo __AVX__ + __AVX2__ | LANG= cpp -E -mno-avx -mavx2
> # 0 "<stdin>"
> # 0 "<built-in>"
> # 0 "<command-line>"
> # 1 "/usr/include/stdc-predef.h" 1 3 4
> # 0 "<command-line>" 2
> # 1 "<stdin>"
> 1 + 1
> 
> so there must be a way to handle this...
> 
> -- 
> Xi Ruoyao <xry111@xry111.site>
> School of Aerospace Science and Technology, Xidian University

Emm... What happens if you reverse the order?

$ echo __AVX__ + __AVX2__ | LANG= cpp -E -mavx2 -mno-avx

Anyways, I believe there may be other ways to implement this, but it would
require equally much effort (or even much more) that the current approach.
Especially considering the possiblity of future updates -- we now have a
framework for this sort of things.

Meanwhile you confortably can stay away from -msimd= and use only
-mlsx / -mlasx. So...a matter of style maybe?
  
Xi Ruoyao Aug. 14, 2023, 8:58 a.m. UTC | #8
On Mon, 2023-08-14 at 16:57 +0800, Yujie Yang wrote:
> On Mon, Aug 14, 2023 at 04:49:11PM +0800, Xi Ruoyao wrote:
> > On Mon, 2023-08-14 at 16:44 +0800, Yujie Yang wrote:
> > > I assume we all want:
> > > 
> > >  (1) -mlasx -mlsx -> enable LSX and LASX
> > >  (2) -mlasx -mno-lsx -> disable LSX and LASX
> > >  (3) -mno-lsx -mlasx -> enable LSX and LASX
> > 
> > Yes.
> > 
> > > Unless we declare -mlsx / -mlasx as driver deferred, AFAIK there is no other way for
> > > us to know the actual order of appearnce of all -m[no-]l[a]sx options on the command
> > > line.  All we know from GCC's option system would be a final on/off state of "lsx"
> > > and a final on/off state of "lasx".
> > 
> > But x86 does this correct;
> > 
> > $ echo __AVX__ + __AVX2__ | LANG= cpp -E -mno-avx -mavx2
> > # 0 "<stdin>"
> > # 0 "<built-in>"
> > # 0 "<command-line>"
> > # 1 "/usr/include/stdc-predef.h" 1 3 4
> > # 0 "<command-line>" 2
> > # 1 "<stdin>"
> > 1 + 1
> > 
> > so there must be a way to handle this...
> > 
> > -- 
> > Xi Ruoyao <xry111@xry111.site>
> > School of Aerospace Science and Technology, Xidian University
> 
> Emm... What happens if you reverse the order?
> 
> $ echo __AVX__ + __AVX2__ | LANG= cpp -E -mavx2 -mno-avx
> 
> Anyways, I believe there may be other ways to implement this, but it would
> require equally much effort (or even much more) that the current approach.
> Especially considering the possiblity of future updates -- we now have a
> framework for this sort of things.
> 
> Meanwhile you confortably can stay away from -msimd= and use only
> -mlsx / -mlasx. So...a matter of style maybe?

I'm OK with that, but we need to document it clearly in invoke.texi.
  
Yang Yujie Aug. 15, 2023, 1:12 a.m. UTC | #9
On Mon, Aug 14, 2023 at 01:22:32PM +0800, Xi Ruoyao wrote:
> On Mon, 2023-08-14 at 11:57 +0800, Yang Yujie wrote:
> > The configure script and the GCC driver are updated so that
> > it is easier to customize and control GCC builds for targeting
> > different LoongArch implementations.
> > 
> > * Support options for LoongArch SIMD extensions:
> >   new configure options --with-simd={none,lsx,lasx};
> >   new driver options -m[no]-l[a]sx / -msimd={none,lsx,lasx}.
> 
> What's the relationship between -mlasx and -msimd=lasx?  What will
> happen if the user specifies -mlasx -msimd=none or -mlasx -msimd=lsx?
> 
> -- 
> Xi Ruoyao <xry111@xry111.site>
> School of Aerospace Science and Technology, Xidian University

At this moment we make sure all "flags" (that expresses a config "delta")
are processed after the "parameters", which is documented in the LoongArch
Toolchain Conventions[1].

So if -msimd=* and -m[no-]l[a]sx appear together on the driver command
line, the final configuration would be derived by first applying -msimd=*
and then the sequence of -m[no-]l[a]sx, in the order they appear.

This is similar to the relationship between -msoft-float and -mfpu / -mabi,
where -mfpu / -mabi are applied first, and -msoft-float modifies the existing
target configuration states (FPU, ABI).

[1] currently released at https://github.com/loongson/LoongArch-Documentation
    /blob/main/docs/LoongArch-toolchain-conventions-EN.adoc
  
Richard Sandiford Sept. 6, 2023, 6:43 a.m. UTC | #10
Yang Yujie <yangyujie@loongson.cn> writes:
> @@ -5171,25 +5213,21 @@ case "${target}" in
>  		# ${with_multilib_list} should not contain whitespaces,
>  		# consecutive commas or slashes.
>  		if echo "${with_multilib_list}" \
> -		| grep -E -e "[[:space:]]" -e '[,/][,/]' -e '[,/]$' -e '^[,/]' > /dev/null; then
> +		| grep -E -e "[[:space:]]" -e '[,/][,/]' -e '[,/]$' -e '^[,/]' > /dev/null 2>&1; then
>  			echo "Invalid argument to --with-multilib-list." 1>&2
>  			exit 1
>  		fi
>  
> -		unset component idx elem_abi_base elem_abi_ext elem_tmp
> +		unset component elem_abi_base elem_abi_ext elem_tmp parse_state all_abis
>  		for elem in $(echo "${with_multilib_list}" | tr ',' ' '); do
> -			idx=0
> -			while true; do
> -				idx=$((idx + 1))
> -				component=$(echo "${elem}" | awk -F'/' '{print $'"${idx}"'}')
> -
> -				case ${idx} in
> -				1)
> -					# Component 1: Base ABI type
> +			unset elem_abi_base elem_abi_ext
> +			parse_state="abi-base"
> +
> +			for component in $(echo "${elem}" | tr '/' ' '); do
> +				if test x${parse_state} = x"abi-base"; then
> +					# Base ABI type
>  					case ${component} in
> -					lp64d) elem_tmp="ABI_BASE_LP64D,";;
> -					lp64f) elem_tmp="ABI_BASE_LP64F,";;
> -					lp64s) elem_tmp="ABI_BASE_LP64S,";;
> +					lp64d | lp64f | lp64s) elem_tmp="ABI_BASE_$(tr a-z A-Z <<< ${component}),";;

"<<<" isn't portable shell.  Could you try with:

  echo ${component} | tr ...

instead?

As it stands, this causes a bootstrap failure with non-bash shells
such as dash, even on non-Loongson targets.

(Part of me wishes that we'd just standardise on bash.  But since that
isn't the policy, I sometimes use dash to pick up my own lapses.)

Thanks,
Richard
  
Yang Yujie Sept. 6, 2023, 6:54 a.m. UTC | #11
On Wed, Sep 06, 2023 at 07:43:26AM +0100, Richard Sandiford wrote:
> Yang Yujie <yangyujie@loongson.cn> writes:
> > @@ -5171,25 +5213,21 @@ case "${target}" in
> >  		# ${with_multilib_list} should not contain whitespaces,
> >  		# consecutive commas or slashes.
> >  		if echo "${with_multilib_list}" \
> > -		| grep -E -e "[[:space:]]" -e '[,/][,/]' -e '[,/]$' -e '^[,/]' > /dev/null; then
> > +		| grep -E -e "[[:space:]]" -e '[,/][,/]' -e '[,/]$' -e '^[,/]' > /dev/null 2>&1; then
> >  			echo "Invalid argument to --with-multilib-list." 1>&2
> >  			exit 1
> >  		fi
> >  
> > -		unset component idx elem_abi_base elem_abi_ext elem_tmp
> > +		unset component elem_abi_base elem_abi_ext elem_tmp parse_state all_abis
> >  		for elem in $(echo "${with_multilib_list}" | tr ',' ' '); do
> > -			idx=0
> > -			while true; do
> > -				idx=$((idx + 1))
> > -				component=$(echo "${elem}" | awk -F'/' '{print $'"${idx}"'}')
> > -
> > -				case ${idx} in
> > -				1)
> > -					# Component 1: Base ABI type
> > +			unset elem_abi_base elem_abi_ext
> > +			parse_state="abi-base"
> > +
> > +			for component in $(echo "${elem}" | tr '/' ' '); do
> > +				if test x${parse_state} = x"abi-base"; then
> > +					# Base ABI type
> >  					case ${component} in
> > -					lp64d) elem_tmp="ABI_BASE_LP64D,";;
> > -					lp64f) elem_tmp="ABI_BASE_LP64F,";;
> > -					lp64s) elem_tmp="ABI_BASE_LP64S,";;
> > +					lp64d | lp64f | lp64s) elem_tmp="ABI_BASE_$(tr a-z A-Z <<< ${component}),";;
> 
> "<<<" isn't portable shell.  Could you try with:
> 
>   echo ${component} | tr ...
> 
> instead?
> 
> As it stands, this causes a bootstrap failure with non-bash shells
> such as dash, even on non-Loongson targets.
> 
> (Part of me wishes that we'd just standardise on bash.  But since that
> isn't the policy, I sometimes use dash to pick up my own lapses.)
> 
> Thanks,
> Richard

Sorry, I missed it when cleaning up the patches. :(
Thanks for the review.  Will fix this in a new patch.

Yujie
  

Patch

diff --git a/gcc/config.gcc b/gcc/config.gcc
index 415e0e1ebc5..9412f73fe35 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -4891,8 +4891,8 @@  case "${target}" in
 		esac
 		;;
 
-	loongarch*-*-*)
-		supported_defaults="abi arch tune fpu"
+	loongarch*-*)
+		supported_defaults="abi arch tune fpu simd multilib-default"
 
 		# Local variables
 		unset \
@@ -4900,7 +4900,6 @@  case "${target}" in
 			abiext_pattern   abiext_default \
 			arch_pattern     arch_default   \
 			fpu_pattern      fpu_default    \
-			tune_pattern     tune_default   \
 			triplet_os       triplet_abi
 
 		# Infer ABI from the triplet.
@@ -4937,20 +4936,17 @@  case "${target}" in
 			  ;;
 		esac
 
+
+
 		# Perform initial sanity checks on --with-* options.
 		case ${with_arch} in
-		"" | loongarch64 | la464) ;; # OK, append here.
+		"" | abi-default | loongarch64 | la464) ;; # OK, append here.
 		native)
 			if test x${host} != x${target}; then
 				echo "--with-arch=native is illegal for cross-compiler." 1>&2
 				exit 1
 			fi
 			;;
-		"")
-			echo "Please set a default value for \${with_arch}" \
-			     "according to your target triplet \"${target}\"." 1>&2
-			exit 1
-			;;
 		*)
 			echo "Unknown arch in --with-arch=$with_arch" 1>&2
 			exit 1
@@ -4985,6 +4981,25 @@  case "${target}" in
 			;;
 		esac
 
+		case ${with_simd} in
+		"" | none) ;;
+		lsx | lasx)  # OK, append here.
+			case ${with_fpu} in
+			64) ;;
+			"") with_fpu=64 ;;
+			*)
+				echo "--with-simd=${with_simd} conflicts with --with-fpu=${with_fpu}" 1>&2
+				exit 1
+				;;
+			esac
+			;;
+
+		*)
+			echo "Unknown SIMD extension in --with-simd=$with_simd" 1>&2
+			exit 1
+			;;
+		esac
+
 
 		# Set default value for with_abi.
 		case ${with_abi} in
@@ -5040,9 +5055,9 @@  case "${target}" in
 		case ${with_abi}/${with_abiext} in
 		lp64*/base)
 			# architectures that support lp64* ABI
-			arch_pattern="native|loongarch64|la464"
+			arch_pattern="native|abi-default|loongarch64|la464"
 			# default architecture for lp64* ABI
-			arch_default="loongarch64"
+			arch_default="abi-default"
 			;;
 		*)
 			echo "Unsupported ABI type ${with_abi}/${with_abiext}." 1>&2
@@ -5112,32 +5127,19 @@  case "${target}" in
 		esac
 
 
-		# Infer default with_tune from with_arch: pass 1
+		# Check default with_tune configuration using with_arch.
 		case ${with_arch} in
-		native)
-			tune_pattern="*"
-			tune_default="native"
-			;;
 		loongarch64)
-			tune_pattern="loongarch64|la464"
-			tune_default="la464"
+			tune_pattern="native|abi-default|loongarch64|la464"
 			;;
 		*)
 			# By default, $with_tune == $with_arch
-			tune_pattern="$with_arch"
+			tune_pattern="*"
 			;;
 		esac
 
-		## Set default value for with_tune.
 		case ${with_tune} in
-		"")
-			if test x${tune_default} != x; then
-				with_tune=${tune_default}
-			else
-				with_tune=${tune_pattern}
-			fi
-			;;
-
+		"") ;; # OK
 		*)
 			if echo "${with_tune}" | grep -E "^${tune_pattern}$" > /dev/null; then
 				: # OK
@@ -5149,6 +5151,46 @@  case "${target}" in
 			;;
 		esac
 
+		# Handle --with-multilib-default
+		if echo "${with_multilib_default}" \
+		| grep -E -e '[[:space:]]' -e '//' -e '/$' -e '^/' > /dev/null 2>&1; then
+			echo "Invalid argument to --with-multilib-default." 1>&2
+			exit 1
+		fi
+
+		if test x${with_multilib_default} = x; then
+			# Use -march=abi-default by default when building libraries.
+			with_multilib_default="/march=abi-default"
+		else
+			unset parse_state component
+			parse_state=arch
+			for component in $(echo "${with_multilib_default}" | tr '/' ' '); do
+				case ${parse_state},${component} in
+				arch,|arch,abi-default)
+					# ABI-default: use the ABI's default ARCH configuration for
+					# multilib library builds, unless otherwise specified
+					# in --with-multilib-list.
+					with_multilib_default="/march=abi-default" ;;
+				arch,fixed)
+					# Fixed: use the default gcc configuration for all multilib
+					# builds by default.
+					with_multilib_default="" ;;
+				arch,native|arch,loongarch64|arch,la464) # OK, append here.
+					with_multilib_default="/march=${component}" ;;
+				arch,*)
+					with_multilib_default="/march=abi-default"
+					with_multilib_default="${with_multilib_default}/${component}" ;;
+				opts,*)
+					with_multilib_default="${with_multilib_default}/${component}" ;;
+				esac
+
+				if test x${parse_state} = xarch; then
+					parse_state=opt;
+				fi
+			done
+			unset parse_state component
+		fi
+
 		# Handle --with-multilib-list.
 		if test x"${with_multilib_list}" = x \
 		   || test x"${with_multilib_list}" = xno \
@@ -5171,25 +5213,21 @@  case "${target}" in
 		# ${with_multilib_list} should not contain whitespaces,
 		# consecutive commas or slashes.
 		if echo "${with_multilib_list}" \
-		| grep -E -e "[[:space:]]" -e '[,/][,/]' -e '[,/]$' -e '^[,/]' > /dev/null; then
+		| grep -E -e "[[:space:]]" -e '[,/][,/]' -e '[,/]$' -e '^[,/]' > /dev/null 2>&1; then
 			echo "Invalid argument to --with-multilib-list." 1>&2
 			exit 1
 		fi
 
-		unset component idx elem_abi_base elem_abi_ext elem_tmp
+		unset component elem_abi_base elem_abi_ext elem_tmp parse_state all_abis
 		for elem in $(echo "${with_multilib_list}" | tr ',' ' '); do
-			idx=0
-			while true; do
-				idx=$((idx + 1))
-				component=$(echo "${elem}" | awk -F'/' '{print $'"${idx}"'}')
-
-				case ${idx} in
-				1)
-					# Component 1: Base ABI type
+			unset elem_abi_base elem_abi_ext
+			parse_state="abi-base"
+
+			for component in $(echo "${elem}" | tr '/' ' '); do
+				if test x${parse_state} = x"abi-base"; then
+					# Base ABI type
 					case ${component} in
-					lp64d) elem_tmp="ABI_BASE_LP64D,";;
-					lp64f) elem_tmp="ABI_BASE_LP64F,";;
-					lp64s) elem_tmp="ABI_BASE_LP64S,";;
+					lp64d | lp64f | lp64s) elem_tmp="ABI_BASE_$(tr a-z A-Z <<< ${component}),";;
 					*)
 						echo "Unknown base ABI \"${component}\" in --with-multilib-list." 1>&2
 						exit 1
@@ -5198,47 +5236,101 @@  case "${target}" in
 					loongarch_multilib_list_c="${loongarch_multilib_list_c}${elem_tmp}"
 					loongarch_multilib_list_make="${loongarch_multilib_list_make}mabi=${component}"
 					elem_abi_base="${component}"
-					;;
 
-				2)
-					# Component 2: ABI extension type
+					parse_state="abi-ext"
+					continue
+				fi
+
+				if test x${parse_state} = x"abi-ext"; then
+					# ABI extension type
 					case ${component} in
-					"" | base)
-						component="base"
-						elem_tmp="ABI_EXT_BASE,"
-						;;
-					*)
-						echo "Unknown ABI extension \"${component}\" in --with-multilib-list." 1>&2
-						exit 1
+					base)
+						elem_abi_ext="base"
+						loongarch_multilib_list_c="${loongarch_multilib_list_c}ABI_EXT_BASE,"
+						loongarch_multilib_list_make="${loongarch_multilib_list_make}" # Add nothing for now.
+						parse_state="arch"
+						continue;
 						;;
 					esac
-					loongarch_multilib_list_c="${loongarch_multilib_list_c}${elem_tmp}"
+
+					# The default ABI extension is "base" if unspecified.
+					elem_abi_ext="base"
+					loongarch_multilib_list_c="${loongarch_multilib_list_c}ABI_EXT_BASE,"
 					loongarch_multilib_list_make="${loongarch_multilib_list_make}" # Add nothing for now.
-					elem_abi_ext="${component}"
-					;;
+					parse_state="arch"
+				fi
 
-				*)
-					# Component 3 and on: optional stuff
+				if test x${parse_state} = x"arch"; then
+					# -march option
 					case ${component} in
-					"")
-						# End of component list.
-						break
+					native | abi-default | loongarch64 | la464) # OK, append here.
+						# Append -march spec for each multilib variant.
+						loongarch_multilib_list_make="${loongarch_multilib_list_make}/march=${component}"
+						parse_state="opts"
+						continue
+						;;
+
+					default)
+						# "/default" is equivalent to --with-multilib-default=fixed
+						parse_state="opts"
+						continue
 						;;
+					esac
+
+					# If ARCH is unspecified for this multilib variant, use ${with_multllib_default}.
+					loongarch_multilib_list_make="${loongarch_multilib_list_make}${with_multilib_default}"
+					parse_state="opts"
+				fi
+
+				if test x${parse_state} = x"opts"; then
+					# Other compiler options for building libraries.
+					# (no static sanity check performed)
+					case ${component} in
 					*)
-						echo "Unknown ABI \"${elem}\" in --with-multilib-list." 1>&2
-						exit 1
+						# Append other components as additional build options
+						# (without the prepending dash).
+						# Their validity should be examined by the compiler.
+						loongarch_multilib_list_make="${loongarch_multilib_list_make}/${component}"
 						;;
 					esac
-					;;
-				esac
+				fi
 			done
 
+			case ${parse_state} in
+			    "abi-ext")
+					elem_abi_ext="base"
+					loongarch_multilib_list_c="${loongarch_multilib_list_c}ABI_EXT_BASE,"
+					loongarch_multilib_list_make="${loongarch_multilib_list_make}" # Add nothing for now.
+					loongarch_multilib_list_make="${loongarch_multilib_list_make}${with_multilib_default}"
+					;;
+			    "arch")
+					# If ARCH is unspecified for this multilib variant, use ${with_multllib_default}.
+					loongarch_multilib_list_make="${loongarch_multilib_list_make}${with_multilib_default}"
+					;;
+			    "opts")
+					:
+					;;
+			esac
+
+			# Check for repeated configuration of the same multilib variant.
+			if echo "${elem_abi_base}/${elem_abi_ext}" \
+			 | grep -E "^(${all_abis%|})$" >/dev/null 2>&1; then
+				echo "Repeated multilib config of \"${elem_abi_base}/${elem_abi_ext}\" in --with-multilib-list."
+				exit 1
+			fi
+			all_abis="${all_abis}${elem_abi_base}/${elem_abi_ext}|"
+
+
+			# Check if the default ABI configuration of the GCC binary
+			# is included in the enabled multilib variants.
 			if test x${elem_abi_base} = x${with_abi} \
 			&& test x${elem_abi_ext} = x${with_abiext}; then
 				loongarch_multilib_list_sane=yes
 			fi
 			loongarch_multilib_list_make="${loongarch_multilib_list_make},"
 		done
+		unset component elem_abi_base elem_abi_ext elem_tmp parse_state all_abis
+
 
 		# Check if the default ABI combination is in the default list.
 		if test x${loongarch_multilib_list_sane} = xno; then
@@ -5739,34 +5831,37 @@  case ${target} in
 
 		# Let --with- flags initialize the enum variables from loongarch.opt.
 		# See macro definitions from loongarch-opts.h and loongarch-cpu.h.
-		case ${with_arch} in
-		native)		tm_defines="${tm_defines} DEFAULT_CPU_ARCH=CPU_NATIVE" ;;
-		la464)		tm_defines="${tm_defines} DEFAULT_CPU_ARCH=CPU_LA464" ;;
-		loongarch64)	tm_defines="${tm_defines} DEFAULT_CPU_ARCH=CPU_LOONGARCH64" ;;
-		esac
 
-		case ${with_tune} in
-		native)		tm_defines="${tm_defines} DEFAULT_CPU_TUNE=CPU_NATIVE" ;;
-		la464)		tm_defines="${tm_defines} DEFAULT_CPU_TUNE=CPU_LA464" ;;
-		loongarch64)	tm_defines="${tm_defines} DEFAULT_CPU_TUNE=CPU_LOONGARCH64" ;;
-		esac
+		# Architecture
+		tm_defines="${tm_defines} DEFAULT_CPU_ARCH=CPU_$(echo ${with_arch} | tr a-z- A-Z_)"
 
-		case ${with_abi} in
-		lp64d)     tm_defines="${tm_defines} DEFAULT_ABI_BASE=ABI_BASE_LP64D" ;;
-		lp64f)     tm_defines="${tm_defines} DEFAULT_ABI_BASE=ABI_BASE_LP64F" ;;
-		lp64s)     tm_defines="${tm_defines} DEFAULT_ABI_BASE=ABI_BASE_LP64S" ;;
-		esac
+		# Base ABI type
+		tm_defines="${tm_defines} DEFAULT_ABI_BASE=ABI_BASE_$(echo ${with_abi} | tr a-z- A-Z_)"
 
+		# ABI Extension
 		case ${with_abiext} in
 		base)      tm_defines="${tm_defines} DEFAULT_ABI_EXT=ABI_EXT_BASE" ;;
 		esac
 
+		# Microarchitecture
+		if test x${with_tune} != x; then
+		  tm_defines="${tm_defines} DEFAULT_CPU_TUNE=CPU_$(echo ${with_tune} | tr a-z- A-Z_)"
+		fi
+
+		# FPU adjustment
 		case ${with_fpu} in
-		none)    tm_defines="$tm_defines DEFAULT_ISA_EXT_FPU=ISA_EXT_NOFPU" ;;
+		none)    tm_defines="$tm_defines DEFAULT_ISA_EXT_FPU=ISA_EXT_NONE" ;;
 		32)      tm_defines="$tm_defines DEFAULT_ISA_EXT_FPU=ISA_EXT_FPU32" ;;
 		64)      tm_defines="$tm_defines DEFAULT_ISA_EXT_FPU=ISA_EXT_FPU64" ;;
 		esac
 
+		# SIMD extensions
+		case ${with_simd} in
+		none)    tm_defines="$tm_defines DEFAULT_ISA_EXT_SIMD=ISA_EXT_NONE" ;;
+		lsx)     tm_defines="$tm_defines DEFAULT_ISA_EXT_SIMD=ISA_EXT_SIMD_LSX" ;;
+		lasx)    tm_defines="$tm_defines DEFAULT_ISA_EXT_SIMD=ISA_EXT_SIMD_LASX" ;;
+		esac
+
 		tmake_file="loongarch/t-loongarch $tmake_file"
 		;;
 
diff --git a/gcc/config/loongarch/genopts/loongarch-strings b/gcc/config/loongarch/genopts/loongarch-strings
index a40998ead97..a536be5e708 100644
--- a/gcc/config/loongarch/genopts/loongarch-strings
+++ b/gcc/config/loongarch/genopts/loongarch-strings
@@ -23,6 +23,7 @@  OPTSTR_ARCH	      arch
 OPTSTR_TUNE	      tune
 
 STR_CPU_NATIVE	      native
+STR_CPU_ABI_DEFAULT   abi-default
 STR_CPU_LOONGARCH64   loongarch64
 STR_CPU_LA464	      la464
 
@@ -31,7 +32,7 @@  STR_ISA_BASE_LA64V100 la64
 
 # -mfpu
 OPTSTR_ISA_EXT_FPU    fpu
-STR_ISA_EXT_NOFPU     none
+STR_NONE              none
 STR_ISA_EXT_FPU0      0
 STR_ISA_EXT_FPU32     32
 STR_ISA_EXT_FPU64     64
@@ -40,6 +41,11 @@  OPTSTR_SOFT_FLOAT     soft-float
 OPTSTR_SINGLE_FLOAT   single-float
 OPTSTR_DOUBLE_FLOAT   double-float
 
+# SIMD extensions
+OPTSTR_ISA_EXT_SIMD   simd
+STR_ISA_EXT_LSX       lsx
+STR_ISA_EXT_LASX      lasx
+
 # -mabi=
 OPTSTR_ABI_BASE	      abi
 STR_ABI_BASE_LP64D    lp64d
diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in b/gcc/config/loongarch/genopts/loongarch.opt.in
index 4b9b4ac273e..f3567b27935 100644
--- a/gcc/config/loongarch/genopts/loongarch.opt.in
+++ b/gcc/config/loongarch/genopts/loongarch.opt.in
@@ -17,22 +17,12 @@ 
 ; <http://www.gnu.org/licenses/>.
 ;
 
-; Variables (macros) that should be exported by loongarch.opt:
-;   la_opt_switches,
-;   la_opt_abi_base, la_opt_abi_ext,
-;   la_opt_cpu_arch, la_opt_cpu_tune,
-;   la_opt_fpu,
-;   la_cmodel.
-
 HeaderInclude
 config/loongarch/loongarch-opts.h
 
 HeaderInclude
 config/loongarch/loongarch-str.h
 
-Variable
-HOST_WIDE_INT la_opt_switches = 0
-
 ; ISA related options
 ;; Base ISA
 Enum
@@ -42,14 +32,13 @@  Basic ISAs of LoongArch:
 EnumValue
 Enum(isa_base) String(@@STR_ISA_BASE_LA64V100@@) Value(ISA_BASE_LA64V100)
 
-
 ;; ISA extensions / adjustments
 Enum
 Name(isa_ext_fpu) Type(int)
 FPU types of LoongArch:
 
 EnumValue
-Enum(isa_ext_fpu) String(@@STR_ISA_EXT_NOFPU@@) Value(ISA_EXT_NOFPU)
+Enum(isa_ext_fpu) String(@@STR_NONE@@) Value(ISA_EXT_NONE)
 
 EnumValue
 Enum(isa_ext_fpu) String(@@STR_ISA_EXT_FPU32@@) Value(ISA_EXT_FPU32)
@@ -58,24 +47,48 @@  EnumValue
 Enum(isa_ext_fpu) String(@@STR_ISA_EXT_FPU64@@) Value(ISA_EXT_FPU64)
 
 m@@OPTSTR_ISA_EXT_FPU@@=
-Target RejectNegative Joined ToLower Enum(isa_ext_fpu) Var(la_opt_fpu) Init(M_OPTION_NOT_SEEN)
+Target RejectNegative Joined ToLower Enum(isa_ext_fpu) Var(la_opt_fpu) Init(M_OPT_UNSET)
 -m@@OPTSTR_ISA_EXT_FPU@@=FPU	Generate code for the given FPU.
 
 m@@OPTSTR_ISA_EXT_FPU@@=@@STR_ISA_EXT_FPU0@@
-Target RejectNegative Alias(m@@OPTSTR_ISA_EXT_FPU@@=,@@STR_ISA_EXT_NOFPU@@)
+Target RejectNegative Alias(m@@OPTSTR_ISA_EXT_FPU@@=,@@STR_NONE@@)
 
 m@@OPTSTR_SOFT_FLOAT@@
-Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_SOFTF) Negative(m@@OPTSTR_SINGLE_FLOAT@@)
+Target Driver Defer Var(la_deferred_options) RejectNegative Negative(m@@OPTSTR_SINGLE_FLOAT@@)
 Prevent the use of all hardware floating-point instructions.
 
 m@@OPTSTR_SINGLE_FLOAT@@
-Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_F32) Negative(m@@OPTSTR_DOUBLE_FLOAT@@)
+Target Driver Defer Var(la_deferred_options) RejectNegative Negative(m@@OPTSTR_DOUBLE_FLOAT@@)
 Restrict the use of hardware floating-point instructions to 32-bit operations.
 
 m@@OPTSTR_DOUBLE_FLOAT@@
-Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_F64) Negative(m@@OPTSTR_SOFT_FLOAT@@)
+Target Driver Defer Var(la_deferred_options) RejectNegative Negative(m@@OPTSTR_SOFT_FLOAT@@)
 Allow hardware floating-point instructions to cover both 32-bit and 64-bit operations.
 
+Enum
+Name(isa_ext_simd) Type(int)
+SIMD extension levels of LoongArch:
+
+EnumValue
+Enum(isa_ext_simd) String(@@STR_NONE@@) Value(ISA_EXT_NONE)
+
+EnumValue
+Enum(isa_ext_simd) String(@@STR_ISA_EXT_LSX@@) Value(ISA_EXT_SIMD_LSX)
+
+EnumValue
+Enum(isa_ext_simd) String(@@STR_ISA_EXT_LASX@@) Value(ISA_EXT_SIMD_LASX)
+
+m@@OPTSTR_ISA_EXT_SIMD@@=
+Target RejectNegative Joined ToLower Enum(isa_ext_simd) Var(la_opt_simd) Init(M_OPT_UNSET)
+-m@@OPTSTR_ISA_EXT_SIMD@@=SIMD	Generate code for the given SIMD extension.
+
+m@@STR_ISA_EXT_LSX@@
+Target Driver Defer Var(la_deferred_options)
+Enable LoongArch SIMD Extension (LSX, 128-bit).
+
+m@@STR_ISA_EXT_LASX@@
+Target Driver Defer Var(la_deferred_options)
+Enable LoongArch Advanced SIMD Extension (LASX, 256-bit).
 
 ;; Base target models (implies ISA & tune parameters)
 Enum
@@ -85,6 +98,9 @@  LoongArch CPU types:
 EnumValue
 Enum(cpu_type) String(@@STR_CPU_NATIVE@@) Value(CPU_NATIVE)
 
+EnumValue
+Enum(cpu_type) String(@@STR_CPU_ABI_DEFAULT@@) Value(CPU_ABI_DEFAULT)
+
 EnumValue
 Enum(cpu_type) String(@@STR_CPU_LOONGARCH64@@) Value(CPU_LOONGARCH64)
 
@@ -92,11 +108,11 @@  EnumValue
 Enum(cpu_type) String(@@STR_CPU_LA464@@) Value(CPU_LA464)
 
 m@@OPTSTR_ARCH@@=
-Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_arch) Init(M_OPTION_NOT_SEEN)
+Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_arch) Init(M_OPT_UNSET)
 -m@@OPTSTR_ARCH@@=PROCESSOR	Generate code for the given PROCESSOR ISA.
 
 m@@OPTSTR_TUNE@@=
-Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_tune) Init(M_OPTION_NOT_SEEN)
+Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_tune) Init(M_OPT_UNSET)
 -m@@OPTSTR_TUNE@@=PROCESSOR	Generate optimized code for PROCESSOR.
 
 
@@ -118,13 +134,13 @@  EnumValue
 Enum(abi_base) String(@@STR_ABI_BASE_LP64S@@) Value(ABI_BASE_LP64S)
 
 m@@OPTSTR_ABI_BASE@@=
-Target RejectNegative Joined ToLower Enum(abi_base) Var(la_opt_abi_base) Init(M_OPTION_NOT_SEEN)
+Target RejectNegative Joined ToLower Enum(abi_base) Var(la_opt_abi_base) Init(M_OPT_UNSET)
 -m@@OPTSTR_ABI_BASE@@=BASEABI	Generate code that conforms to the given BASEABI.
 
+
 ;; ABI Extension
 Variable
-int la_opt_abi_ext = M_OPTION_NOT_SEEN
-
+int la_opt_abi_ext = M_OPT_UNSET
 
 mbranch-cost=
 Target RejectNegative Joined UInteger Var(loongarch_branch_cost)
@@ -182,7 +198,7 @@  EnumValue
 Enum(cmodel) String(@@STR_CMODEL_EXTREME@@) Value(CMODEL_EXTREME)
 
 mcmodel=
-Target RejectNegative Joined Enum(cmodel) Var(la_opt_cmodel) Init(CMODEL_NORMAL)
+Target RejectNegative Joined Enum(cmodel) Var(la_opt_cmodel) Init(M_OPT_UNSET)
 Specify the code model.
 
 mdirect-extern-access
diff --git a/gcc/config/loongarch/la464.md b/gcc/config/loongarch/la464.md
index 541040fdfa8..3b4d14f0b3c 100644
--- a/gcc/config/loongarch/la464.md
+++ b/gcc/config/loongarch/la464.md
@@ -43,88 +43,88 @@ 
 ;; Describe instruction reservations.
 
 (define_insn_reservation "la464_arith" 1
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "arith,clz,const,logical,
 			move,nop,shift,signext,slt"))
   "la464_alu1 | la464_alu2")
 
 (define_insn_reservation "la464_branch" 1
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "branch,jump,call,condmove,trap"))
   "la464_alu1 | la464_alu2")
 
 (define_insn_reservation "la464_imul" 7
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "imul"))
   "la464_alu1 | la464_alu2")
 
 (define_insn_reservation "la464_idiv_si" 12
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (and (eq_attr "type" "idiv")
 	    (eq_attr "mode" "SI")))
   "la464_alu1 | la464_alu2")
 
 (define_insn_reservation "la464_idiv_di" 25
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (and (eq_attr "type" "idiv")
 	    (eq_attr "mode" "DI")))
   "la464_alu1 | la464_alu2")
 
 (define_insn_reservation "la464_load" 4
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "load"))
   "la464_mem1 | la464_mem2")
 
 (define_insn_reservation "la464_gpr_fp" 16
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "mftg,mgtf"))
   "la464_mem1")
 
 (define_insn_reservation "la464_fpload" 4
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "fpload"))
   "la464_mem1 | la464_mem2")
 
 (define_insn_reservation "la464_prefetch" 0
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "prefetch,prefetchx"))
   "la464_mem1 | la464_mem2")
 
 (define_insn_reservation "la464_store" 0
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "store,fpstore,fpidxstore"))
   "la464_mem1 | la464_mem2")
 
 (define_insn_reservation "la464_fadd" 4
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "fadd,fmul,fmadd"))
   "la464_falu1 | la464_falu2")
 
 (define_insn_reservation "la464_fcmp" 2
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "fabs,fcmp,fmove,fneg"))
   "la464_falu1 | la464_falu2")
 
 (define_insn_reservation "la464_fcvt" 4
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "fcvt"))
   "la464_falu1 | la464_falu2")
 
 (define_insn_reservation "la464_fdiv_sf" 12
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
 	    (eq_attr "mode" "SF")))
   "la464_falu1 | la464_falu2")
 
 (define_insn_reservation "la464_fdiv_df" 19
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
 	    (eq_attr "mode" "DF")))
   "la464_falu1 | la464_falu2")
 
 ;; Force single-dispatch for unknown or multi.
 (define_insn_reservation "la464_unknown" 1
-  (and (match_test "TARGET_TUNE_LA464")
+  (and (match_test "TARGET_uARCH_LA464")
        (eq_attr "type" "unknown,multi,atomic,syncloop"))
   "la464_alu1 + la464_alu2 + la464_falu1
    + la464_falu2 + la464_mem1 + la464_mem2")
diff --git a/gcc/config/loongarch/loongarch-c.cc b/gcc/config/loongarch/loongarch-c.cc
index 67911b78f28..660c68f0e06 100644
--- a/gcc/config/loongarch/loongarch-c.cc
+++ b/gcc/config/loongarch/loongarch-c.cc
@@ -61,8 +61,8 @@  loongarch_cpu_cpp_builtins (cpp_reader *pfile)
   builtin_assert ("cpu=loongarch");
   builtin_define ("__loongarch__");
 
-  LARCH_CPP_SET_PROCESSOR ("_LOONGARCH_ARCH", LARCH_ACTUAL_ARCH);
-  LARCH_CPP_SET_PROCESSOR ("_LOONGARCH_TUNE", LARCH_ACTUAL_TUNE);
+  LARCH_CPP_SET_PROCESSOR ("_LOONGARCH_ARCH", la_target.cpu_arch);
+  LARCH_CPP_SET_PROCESSOR ("_LOONGARCH_TUNE", la_target.cpu_tune);
 
   /* Base architecture / ABI.  */
   if (TARGET_64BIT)
diff --git a/gcc/config/loongarch/loongarch-cpu.cc b/gcc/config/loongarch/loongarch-cpu.cc
index 34905f3de30..f76efd7c66f 100644
--- a/gcc/config/loongarch/loongarch-cpu.cc
+++ b/gcc/config/loongarch/loongarch-cpu.cc
@@ -26,6 +26,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "diagnostic-core.h"
 
+#include "loongarch-def.h"
 #include "loongarch-opts.h"
 #include "loongarch-cpu.h"
 #include "loongarch-str.h"
@@ -80,127 +81,188 @@  get_native_prid_str (void)
 }
 
 /* Fill property tables for CPU_NATIVE.  */
-unsigned int
-fill_native_cpu_config (int p_arch_native, int p_tune_native)
+void
+fill_native_cpu_config (struct loongarch_target *tgt)
 {
-  int ret_cpu_type;
+  int arch_native_p = tgt->cpu_arch == CPU_NATIVE;
+  int tune_native_p = tgt->cpu_tune == CPU_NATIVE;
+  int native_cpu_type = CPU_NATIVE;
 
   /* Nothing needs to be done unless "-march/tune=native"
      is given or implied.  */
-  if (!(p_arch_native || p_tune_native))
-    return CPU_NATIVE;
+  if (!arch_native_p && !tune_native_p)
+    return;
 
   /* Fill cpucfg_cache with the "cpucfg" instruction.  */
   cache_cpucfg ();
 
-
-  /* Fill: loongarch_cpu_default_isa[CPU_NATIVE].base
-     With: base architecture (ARCH)
-     At:   cpucfg_words[1][1:0] */
-
-  #define NATIVE_BASE_ISA (loongarch_cpu_default_isa[CPU_NATIVE].base)
-  switch (cpucfg_cache[1] & 0x3)
-    {
-      case 0x02:
-	NATIVE_BASE_ISA = ISA_BASE_LA64V100;
-	break;
-
-      default:
-	if (p_arch_native)
-	  fatal_error (UNKNOWN_LOCATION,
-		       "unknown base architecture %<0x%x%>, %qs failed",
-		       (unsigned int) (cpucfg_cache[1] & 0x3),
-		       "-m" OPTSTR_ARCH "=" STR_CPU_NATIVE);
-    }
-
-  /* Fill: loongarch_cpu_default_isa[CPU_NATIVE].fpu
-     With: FPU type (FP, FP_SP, FP_DP)
-     At:   cpucfg_words[2][2:0] */
-
-  #define NATIVE_FPU (loongarch_cpu_default_isa[CPU_NATIVE].fpu)
-  switch (cpucfg_cache[2] & 0x7)
-    {
-      case 0x07:
-	NATIVE_FPU = ISA_EXT_FPU64;
-	break;
-
-      case 0x03:
-	NATIVE_FPU = ISA_EXT_FPU32;
-	break;
-
-      case 0x00:
-	NATIVE_FPU = ISA_EXT_NOFPU;
-	break;
-
-      default:
-	if (p_arch_native)
-	  fatal_error (UNKNOWN_LOCATION,
-		       "unknown FPU type %<0x%x%>, %qs failed",
-		       (unsigned int) (cpucfg_cache[2] & 0x7),
-		       "-m" OPTSTR_ARCH "=" STR_CPU_NATIVE);
-    }
-
-  /* Fill: loongarch_cpu_cache[CPU_NATIVE]
-     With: cache size info
-     At:   cpucfg_words[16:20][31:0] */
-
-  int l1d_present = 0, l1u_present = 0;
-  int l2d_present = 0;
-  uint32_t l1_szword, l2_szword;
-
-  l1u_present |= cpucfg_cache[16] & 3;	      /* bit[1:0]: unified l1 cache */
-  l1d_present |= cpucfg_cache[16] & 4;	      /* bit[2:2]: l1 dcache */
-  l1_szword = l1d_present ? 18 : (l1u_present ? 17 : 0);
-  l1_szword = l1_szword ? cpucfg_cache[l1_szword]: 0;
-
-  l2d_present |= cpucfg_cache[16] & 24;	      /* bit[4:3]: unified l2 cache */
-  l2d_present |= cpucfg_cache[16] & 128;      /* bit[7:7]: l2 dcache */
-  l2_szword = l2d_present ? cpucfg_cache[19]: 0;
-
-  loongarch_cpu_cache[CPU_NATIVE].l1d_line_size
-    = 1 << ((l1_szword & 0x7f000000) >> 24);  /* bit[30:24]: log2(linesize) */
-
-  loongarch_cpu_cache[CPU_NATIVE].l1d_size
-    = (1 << ((l1_szword & 0x00ff0000) >> 16)) /* bit[23:16]: log2(idx) */
-    * ((l1_szword & 0x0000ffff) + 1)	      /* bit[15:0]:  sets - 1 */
-    * (1 << ((l1_szword & 0x7f000000) >> 24)) /* bit[30:24]: log2(linesize) */
-    >> 10;				      /* in kilobytes */
-
-  loongarch_cpu_cache[CPU_NATIVE].l2d_size
-    = (1 << ((l2_szword & 0x00ff0000) >> 16)) /* bit[23:16]: log2(idx) */
-    * ((l2_szword & 0x0000ffff) + 1)	      /* bit[15:0]:  sets - 1 */
-    * (1 << ((l2_szword & 0x7f000000) >> 24)) /* bit[30:24]: log2(linesize) */
-    >> 10;				      /* in kilobytes */
-
-  /* Fill: ret_cpu_type
+  /* Fill: tgt->cpu_arch | tgt->cpu_tune
      With: processor ID (PRID)
      At:   cpucfg_words[0][31:0] */
 
   switch (cpucfg_cache[0] & 0x00ffff00)
   {
     case 0x0014c000:   /* LA464 */
-      ret_cpu_type = CPU_LA464;
+      native_cpu_type = CPU_LA464;
       break;
 
     default:
-      /* Unknown PRID.  This is generally harmless as long as
-	 the properties above can be obtained via "cpucfg".  */
-      if (p_tune_native)
+      /* Unknown PRID.  */
+      if (tune_native_p)
 	inform (UNKNOWN_LOCATION, "unknown processor ID %<0x%x%>, "
 		"some tuning parameters will fall back to default",
 		cpucfg_cache[0]);
       break;
   }
 
-  /* Properties that cannot be looked up directly using cpucfg.  */
-  loongarch_cpu_issue_rate[CPU_NATIVE]
-    = loongarch_cpu_issue_rate[ret_cpu_type];
-
-  loongarch_cpu_multipass_dfa_lookahead[CPU_NATIVE]
-    = loongarch_cpu_multipass_dfa_lookahead[ret_cpu_type];
-
-  loongarch_cpu_rtx_cost_data[CPU_NATIVE]
-    = loongarch_cpu_rtx_cost_data[ret_cpu_type];
+  /* if -march=native */
+  if (arch_native_p)
+    {
+      int tmp;
+      tgt->cpu_arch = native_cpu_type;
+
+      /* Fill: loongarch_cpu_default_isa[tgt->cpu_arch].base
+	 With: base architecture (ARCH)
+	 At:   cpucfg_words[1][1:0] */
+
+      #define PRESET_ARCH (loongarch_cpu_default_isa[tgt->cpu_arch].base)
+      switch (cpucfg_cache[1] & 0x3)
+	{
+	  case 0x02:
+	    tmp = ISA_BASE_LA64V100;
+	    break;
+
+	  default:
+	    fatal_error (UNKNOWN_LOCATION,
+			 "unknown native base architecture %<0x%x%>, %qs failed",
+			 (unsigned int) (cpucfg_cache[1] & 0x3),
+			 "-m" OPTSTR_ARCH "=" STR_CPU_NATIVE);
+	}
+
+      /* Check consistency with PRID presets.  */
+      if (native_cpu_type != CPU_NATIVE && tmp != PRESET_ARCH)
+	warning (0, "base architecture %qs differs from PRID preset %qs",
+		 loongarch_isa_base_strings[tmp],
+		 loongarch_isa_base_strings[PRESET_ARCH]);
+
+      /* Use the native value anyways.  */
+      PRESET_ARCH = tmp;
+
+      /* Fill: loongarch_cpu_default_isa[tgt->cpu_arch].fpu
+	 With: FPU type (FP, FP_SP, FP_DP)
+	 At:   cpucfg_words[2][2:0] */
+
+      #define PRESET_FPU (loongarch_cpu_default_isa[tgt->cpu_arch].fpu)
+      switch (cpucfg_cache[2] & 0x7)
+	{
+	  case 0x07:
+	    tmp = ISA_EXT_FPU64;
+	    break;
+
+	  case 0x03:
+	    tmp = ISA_EXT_FPU32;
+	    break;
+
+	  case 0x00:
+	    tmp = ISA_EXT_NONE;
+	    break;
+
+	  default:
+	    fatal_error (UNKNOWN_LOCATION,
+			 "unknown native FPU type %<0x%x%>, %qs failed",
+			 (unsigned int) (cpucfg_cache[2] & 0x7),
+			 "-m" OPTSTR_ARCH "=" STR_CPU_NATIVE);
+	}
+
+      /* Check consistency with PRID presets.  */
+      if (native_cpu_type != CPU_NATIVE && tmp != PRESET_FPU)
+	warning (0, "floating-point unit %qs differs from PRID preset %qs",
+		 loongarch_isa_ext_strings[tmp],
+		 loongarch_isa_ext_strings[PRESET_FPU]);
+
+      /* Use the native value anyways.  */
+      PRESET_FPU = tmp;
+
+
+      /* Fill: loongarch_cpu_default_isa[CPU_NATIVE].simd
+	 With: SIMD extension type (LSX, LASX)
+	 At:   cpucfg_words[2][7:6] */
+
+      #define PRESET_SIMD (loongarch_cpu_default_isa[tgt->cpu_arch].simd)
+      switch (cpucfg_cache[2] & 0xc0)
+	{
+	  case 0xc0:
+	    tmp = ISA_EXT_SIMD_LASX;
+	    break;
+
+	  case 0x40:
+	    tmp = ISA_EXT_SIMD_LSX;
+	    break;
+
+	  case 0x80:
+	    warning (0, "unknown SIMD extension "
+			"(%qs disabled while %qs is enabled), disabling SIMD",
+			loongarch_isa_ext_strings[ISA_EXT_SIMD_LSX],
+			loongarch_isa_ext_strings[ISA_EXT_SIMD_LASX]);
+
+	  case 0x00:
+	    tmp = 0;
+	    break;
+	}
+
+      /* Check consistency with PRID presets.  */
+      /*
+      if (native_cpu_type != CPU_NATIVE && tmp != PRESET_SIMD)
+	warning (0, "SIMD extension %qs differs from PRID preset %qs",
+		 loongarch_isa_ext_strings[tmp],
+		 loongarch_isa_ext_strings[PRESET_SIMD]);
+      */
+
+      /* Use the native value anyways.  */
+      PRESET_SIMD = tmp;
+    }
 
-  return ret_cpu_type;
+  if (tune_native_p)
+    {
+      tgt->cpu_tune = native_cpu_type;
+
+      /* Fill: loongarch_cpu_cache[tgt->cpu_tune]
+	 With: cache size info
+	 At:   cpucfg_words[16:20][31:0] */
+
+      #define PRESET_CACHE (loongarch_cpu_cache[tgt->cpu_tune])
+      struct loongarch_cache native_cache;
+      int l1d_present = 0, l1u_present = 0;
+      int l2d_present = 0;
+      uint32_t l1_szword, l2_szword;
+
+      l1u_present |= cpucfg_cache[16] & 3;	  /* bit[1:0]: unified l1 */
+      l1d_present |= cpucfg_cache[16] & 4;	  /* bit[2:2]: l1d */
+      l1_szword = l1d_present ? 18 : (l1u_present ? 17 : 0);
+      l1_szword = l1_szword ? cpucfg_cache[l1_szword]: 0;
+
+      l2d_present |= cpucfg_cache[16] & 24;	  /* bit[4:3]: unified l2 */
+      l2d_present |= cpucfg_cache[16] & 128;	  /* bit[7:7]: l2d */
+      l2_szword = l2d_present ? cpucfg_cache[19]: 0;
+
+      native_cache.l1d_line_size
+	= 1 << ((l1_szword & 0x7f000000) >> 24);  /* bit[30:24]: log2(line) */
+
+      native_cache.l1d_size
+	= (1 << ((l1_szword & 0x00ff0000) >> 16)) /* bit[23:16]: log2(idx) */
+	* ((l1_szword & 0x0000ffff) + 1)	  /* bit[15:0]:  sets - 1 */
+	* (1 << ((l1_szword & 0x7f000000) >> 24)) /* bit[30:24]: log2(line) */
+	>> 10;					  /* in kibibytes */
+
+      native_cache.l2d_size
+	= (1 << ((l2_szword & 0x00ff0000) >> 16)) /* bit[23:16]: log2(idx) */
+	* ((l2_szword & 0x0000ffff) + 1)	  /* bit[15:0]:  sets - 1 */
+	* (1 << ((l2_szword & 0x7f000000) >> 24)) /* bit[30:24]: log2(linesz) */
+	>> 10;					  /* in kibibytes */
+
+      /* Use the native value anyways.  */
+      PRESET_CACHE.l1d_line_size = native_cache.l1d_line_size;
+      PRESET_CACHE.l1d_size = native_cache.l1d_size;
+      PRESET_CACHE.l2d_size = native_cache.l2d_size;
+    }
 }
diff --git a/gcc/config/loongarch/loongarch-cpu.h b/gcc/config/loongarch/loongarch-cpu.h
index 57ad8456ffa..28618f3cf6c 100644
--- a/gcc/config/loongarch/loongarch-cpu.h
+++ b/gcc/config/loongarch/loongarch-cpu.h
@@ -21,9 +21,10 @@  along with GCC; see the file COPYING3.  If not see
 #define LOONGARCH_CPU_H
 
 #include "system.h"
+#include "loongarch-def.h"
 
 void cache_cpucfg (void);
-unsigned int fill_native_cpu_config (int p_arch_native, int p_tune_native);
+void fill_native_cpu_config (struct loongarch_target *tgt);
 uint32_t get_native_prid (void);
 const char* get_native_prid_str (void);
 
diff --git a/gcc/config/loongarch/loongarch-def.c b/gcc/config/loongarch/loongarch-def.c
index 6729c857f7c..5bbc6923a7f 100644
--- a/gcc/config/loongarch/loongarch-def.c
+++ b/gcc/config/loongarch/loongarch-def.c
@@ -21,25 +21,11 @@  along with GCC; see the file COPYING3.  If not see
 #include "loongarch-def.h"
 #include "loongarch-str.h"
 
-/* Default RTX cost initializer.  */
-#define COSTS_N_INSNS(N) ((N) * 4)
-#define DEFAULT_COSTS				\
-    .fp_add		= COSTS_N_INSNS (1),	\
-    .fp_mult_sf		= COSTS_N_INSNS (2),	\
-    .fp_mult_df		= COSTS_N_INSNS (4),	\
-    .fp_div_sf		= COSTS_N_INSNS (6),	\
-    .fp_div_df		= COSTS_N_INSNS (8),	\
-    .int_mult_si	= COSTS_N_INSNS (1),	\
-    .int_mult_di	= COSTS_N_INSNS (1),	\
-    .int_div_si		= COSTS_N_INSNS (4),	\
-    .int_div_di		= COSTS_N_INSNS (6),	\
-    .branch_cost	= 2,			\
-    .memory_latency	= 4
-
 /* CPU property tables.  */
 const char*
 loongarch_cpu_strings[N_TUNE_TYPES] = {
   [CPU_NATIVE]		  = STR_CPU_NATIVE,
+  [CPU_ABI_DEFAULT]	  = STR_CPU_ABI_DEFAULT,
   [CPU_LOONGARCH64]	  = STR_CPU_LOONGARCH64,
   [CPU_LA464]		  = STR_CPU_LA464,
 };
@@ -49,10 +35,12 @@  loongarch_cpu_default_isa[N_ARCH_TYPES] = {
   [CPU_LOONGARCH64] = {
       .base = ISA_BASE_LA64V100,
       .fpu = ISA_EXT_FPU64,
+      .simd = 0,
   },
   [CPU_LA464] = {
       .base = ISA_BASE_LA64V100,
       .fpu = ISA_EXT_FPU64,
+      .simd = ISA_EXT_SIMD_LASX,
   },
 };
 
@@ -84,6 +72,22 @@  loongarch_cpu_align[N_TUNE_TYPES] = {
   },
 };
 
+
+/* Default RTX cost initializer.  */
+#define COSTS_N_INSNS(N) ((N) * 4)
+#define DEFAULT_COSTS				\
+    .fp_add		= COSTS_N_INSNS (1),	\
+    .fp_mult_sf		= COSTS_N_INSNS (2),	\
+    .fp_mult_df		= COSTS_N_INSNS (4),	\
+    .fp_div_sf		= COSTS_N_INSNS (6),	\
+    .fp_div_df		= COSTS_N_INSNS (8),	\
+    .int_mult_si	= COSTS_N_INSNS (1),	\
+    .int_mult_di	= COSTS_N_INSNS (1),	\
+    .int_div_si		= COSTS_N_INSNS (4),	\
+    .int_div_di		= COSTS_N_INSNS (6),	\
+    .branch_cost	= 2,			\
+    .memory_latency	= 4
+
 /* The following properties cannot be looked up directly using "cpucfg".
  So it is necessary to provide a default value for "unknown native"
  tune targets (i.e. -mtune=native while PRID does not correspond to
@@ -103,7 +107,7 @@  loongarch_cpu_rtx_cost_data[N_TUNE_TYPES] = {
 };
 
 /* RTX costs to use when optimizing for size.  */
-extern const struct loongarch_rtx_cost_data
+const struct loongarch_rtx_cost_data
 loongarch_rtx_cost_optimize_size = {
     .fp_add	      = 4,
     .fp_mult_sf	      = 4,
@@ -144,9 +148,11 @@  loongarch_isa_base_strings[N_ISA_BASE_TYPES] = {
 
 const char*
 loongarch_isa_ext_strings[N_ISA_EXT_TYPES] = {
-  [ISA_EXT_FPU64] = STR_ISA_EXT_FPU64,
+  [ISA_EXT_NONE] = STR_NONE,
   [ISA_EXT_FPU32] = STR_ISA_EXT_FPU32,
-  [ISA_EXT_NOFPU] = STR_ISA_EXT_NOFPU,
+  [ISA_EXT_FPU64] = STR_ISA_EXT_FPU64,
+  [ISA_EXT_SIMD_LSX] = STR_ISA_EXT_LSX,
+  [ISA_EXT_SIMD_LASX] = STR_ISA_EXT_LASX,
 };
 
 const char*
@@ -171,24 +177,17 @@  loongarch_cmodel_strings[] = {
   [CMODEL_EXTREME]	  = STR_CMODEL_EXTREME,
 };
 
-const char*
-loongarch_switch_strings[] = {
-  [SW_SOFT_FLOAT]	  = OPTSTR_SOFT_FLOAT,
-  [SW_SINGLE_FLOAT]	  = OPTSTR_SINGLE_FLOAT,
-  [SW_DOUBLE_FLOAT]	  = OPTSTR_DOUBLE_FLOAT,
-};
-
 
 /* ABI-related definitions.  */
 const struct loongarch_isa
 abi_minimal_isa[N_ABI_BASE_TYPES][N_ABI_EXT_TYPES] = {
   [ABI_BASE_LP64D] = {
-      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_FPU64},
+      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_FPU64, .simd = 0},
   },
   [ABI_BASE_LP64F] = {
-      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_FPU32},
+      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_FPU32, .simd = 0},
   },
   [ABI_BASE_LP64S] = {
-      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_NOFPU},
+      [ABI_EXT_BASE] = {.base = ISA_BASE_LA64V100, .fpu = ISA_EXT_NONE, .simd = 0},
   },
 };
diff --git a/gcc/config/loongarch/loongarch-def.h b/gcc/config/loongarch/loongarch-def.h
index fb8bb88eb52..769efcb70fb 100644
--- a/gcc/config/loongarch/loongarch-def.h
+++ b/gcc/config/loongarch/loongarch-def.h
@@ -59,11 +59,13 @@  extern const char* loongarch_isa_base_strings[];
 
 /* enum isa_ext_* */
 extern const char* loongarch_isa_ext_strings[];
-#define ISA_EXT_NOFPU	      0
+#define ISA_EXT_NONE	      0
 #define ISA_EXT_FPU32	      1
 #define ISA_EXT_FPU64	      2
 #define N_ISA_EXT_FPU_TYPES   3
-#define N_ISA_EXT_TYPES	      3
+#define ISA_EXT_SIMD_LSX      3
+#define ISA_EXT_SIMD_LASX     4
+#define N_ISA_EXT_TYPES	      5
 
 /* enum abi_base */
 extern const char* loongarch_abi_base_strings[];
@@ -72,6 +74,16 @@  extern const char* loongarch_abi_base_strings[];
 #define ABI_BASE_LP64S	      2
 #define N_ABI_BASE_TYPES      3
 
+#define TO_LP64_ABI_BASE(C) (C)
+
+#define ABI_FPU_64(abi_base) \
+  (abi_base == ABI_BASE_LP64D)
+#define ABI_FPU_32(abi_base) \
+  (abi_base == ABI_BASE_LP64F)
+#define ABI_FPU_NONE(abi_base) \
+  (abi_base == ABI_BASE_LP64S)
+
+
 /* enum abi_ext */
 extern const char* loongarch_abi_ext_strings[];
 #define ABI_EXT_BASE	      0
@@ -87,55 +99,44 @@  extern const char* loongarch_cmodel_strings[];
 #define CMODEL_EXTREME	      5
 #define N_CMODEL_TYPES	      6
 
-/* enum switches */
-/* The "SW_" codes represent command-line switches (options that
-   accept no parameters). Definition for other switches that affects
-   the target ISA / ABI configuration will also be appended here
-   in the future.  */
-
-extern const char* loongarch_switch_strings[];
-#define SW_SOFT_FLOAT	      0
-#define SW_SINGLE_FLOAT	      1
-#define SW_DOUBLE_FLOAT	      2
-#define N_SWITCH_TYPES	      3
-
 /* The common default value for variables whose assignments
    are triggered by command-line options.  */
 
-#define M_OPTION_NOT_SEEN -1
-#define M_OPT_ABSENT(opt_enum)  ((opt_enum) == M_OPTION_NOT_SEEN)
+#define M_OPT_UNSET -1
+#define M_OPT_ABSENT(opt_enum)  ((opt_enum) == M_OPT_UNSET)
 
 
 /* Internal representation of the target.  */
 struct loongarch_isa
 {
-  unsigned char base;	    /* ISA_BASE_ */
-  unsigned char fpu;	    /* ISA_EXT_FPU_ */
+  int base;	    /* ISA_BASE_ */
+  int fpu;	    /* ISA_EXT_FPU_ */
+  int simd;	    /* ISA_EXT_SIMD_ */
 };
 
 struct loongarch_abi
 {
-  unsigned char base;	    /* ABI_BASE_ */
-  unsigned char ext;	    /* ABI_EXT_ */
+  int base;	    /* ABI_BASE_ */
+  int ext;	    /* ABI_EXT_ */
 };
 
 struct loongarch_target
 {
   struct loongarch_isa isa;
   struct loongarch_abi abi;
-  unsigned char cpu_arch;   /* CPU_ */
-  unsigned char cpu_tune;   /* same */
-  unsigned char cpu_native; /* same */
-  unsigned char cmodel;	    /* CMODEL_ */
+  int cpu_arch;	    /* CPU_ */
+  int cpu_tune;	    /* same */
+  int cmodel;	    /* CMODEL_ */
 };
 
 /* CPU properties.  */
 /* index */
 #define CPU_NATIVE	  0
-#define CPU_LOONGARCH64	  1
-#define CPU_LA464	  2
-#define N_ARCH_TYPES	  3
-#define N_TUNE_TYPES	  3
+#define CPU_ABI_DEFAULT   1
+#define CPU_LOONGARCH64	  2
+#define CPU_LA464	  3
+#define N_ARCH_TYPES	  4
+#define N_TUNE_TYPES	  4
 
 /* parallel tables.  */
 extern const char* loongarch_cpu_strings[];
diff --git a/gcc/config/loongarch/loongarch-driver.cc b/gcc/config/loongarch/loongarch-driver.cc
index 11ce082417f..89ebca59975 100644
--- a/gcc/config/loongarch/loongarch-driver.cc
+++ b/gcc/config/loongarch/loongarch-driver.cc
@@ -26,122 +26,134 @@  along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "obstack.h"
 #include "diagnostic-core.h"
+#include "opts.h"
 
 #include "loongarch-opts.h"
 #include "loongarch-driver.h"
 
-static int
-  opt_arch_driver = M_OPTION_NOT_SEEN,
-  opt_tune_driver = M_OPTION_NOT_SEEN,
-  opt_fpu_driver = M_OPTION_NOT_SEEN,
-  opt_abi_base_driver = M_OPTION_NOT_SEEN,
-  opt_abi_ext_driver = M_OPTION_NOT_SEEN,
-  opt_cmodel_driver = M_OPTION_NOT_SEEN;
-
-int opt_switches = 0;
-
 /* This flag is set to 1 if we believe that the user might be avoiding
    linking (implicitly) against something from the startfile search paths.  */
 static int no_link = 0;
 
-#define LARCH_DRIVER_SET_M_FLAG(OPTS_ARRAY, N_OPTS, FLAG, STR)	\
-  for (int i = 0; i < (N_OPTS); i++)				\
-  {								\
-    if ((OPTS_ARRAY)[i] != 0)					\
-      if (strcmp ((STR), (OPTS_ARRAY)[i]) == 0)			\
-	(FLAG) = i;						\
-  }
-
 /* Use the public obstack from the gcc driver (defined in gcc.c).
    This is for allocating space for the returned string.  */
 extern struct obstack opts_obstack;
 
-#define APPEND_LTR(S)				      \
-  obstack_grow (&opts_obstack, (const void*) (S),     \
-		sizeof ((S)) / sizeof (char) -1)
-
-#define APPEND_VAL(S) \
-  obstack_grow (&opts_obstack, (const void*) (S), strlen ((S)))
+const char*
+la_driver_init (int argc ATTRIBUTE_UNUSED, const char **argv ATTRIBUTE_UNUSED)
+{
+  /* Initialize all fields of la_target to -1 */
+  loongarch_init_target (&la_target, M_OPT_UNSET, M_OPT_UNSET, M_OPT_UNSET,
+			 M_OPT_UNSET, M_OPT_UNSET, M_OPT_UNSET, M_OPT_UNSET);
+  return "";
+}
 
+const char*
+driver_set_no_link (int argc, const char **argv)
+{
+  no_link = 1;
+  return "";
+}
 
 const char*
-driver_set_m_flag (int argc, const char **argv)
+driver_set_m_parm (int argc, const char **argv)
 {
-  int parm_off = 0;
+  gcc_assert (argc == 2);
+
+#define LARCH_DRIVER_PARSE_PARM(OPT_IDX, NAME, OPTSTR_LIST, \
+				OPT_IDX_LO, OPT_IDX_HI)	    \
+  if (strcmp (argv[0], OPTSTR_##NAME) == 0)		    \
+    for (int i = (OPT_IDX_LO); i < (OPT_IDX_HI); i++)	    \
+    {							    \
+      if ((OPTSTR_LIST)[i] != 0)			    \
+	if (strcmp (argv[1], (OPTSTR_LIST)[i]) == 0)	    \
+	  {						    \
+	    (OPT_IDX) = i;				    \
+	    return 0;					    \
+	  }						    \
+    }
 
-  if (argc != 1)
-    return "%eset_m_flag requires exactly 1 argument.";
+  LARCH_DRIVER_PARSE_PARM (la_target.abi.base, ABI_BASE, \
+			   loongarch_abi_base_strings, 0, N_ABI_BASE_TYPES)
 
-#undef PARM
-#define PARM (argv[0] + parm_off)
+  LARCH_DRIVER_PARSE_PARM (la_target.isa.fpu, ISA_EXT_FPU, \
+			   loongarch_isa_ext_strings, 0, N_ISA_EXT_FPU_TYPES)
 
-/* Note: sizeof (OPTSTR_##NAME) equals the length of "<option>=".  */
-#undef MATCH_OPT
-#define MATCH_OPT(NAME) \
-  (strncmp (argv[0], OPTSTR_##NAME "=", \
-	    (parm_off = sizeof (OPTSTR_##NAME))) == 0)
+  LARCH_DRIVER_PARSE_PARM (la_target.isa.simd, ISA_EXT_SIMD, \
+			   loongarch_isa_ext_strings, 0, N_ISA_EXT_TYPES)
 
-  if (strcmp (argv[0], "no_link") == 0)
-    {
-      no_link = 1;
-    }
-  else if (MATCH_OPT (ABI_BASE))
-    {
-      LARCH_DRIVER_SET_M_FLAG (
-	loongarch_abi_base_strings, N_ABI_BASE_TYPES,
-	opt_abi_base_driver, PARM)
-    }
-  else if (MATCH_OPT (ISA_EXT_FPU))
-    {
-      LARCH_DRIVER_SET_M_FLAG (loongarch_isa_ext_strings, N_ISA_EXT_FPU_TYPES,
-			       opt_fpu_driver, PARM)
-    }
-  else if (MATCH_OPT (ARCH))
-    {
-      LARCH_DRIVER_SET_M_FLAG (loongarch_cpu_strings, N_ARCH_TYPES,
-			       opt_arch_driver, PARM)
-    }
-  else if (MATCH_OPT (TUNE))
-    {
-      LARCH_DRIVER_SET_M_FLAG (loongarch_cpu_strings, N_TUNE_TYPES,
-			       opt_tune_driver, PARM)
-    }
-  else if (MATCH_OPT (CMODEL))
-    {
-      LARCH_DRIVER_SET_M_FLAG (loongarch_cmodel_strings, N_CMODEL_TYPES,
-			       opt_cmodel_driver, PARM)
-    }
-  else /* switches */
-    {
-      int switch_idx = M_OPTION_NOT_SEEN;
+  LARCH_DRIVER_PARSE_PARM (la_target.cpu_arch, ARCH, \
+			   loongarch_cpu_strings, 0, N_ARCH_TYPES)
 
-      LARCH_DRIVER_SET_M_FLAG (loongarch_switch_strings, N_SWITCH_TYPES,
-			       switch_idx, argv[0])
+  LARCH_DRIVER_PARSE_PARM (la_target.cpu_tune, TUNE, \
+			   loongarch_cpu_strings, 0, N_TUNE_TYPES)
 
-      if (switch_idx != M_OPTION_NOT_SEEN)
-	opt_switches |= loongarch_switch_mask[switch_idx];
-    }
-  return "";
+  LARCH_DRIVER_PARSE_PARM (la_target.cmodel, CMODEL, \
+			   loongarch_cmodel_strings, 0, N_CMODEL_TYPES)
+
+  gcc_unreachable ();
+}
+
+static void
+driver_record_deferred_opts (struct loongarch_flags *flags)
+{
+  unsigned int i;
+  cl_deferred_option *opt;
+  vec<cl_deferred_option> *v = (vec<cl_deferred_option> *) la_deferred_options;
+
+  gcc_assert (flags);
+
+  /* Initialize flags */
+  flags->flt = M_OPT_UNSET;
+  flags->flt_str = NULL;
+  flags->sx[0] = flags->sx[1] = 0;
+
+  int sx_flag_idx = 0;
+
+  if (v)
+    FOR_EACH_VEC_ELT (*v, i, opt)
+      {
+	switch (opt->opt_index)
+	  {
+	  case OPT_mlsx:
+	    flags->sx[sx_flag_idx++] = ISA_EXT_SIMD_LSX * (opt->value ? 1 : -1);
+	    break;
+
+	  case OPT_mlasx:
+	    flags->sx[sx_flag_idx++] = ISA_EXT_SIMD_LASX * (opt->value ? 1 : -1);
+	    break;
+
+	  case OPT_msoft_float:
+	    flags->flt = ISA_EXT_NONE;
+	    flags->flt_str = OPTSTR_SOFT_FLOAT;
+	    break;
+
+	  case OPT_msingle_float:
+	    flags->flt = ISA_EXT_FPU32;
+	    flags->flt_str = OPTSTR_SINGLE_FLOAT;
+	    break;
+
+	  case OPT_mdouble_float:
+	    flags->flt = ISA_EXT_FPU64;
+	    flags->flt_str = OPTSTR_DOUBLE_FLOAT;
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	  }
+	gcc_assert (sx_flag_idx <= 2);
+      }
 }
 
 const char*
-driver_get_normalized_m_opts (int argc, const char **argv)
+driver_get_normalized_m_opts (int argc, const char **argv ATTRIBUTE_UNUSED)
 {
   if (argc != 0)
-    {
-      (void) argv;  /* To make compiler shut up about unused argument.  */
-      return " %eget_normalized_m_opts requires no argument.\n";
-    }
+    return " %eget_normalized_m_opts requires no argument.\n";
 
-  loongarch_config_target (& la_target,
-			   opt_switches,
-			   opt_arch_driver,
-			   opt_tune_driver,
-			   opt_fpu_driver,
-			   opt_abi_base_driver,
-			   opt_abi_ext_driver,
-			   opt_cmodel_driver,
-			   !no_link /* follow_multilib_list */);
+  struct loongarch_flags flags;
+  driver_record_deferred_opts (&flags);
+  loongarch_config_target (&la_target, &flags, !no_link /* follow_multilib_list */);
 
   /* Output normalized option strings.  */
   obstack_blank (&opts_obstack, 0);
@@ -160,11 +172,15 @@  driver_get_normalized_m_opts (int argc, const char **argv)
    APPEND_LTR (" %<m" OPTSTR_##NAME "=* " \
 	       " -m" OPTSTR_##NAME "=")
 
-  for (int i = 0; i < N_SWITCH_TYPES; i++)
-    {
-      APPEND_LTR (" %<m");
-      APPEND_VAL (loongarch_switch_strings[i]);
-    }
+#undef CLEAR_FLAG
+#define CLEAR_FLAG(NAME) \
+  APPEND_LTR ( " %<m" NAME " %<mno-" NAME )
+
+  CLEAR_FLAG (STR_ISA_EXT_LSX);
+  CLEAR_FLAG (STR_ISA_EXT_LASX);
+  CLEAR_FLAG (OPTSTR_SOFT_FLOAT);
+  CLEAR_FLAG (OPTSTR_SINGLE_FLOAT);
+  CLEAR_FLAG (OPTSTR_DOUBLE_FLOAT);
 
   APPEND_OPT (ABI_BASE);
   APPEND_VAL (loongarch_abi_base_strings[la_target.abi.base]);
@@ -175,6 +191,9 @@  driver_get_normalized_m_opts (int argc, const char **argv)
   APPEND_OPT (ISA_EXT_FPU);
   APPEND_VAL (loongarch_isa_ext_strings[la_target.isa.fpu]);
 
+  APPEND_OPT (ISA_EXT_SIMD);
+  APPEND_VAL (loongarch_isa_ext_strings[la_target.isa.simd]);
+
   APPEND_OPT (CMODEL);
   APPEND_VAL (loongarch_cmodel_strings[la_target.cmodel]);
 
diff --git a/gcc/config/loongarch/loongarch-driver.h b/gcc/config/loongarch/loongarch-driver.h
index ba8817a4621..b39b96dec36 100644
--- a/gcc/config/loongarch/loongarch-driver.h
+++ b/gcc/config/loongarch/loongarch-driver.h
@@ -24,33 +24,37 @@  along with GCC; see the file COPYING3.  If not see
 #include "loongarch-str.h"
 
 extern const char*
-driver_set_m_flag (int argc, const char **argv);
+la_driver_init (int argc, const char **argv);
+
+extern const char*
+driver_set_m_parm (int argc, const char **argv);
+
+extern const char*
+driver_set_no_link (int argc, const char **argv);
 
 extern const char*
 driver_get_normalized_m_opts (int argc, const char **argv);
 
 #define EXTRA_SPEC_FUNCTIONS \
-  { "set_m_flag", driver_set_m_flag  }, \
+  { "driver_init", la_driver_init }, \
+  { "set_m_parm", driver_set_m_parm  }, \
+  { "set_no_link", driver_set_no_link }, \
   { "get_normalized_m_opts", driver_get_normalized_m_opts  },
 
 /* Pre-process ABI-related options.  */
 #define LA_SET_PARM_SPEC(NAME) \
-  " %{m" OPTSTR_##NAME  "=*: %:set_m_flag(" OPTSTR_##NAME "=%*)}" \
-
-#define LA_SET_FLAG_SPEC(NAME) \
-  " %{m" OPTSTR_##NAME  ": %:set_m_flag(" OPTSTR_##NAME ")}" \
-
-#define DRIVER_HANDLE_MACHINE_OPTIONS			      \
-  " %{c|S|E|nostdlib: %:set_m_flag(no_link)}"		      \
-  " %{nostartfiles: %{nodefaultlibs: %:set_m_flag(no_link)}}" \
-  LA_SET_PARM_SPEC (ABI_BASE)				      \
-  LA_SET_PARM_SPEC (ARCH)				      \
-  LA_SET_PARM_SPEC (TUNE)				      \
-  LA_SET_PARM_SPEC (ISA_EXT_FPU)			      \
-  LA_SET_PARM_SPEC (CMODEL)				      \
-  LA_SET_FLAG_SPEC (SOFT_FLOAT)				      \
-  LA_SET_FLAG_SPEC (SINGLE_FLOAT)			      \
-  LA_SET_FLAG_SPEC (DOUBLE_FLOAT)			      \
+  " %{m" OPTSTR_##NAME  "=*: %:set_m_parm(" OPTSTR_##NAME " %*)}" \
+
+#define DRIVER_HANDLE_MACHINE_OPTIONS \
+  " %:driver_init()" \
+  " %{c|S|E|nostdlib: %:set_no_link()}" \
+  " %{nostartfiles: %{nodefaultlibs: %:set_no_link()}}" \
+  LA_SET_PARM_SPEC (ABI_BASE) \
+  LA_SET_PARM_SPEC (ARCH) \
+  LA_SET_PARM_SPEC (TUNE) \
+  LA_SET_PARM_SPEC (ISA_EXT_FPU) \
+  LA_SET_PARM_SPEC (ISA_EXT_SIMD) \
+  LA_SET_PARM_SPEC (CMODEL) \
   " %:get_normalized_m_opts()"
 
 #define DRIVER_SELF_SPECS \
diff --git a/gcc/config/loongarch/loongarch-opts.cc b/gcc/config/loongarch/loongarch-opts.cc
index a52e25236ea..a2d5a98a679 100644
--- a/gcc/config/loongarch/loongarch-opts.cc
+++ b/gcc/config/loongarch/loongarch-opts.cc
@@ -26,9 +26,11 @@  along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "obstack.h"
 #include "diagnostic-core.h"
+
 #include "loongarch-cpu.h"
 #include "loongarch-opts.h"
 #include "loongarch-str.h"
+#include "loongarch-def.h"
 
 struct loongarch_target la_target;
 
@@ -76,16 +78,6 @@  init_enabled_abi_types ()
 #endif
 }
 
-/* Switch masks.  */
-#undef M
-#define M(NAME) OPTION_MASK_##NAME
-const int loongarch_switch_mask[N_SWITCH_TYPES] = {
-  /* SW_SOFT_FLOAT */    M(FORCE_SOFTF),
-  /* SW_SINGLE_FLOAT */  M(FORCE_F32),
-  /* SW_DOUBLE_FLOAT */  M(FORCE_F64),
-};
-#undef M
-
 /* String processing.  */
 static struct obstack msg_obstack;
 #define APPEND_STRING(STR) obstack_grow (&msg_obstack, STR, strlen(STR));
@@ -104,9 +96,9 @@  static int isa_fpu_compat_p (const struct loongarch_isa *set1,
 			     const struct loongarch_isa *set2);
 static int abi_compat_p (const struct loongarch_isa *isa,
 			 struct loongarch_abi abi);
-static int abi_default_cpu_arch (struct loongarch_abi abi);
+static int abi_default_cpu_arch (struct loongarch_abi abi, struct loongarch_isa *isa);
 
-/* Checking configure-time defaults.  */
+/* Mandatory configure-time defaults.  */
 #ifndef DEFAULT_ABI_BASE
 #error missing definition of DEFAULT_ABI_BASE in ${tm_defines}.
 #endif
@@ -119,21 +111,57 @@  static int abi_default_cpu_arch (struct loongarch_abi abi);
 #error missing definition of DEFAULT_CPU_ARCH in ${tm_defines}.
 #endif
 
-#ifndef DEFAULT_ISA_EXT_FPU
-#error missing definition of DEFAULT_ISA_EXT_FPU in ${tm_defines}.
+/* Optional configure-time defaults.  */
+#ifdef DEFAULT_CPU_TUNE
+static int with_default_tune = 1;
+#else
+#define DEFAULT_CPU_TUNE -1
+static int with_default_tune = 0;
+#endif
+
+#ifdef DEFAULT_ISA_EXT_FPU
+static int with_default_fpu = 1;
+#else
+#define DEFAULT_ISA_EXT_FPU -1
+static int with_default_fpu = 0;
+#endif
+
+#ifdef DEFAULT_ISA_EXT_SIMD
+static int with_default_simd = 1;
+#else
+#define DEFAULT_ISA_EXT_SIMD -1
+static int with_default_simd = 0;
 #endif
 
-/* Handle combinations of -m machine option values
+
+/* Initialize loongarch_target from separate option variables.  */
+
+void
+loongarch_init_target (struct loongarch_target *target,
+		       int cpu_arch, int cpu_tune, int fpu, int simd,
+		       int abi_base, int abi_ext, int cmodel)
+{
+  if (!target)
+    return;
+  target->cpu_arch = cpu_arch;
+  target->cpu_tune = cpu_tune;
+  target->isa.fpu = fpu;
+  target->isa.simd = simd;
+  target->abi.base = abi_base;
+  target->abi.ext = abi_ext;
+  target->cmodel = cmodel;
+}
+
+
+/* Handle combinations of -m parameters
    (see loongarch.opt and loongarch-opts.h).  */
+
 void
 loongarch_config_target (struct loongarch_target *target,
-			 HOST_WIDE_INT opt_switches,
-			 int opt_arch, int opt_tune, int opt_fpu,
-			 int opt_abi_base, int opt_abi_ext,
-			 int opt_cmodel, int follow_multilib_list)
+			 struct loongarch_flags *flags,
+			 int follow_multilib_list_p)
 {
   struct loongarch_target t;
-
   if (!target)
     return;
 
@@ -142,67 +170,63 @@  loongarch_config_target (struct loongarch_target *target,
   obstack_init (&msg_obstack);
 
   struct {
-    int arch, tune, fpu, abi_base, abi_ext, cmodel;
+    int arch, tune, fpu, simd, abi_base, abi_ext, cmodel, abi_flt;
   } constrained = {
-      M_OPT_ABSENT(opt_arch)     ? 0 : 1,
-      M_OPT_ABSENT(opt_tune)     ? 0 : 1,
-      M_OPT_ABSENT(opt_fpu)      ? 0 : 1,
-      M_OPT_ABSENT(opt_abi_base) ? 0 : 1,
-      M_OPT_ABSENT(opt_abi_ext)  ? 0 : 1,
-      M_OPT_ABSENT(opt_cmodel)   ? 0 : 1,
+      M_OPT_ABSENT (target->cpu_arch)	  ? 0 : 1,
+      M_OPT_ABSENT (target->cpu_tune)	  ? 0 : 1,
+      M_OPT_ABSENT (target->isa.fpu)	  ? 0 : 1,
+      M_OPT_ABSENT (target->isa.simd)	  ? 0 : 1,
+      M_OPT_ABSENT (target->abi.base)	  ? 0 : 1,
+      M_OPT_ABSENT (target->abi.ext)	  ? 0 : 1,
+      M_OPT_ABSENT (target->cmodel)	  ? 0 : 1,
+      M_OPT_ABSENT (target->abi.base)	  ? 0 : 1,
   };
 
-#define on(NAME) ((loongarch_switch_mask[(SW_##NAME)] & opt_switches) \
-		  && (on_switch = (SW_##NAME), 1))
-  int on_switch;
-
   /* 1.  Target ABI */
-  t.abi.base = constrained.abi_base ? opt_abi_base : DEFAULT_ABI_BASE;
+  if (constrained.abi_base)
+    t.abi.base = target->abi.base;
+  else
+    t.abi.base = DEFAULT_ABI_BASE;
 
-  t.abi.ext = constrained.abi_ext ? opt_abi_ext : DEFAULT_ABI_EXT;
+  t.abi.ext = constrained.abi_ext ? target->abi.ext : DEFAULT_ABI_EXT;
 
-  /* Extra switch handling.  */
-  if (on (SOFT_FLOAT) || on (SINGLE_FLOAT) || on (DOUBLE_FLOAT))
+  /* Process -m*-float flags */
+  if (flags && !M_OPT_ABSENT (flags->flt))
     {
-      switch (on_switch)
-	{
-	  case SW_SOFT_FLOAT:
-	    opt_fpu = ISA_EXT_NOFPU;
-	    break;
-
-	  case SW_SINGLE_FLOAT:
-	    opt_fpu = ISA_EXT_FPU32;
-	    break;
-
-	  case SW_DOUBLE_FLOAT:
-	    opt_fpu = ISA_EXT_FPU64;
-	    break;
+      /* Modifying the original "target" here makes it easier to write the
+	 t.isa.fpu assignment below, because otherwise there would be three
+	 levels of precedence (-m*-float / -mfpu / -march) to be handled
+	 (now the first two are merged). */
 
-	  default:
-	    gcc_unreachable();
-	}
+      target->isa.fpu = flags->flt;
       constrained.fpu = 1;
 
       /* The target ISA is not ready yet, but (isa_required (t.abi)
 	 + forced fpu) is enough for computing the forced base ABI.  */
-      struct loongarch_isa default_isa = isa_required (t.abi);
-      struct loongarch_isa force_isa = default_isa;
-      struct loongarch_abi force_abi = t.abi;
-      force_isa.fpu = opt_fpu;
+
+      struct loongarch_isa force_isa = isa_required (t.abi);
+      force_isa.fpu = flags->flt;
+
+      struct loongarch_abi force_abi;
       force_abi.base = isa_default_abi (&force_isa).base;
 
-      if (constrained.abi_base && (t.abi.base != force_abi.base))
-	inform (UNKNOWN_LOCATION,
-		"%<-m%s%> overrides %<-m%s=%s%>, adjusting ABI to %qs",
-		loongarch_switch_strings[on_switch],
-		OPTSTR_ABI_BASE, loongarch_abi_base_strings[t.abi.base],
-		abi_str (force_abi));
+      if (constrained.abi_base && constrained.abi_flt
+	  && (t.abi.base != force_abi.base))
+	{
+	  force_abi.ext = t.abi.ext;
+	  inform (UNKNOWN_LOCATION,
+		  "%<-m%s%> overrides %<-m%s=%s%>, adjusting ABI to %qs",
+		  flags->flt_str, OPTSTR_ABI_BASE,
+		  loongarch_abi_base_strings[t.abi.base],
+		  abi_str (force_abi));
+	}
 
       t.abi.base = force_abi.base;
+      constrained.abi_flt = 1;
     }
 
 #ifdef LA_DISABLE_MULTILIB
-  if (follow_multilib_list)
+  if (follow_multilib_list_p)
     if (t.abi.base != DEFAULT_ABI_BASE || t.abi.ext != DEFAULT_ABI_EXT)
       {
 	static const struct loongarch_abi default_abi
@@ -214,18 +238,22 @@  loongarch_config_target (struct loongarch_target *target,
 #endif
 
   /* 2.  Target CPU */
-  t.cpu_arch = constrained.arch ? opt_arch : DEFAULT_CPU_ARCH;
+  t.cpu_arch = constrained.arch ? target->cpu_arch : DEFAULT_CPU_ARCH;
+
+  /* If cpu_tune is not set using neither -mtune nor --with-tune,
+     the current cpu_arch is used as its default. */
+  t.cpu_tune = constrained.tune ? target->cpu_tune
+    : (constrained.arch ? target->cpu_arch :
+       (with_default_tune ? DEFAULT_CPU_TUNE : DEFAULT_CPU_ARCH));
 
-  t.cpu_tune = constrained.tune ? opt_tune
-    : (constrained.arch ? DEFAULT_CPU_ARCH : DEFAULT_CPU_TUNE);
 
+  /* Handle -march/tune=native */
 #ifdef __loongarch__
   /* For native compilers, gather local CPU information
      and fill the "CPU_NATIVE" index of arrays defined in
      loongarch-cpu.c.  */
 
-  t.cpu_native = fill_native_cpu_config (t.cpu_arch == CPU_NATIVE,
-					 t.cpu_tune == CPU_NATIVE);
+  fill_native_cpu_config (&t);
 
 #else
   if (t.cpu_arch == CPU_NATIVE)
@@ -239,17 +267,111 @@  loongarch_config_target (struct loongarch_target *target,
 		 "-m" OPTSTR_TUNE "=" STR_CPU_NATIVE);
 #endif
 
-  /* 3.  Target ISA */
+  /* Handle -march/tune=abi-default */
+  if (t.cpu_tune == CPU_ABI_DEFAULT)
+    t.cpu_tune = abi_default_cpu_arch (t.abi, NULL);
+
+  if (t.cpu_arch == CPU_ABI_DEFAULT)
+    {
+      t.cpu_arch = abi_default_cpu_arch (t.abi, &(t.isa));
+      loongarch_cpu_default_isa[t.cpu_arch] = t.isa;
+    }
+
+  /* 3.  Target base ISA */
 config_target_isa:
 
   /* Get default ISA from "-march" or its default value.  */
-  t.isa = loongarch_cpu_default_isa[LARCH_ACTUAL_ARCH];
+  t.isa = loongarch_cpu_default_isa[t.cpu_arch];
 
   /* Apply incremental changes.  */
   /* "-march=native" overrides the default FPU type.  */
-  t.isa.fpu = constrained.fpu ? opt_fpu :
-    ((t.cpu_arch == CPU_NATIVE && constrained.arch) ?
-     t.isa.fpu : DEFAULT_ISA_EXT_FPU);
+
+  t.isa.fpu = constrained.fpu ? target->isa.fpu :
+    (constrained.arch ? t.isa.fpu :
+    (with_default_fpu ? DEFAULT_ISA_EXT_FPU : t.isa.fpu));
+
+  t.isa.simd = constrained.simd ? target->isa.simd :
+    (constrained.arch ? t.isa.simd :
+    (with_default_simd ? DEFAULT_ISA_EXT_SIMD : t.isa.simd));
+
+  /* apply -m[no-]lsx and -m[no-]lasx flags */
+  if (flags)
+    for (int i = 0; i < 2; i++)
+      {
+	switch (SX_FLAG_TYPE (flags->sx[i]))
+	  {
+	  case ISA_EXT_SIMD_LSX:
+	    constrained.simd = 1;
+	    if (flags->sx[i] > 0 && t.isa.simd != ISA_EXT_SIMD_LASX)
+	      t.isa.simd = ISA_EXT_SIMD_LSX;
+	    else if (flags->sx[i] < 0)
+	      t.isa.simd = ISA_EXT_NONE;
+	    break;
+
+	  case ISA_EXT_SIMD_LASX:
+	    constrained.simd = 1;
+	    if (flags->sx[i] < 0 && t.isa.simd == ISA_EXT_SIMD_LASX)
+	      t.isa.simd = ISA_EXT_SIMD_LSX;
+	    else if (flags->sx[i] > 0)
+	      t.isa.simd = ISA_EXT_SIMD_LASX;
+	    break;
+
+	  case 0:
+	    break;
+
+	  default:
+	    gcc_unreachable();
+	  }
+      }
+
+  /* All SIMD extensions imply a 64-bit FPU:
+     - silently adjust t.isa.fpu to "fpu64" if it is unconstrained.
+     - warn if -msingle-float / -msoft-float is on,
+       then disable SIMD extensions (done in driver)
+     - abort if -mfpu=0 / -mfpu=32 is forced.  */
+
+  if (t.isa.simd != ISA_EXT_NONE && t.isa.fpu != ISA_EXT_FPU64)
+    {
+      if (!constrained.fpu)
+	{
+	  /* As long as the arch-default "t.isa.simd" is set to non-zero
+	     for an element "t" in loongarch_cpu_default_isa, "t.isa.fpu"
+	     should be set to "ISA_EXT_FPU64" accordingly.  Thus reaching
+	     here must be the result of forcing -mlsx/-mlasx explicitly.  */
+	  gcc_assert (constrained.simd);
+
+	  inform (UNKNOWN_LOCATION,
+		  "enabing %qs promotes %<%s%s%> to %<%s%s%>",
+		  loongarch_isa_ext_strings[t.isa.simd],
+		  OPTSTR_ISA_EXT_FPU, loongarch_isa_ext_strings[t.isa.fpu],
+		  OPTSTR_ISA_EXT_FPU, loongarch_isa_ext_strings[ISA_EXT_FPU64]);
+
+	  t.isa.fpu = ISA_EXT_FPU64;
+	}
+      else if (flags && (flags->flt == ISA_EXT_NONE || flags->flt == ISA_EXT_FPU32))
+	{
+	  if (constrained.simd)
+	    inform (UNKNOWN_LOCATION,
+		    "%qs is disabled by %<-m%s%>, because it requires %<%s%s%>",
+		    loongarch_isa_ext_strings[t.isa.simd], flags->flt_str,
+		    OPTSTR_ISA_EXT_FPU, loongarch_isa_ext_strings[ISA_EXT_FPU64]);
+
+	  t.isa.simd = ISA_EXT_NONE;
+	}
+      else
+	{
+	  /* -mfpu=0 / -mfpu=32 is set.  */
+	  if (constrained.simd)
+	    fatal_error (UNKNOWN_LOCATION,
+			 "%<-m%s=%s%> conflicts with %qs, which requires %<%s%s%>",
+			 OPTSTR_ISA_EXT_FPU, loongarch_isa_ext_strings[t.isa.fpu],
+			 loongarch_isa_ext_strings[t.isa.simd],
+			 OPTSTR_ISA_EXT_FPU, loongarch_isa_ext_strings[ISA_EXT_FPU64]);
+
+	  /* Same as above.  */
+	  t.isa.simd = ISA_EXT_NONE;
+	}
+    }
 
 
   /* 4.  ABI-ISA compatibility */
@@ -272,7 +394,7 @@  config_target_isa:
     {
       /* Base architecture can only be implied by -march,
 	 so we adjust that first if it is not constrained.  */
-      int fallback_arch = abi_default_cpu_arch (t.abi);
+      int fallback_arch = abi_default_cpu_arch (t.abi, NULL);
 
       if (t.cpu_arch == CPU_NATIVE)
 	warning (0, "your native CPU architecture (%qs) "
@@ -319,7 +441,7 @@  fatal:
   if (abi_tmp.base != t.abi.base || abi_tmp.ext != t.abi.ext)
     {
       /* This flag is only set in the GCC driver.  */
-      if (follow_multilib_list)
+      if (follow_multilib_list_p)
 	{
 
 	  /* Continue falling back until we find a feasible ABI type
@@ -360,7 +482,7 @@  fatal:
 fallback:
       t.abi = abi_tmp;
     }
-  else if (follow_multilib_list)
+  else if (follow_multilib_list_p)
     {
       if (!is_multilib_enabled (t.abi))
 	{
@@ -375,7 +497,7 @@  fallback:
 
 
   /* 5.  Target code model */
-  t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL;
+  t.cmodel = constrained.cmodel ? target->cmodel : CMODEL_NORMAL;
 
   switch (t.cmodel)
     {
@@ -419,7 +541,7 @@  isa_default_abi (const struct loongarch_isa *isa)
 	  abi.base = ABI_BASE_LP64F;
 	break;
 
-      case ISA_EXT_NOFPU:
+      case ISA_EXT_NONE:
 	if (isa->base == ISA_BASE_LA64V100)
 	  abi.base = ABI_BASE_LP64S;
 	break;
@@ -459,7 +581,7 @@  isa_fpu_compat_p (const struct loongarch_isa *set1,
       case ISA_EXT_FPU32:
 	return set1->fpu == ISA_EXT_FPU32 || set1->fpu == ISA_EXT_FPU64;
 
-      case ISA_EXT_NOFPU:
+      case ISA_EXT_NONE:
 	return 1;
 
       default:
@@ -483,16 +605,22 @@  abi_compat_p (const struct loongarch_isa *isa, struct loongarch_abi abi)
 /* The behavior of this function should be consistent
    with config.gcc.  */
 static inline int
-abi_default_cpu_arch (struct loongarch_abi abi)
+abi_default_cpu_arch (struct loongarch_abi abi,
+		      struct loongarch_isa *isa)
 {
-  switch (abi.base)
-    {
-      case ABI_BASE_LP64D:
-      case ABI_BASE_LP64F:
-      case ABI_BASE_LP64S:
-	if (abi.ext == ABI_EXT_BASE)
+  static struct loongarch_isa tmp;
+  if (!isa)
+    isa = &tmp;
+
+  if (abi.ext == ABI_EXT_BASE)
+    switch (abi.base)
+      {
+	case ABI_BASE_LP64D:
+	case ABI_BASE_LP64F:
+	case ABI_BASE_LP64S:
+	  *isa = isa_required (abi);
 	  return CPU_LOONGARCH64;
-    }
+      }
   gcc_unreachable ();
 }
 
@@ -521,7 +649,7 @@  isa_str (const struct loongarch_isa *isa, char separator)
   APPEND_STRING (loongarch_isa_base_strings[isa->base])
   APPEND1 (separator)
 
-  if (isa->fpu == ISA_EXT_NOFPU)
+  if (isa->fpu == ISA_EXT_NONE)
     {
       APPEND_STRING ("no" OPTSTR_ISA_EXT_FPU)
     }
@@ -530,6 +658,18 @@  isa_str (const struct loongarch_isa *isa, char separator)
       APPEND_STRING (OPTSTR_ISA_EXT_FPU)
       APPEND_STRING (loongarch_isa_ext_strings[isa->fpu])
     }
+
+  switch (isa->simd)
+    {
+      case ISA_EXT_SIMD_LSX:
+      case ISA_EXT_SIMD_LASX:
+	APPEND1 (separator);
+	APPEND_STRING (loongarch_isa_ext_strings[isa->simd]);
+	break;
+
+      default:
+	gcc_assert (isa->simd == 0);
+    }
   APPEND1 ('\0')
 
   /* Add more here.  */
@@ -542,18 +682,12 @@  arch_str (const struct loongarch_target *target)
 {
   if (target->cpu_arch == CPU_NATIVE)
     {
-      if (target->cpu_native == CPU_NATIVE)
-	{
-	  /* Describe a native CPU with unknown PRID.  */
-	  const char* isa_string = isa_str (&target->isa, ',');
-	  APPEND_STRING ("PRID: 0x")
-	  APPEND_STRING (get_native_prid_str ())
-	  APPEND_STRING (", ISA features: ")
-	  APPEND_STRING (isa_string)
-	  APPEND1 ('\0')
-	}
-      else
-	APPEND_STRING (loongarch_cpu_strings[target->cpu_native]);
+      /* Describe a native CPU with unknown PRID.  */
+      const char* isa_string = isa_str (&target->isa, ',');
+      APPEND_STRING ("PRID: 0x")
+      APPEND_STRING (get_native_prid_str ())
+      APPEND_STRING (", ISA features: ")
+      APPEND_STRING (isa_string)
     }
   else
     APPEND_STRING (loongarch_cpu_strings[target->cpu_arch]);
@@ -594,3 +728,23 @@  multilib_enabled_abi_list ()
 
   return XOBFINISH (&msg_obstack, const char *);
 }
+
+/* option status feedback for "gcc --help=target -Q" */
+void
+loongarch_update_gcc_opt_status (struct loongarch_target *target,
+				 struct gcc_options *opts,
+				 struct gcc_options *opts_set)
+{
+  (void) opts_set;
+
+  /* status of -mabi */
+  opts->x_la_opt_abi_base = target->abi.base;
+
+  /* status of -march and -mtune */
+  opts->x_la_opt_cpu_arch = target->cpu_arch;
+  opts->x_la_opt_cpu_tune = target->cpu_tune;
+
+  /* status of -mfpu */
+  opts->x_la_opt_fpu = target->isa.fpu;
+  opts->x_la_opt_simd = target->isa.simd;
+}
diff --git a/gcc/config/loongarch/loongarch-opts.h b/gcc/config/loongarch/loongarch-opts.h
index b1ff54426e4..a116f15e8be 100644
--- a/gcc/config/loongarch/loongarch-opts.h
+++ b/gcc/config/loongarch/loongarch-opts.h
@@ -21,24 +21,39 @@  along with GCC; see the file COPYING3.  If not see
 #ifndef LOONGARCH_OPTS_H
 #define LOONGARCH_OPTS_H
 
+#include "loongarch-def.h"
 
 /* Target configuration */
 extern struct loongarch_target la_target;
 
-/* Switch masks */
-extern const int loongarch_switch_mask[];
-
-#include "loongarch-def.h"
+/* Flag status */
+struct loongarch_flags {
+    int flt; const char* flt_str;
+#define SX_FLAG_TYPE(x) ((x) < 0 ? -(x) : (x))
+    int sx[2];
+};
 
 #if !defined(IN_LIBGCC2) && !defined(IN_TARGET_LIBS) && !defined(IN_RTS)
+
+/* Initialize loongarch_target from separate option variables.  */
+void
+loongarch_init_target (struct loongarch_target *target,
+		       int cpu_arch, int cpu_tune, int fpu, int simd,
+		       int abi_base, int abi_ext, int cmodel);
+
+
 /* Handler for "-m" option combinations,
    shared by the driver and the compiler proper.  */
 void
 loongarch_config_target (struct loongarch_target *target,
-			 HOST_WIDE_INT opt_switches,
-			 int opt_arch, int opt_tune, int opt_fpu,
-			 int opt_abi_base, int opt_abi_ext,
-			 int opt_cmodel, int follow_multilib_list);
+			 struct loongarch_flags *flags,
+			 int follow_multilib_list_p);
+
+/* option status feedback for "gcc --help=target -Q" */
+void
+loongarch_update_gcc_opt_status (struct loongarch_target *target,
+				 struct gcc_options *opts,
+				 struct gcc_options *opts_set);
 #endif
 
 
@@ -46,15 +61,15 @@  loongarch_config_target (struct loongarch_target *target,
 #define TARGET_CMODEL_NORMAL	    (la_target.cmodel == CMODEL_NORMAL)
 #define TARGET_CMODEL_TINY	    (la_target.cmodel == CMODEL_TINY)
 #define TARGET_CMODEL_TINY_STATIC   (la_target.cmodel == CMODEL_TINY_STATIC)
-#define TARGET_CMODEL_MEDIUM	    (la_target.cmodel == CMODEL_MEDIUM)
+#define TARGET_CMODEL_MEDIUM        (la_target.cmodel == CMODEL_MEDIUM)
 #define TARGET_CMODEL_LARGE	    (la_target.cmodel == CMODEL_LARGE)
 #define TARGET_CMODEL_EXTREME	    (la_target.cmodel == CMODEL_EXTREME)
 
-#define TARGET_HARD_FLOAT	    (la_target.isa.fpu != ISA_EXT_NOFPU)
+#define TARGET_HARD_FLOAT	    (la_target.isa.fpu != ISA_EXT_NONE)
 #define TARGET_HARD_FLOAT_ABI	    (la_target.abi.base == ABI_BASE_LP64D \
 				     || la_target.abi.base == ABI_BASE_LP64F)
 
-#define TARGET_SOFT_FLOAT	  (la_target.isa.fpu == ISA_EXT_NOFPU)
+#define TARGET_SOFT_FLOAT	  (la_target.isa.fpu == ISA_EXT_NONE)
 #define TARGET_SOFT_FLOAT_ABI	  (la_target.abi.base == ABI_BASE_LP64S)
 #define TARGET_SINGLE_FLOAT	  (la_target.isa.fpu == ISA_EXT_FPU32)
 #define TARGET_SINGLE_FLOAT_ABI	  (la_target.abi.base == ABI_BASE_LP64F)
@@ -66,30 +81,16 @@  loongarch_config_target (struct loongarch_target *target,
 				   || la_target.abi.base == ABI_BASE_LP64F \
 				   || la_target.abi.base == ABI_BASE_LP64S)
 
-#define TARGET_ARCH_NATIVE	  (la_target.cpu_arch == CPU_NATIVE)
-#define LARCH_ACTUAL_ARCH	  (TARGET_ARCH_NATIVE \
-				   ? (la_target.cpu_native < N_ARCH_TYPES \
-				      ? (la_target.cpu_native) : (CPU_NATIVE)) \
-				      : (la_target.cpu_arch))
+#define ISA_HAS_LSX		  (la_target.isa.simd == ISA_EXT_SIMD_LSX \
+				   || la_target.isa.simd == ISA_EXT_SIMD_LASX)
+#define ISA_HAS_LASX		  (la_target.isa.simd == ISA_EXT_SIMD_LASX)
 
-#define TARGET_TUNE_NATIVE	(la_target.cpu_tune == CPU_NATIVE)
-#define LARCH_ACTUAL_TUNE		(TARGET_TUNE_NATIVE \
-				 ? (la_target.cpu_native < N_TUNE_TYPES \
-				    ? (la_target.cpu_native) : (CPU_NATIVE)) \
-				    : (la_target.cpu_tune))
 
-#define TARGET_ARCH_LOONGARCH64	  (LARCH_ACTUAL_ARCH == CPU_LOONGARCH64)
-#define TARGET_ARCH_LA464	  (LARCH_ACTUAL_ARCH == CPU_LA464)
-
-#define TARGET_TUNE_LOONGARCH64	  (LARCH_ACTUAL_TUNE == CPU_LOONGARCH64)
-#define TARGET_TUNE_LA464	  (LARCH_ACTUAL_TUNE == CPU_LA464)
+/* TARGET_ macros for use in *.md template conditionals */
+#define TARGET_uARCH_LA464	  (la_target.cpu_tune == CPU_LA464)
 
 /* Note: optimize_size may vary across functions,
    while -m[no]-memcpy imposes a global constraint.  */
 #define TARGET_DO_OPTIMIZE_BLOCK_MOVE_P  loongarch_do_optimize_block_move_p()
 
-#ifndef HAVE_AS_EXPLICIT_RELOCS
-#define HAVE_AS_EXPLICIT_RELOCS 0
-#endif
-
 #endif /* LOONGARCH_OPTS_H */
diff --git a/gcc/config/loongarch/loongarch-str.h b/gcc/config/loongarch/loongarch-str.h
index af2e82a321f..a3e0510493b 100644
--- a/gcc/config/loongarch/loongarch-str.h
+++ b/gcc/config/loongarch/loongarch-str.h
@@ -27,13 +27,14 @@  along with GCC; see the file COPYING3.  If not see
 #define OPTSTR_TUNE "tune"
 
 #define STR_CPU_NATIVE "native"
+#define STR_CPU_ABI_DEFAULT "abi-default"
 #define STR_CPU_LOONGARCH64 "loongarch64"
 #define STR_CPU_LA464 "la464"
 
 #define STR_ISA_BASE_LA64V100 "la64"
 
 #define OPTSTR_ISA_EXT_FPU "fpu"
-#define STR_ISA_EXT_NOFPU "none"
+#define STR_NONE "none"
 #define STR_ISA_EXT_FPU0 "0"
 #define STR_ISA_EXT_FPU32 "32"
 #define STR_ISA_EXT_FPU64 "64"
@@ -42,6 +43,10 @@  along with GCC; see the file COPYING3.  If not see
 #define OPTSTR_SINGLE_FLOAT "single-float"
 #define OPTSTR_DOUBLE_FLOAT "double-float"
 
+#define OPTSTR_ISA_EXT_SIMD "simd"
+#define STR_ISA_EXT_LSX "lsx"
+#define STR_ISA_EXT_LASX "lasx"
+
 #define OPTSTR_ABI_BASE "abi"
 #define STR_ABI_BASE_LP64D "lp64d"
 #define STR_ABI_BASE_LP64F "lp64f"
diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
index 86d58784113..4fefa2d8b15 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -2553,7 +2553,7 @@  loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
 
   if (flag_plt)
     {
-      switch (la_opt_cmodel)
+      switch (la_target.cmodel)
 	{
 	case CMODEL_NORMAL:
 	  insn = emit_call_insn (gen_call_value_internal (v0,
@@ -2595,7 +2595,7 @@  loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
     {
       rtx dest = gen_reg_rtx (Pmode);
 
-      switch (la_opt_cmodel)
+      switch (la_target.cmodel)
 	{
 	case CMODEL_NORMAL:
 	case CMODEL_MEDIUM:
@@ -6007,8 +6007,8 @@  loongarch_adjust_cost (rtx_insn *, int dep_type, rtx_insn *, int cost,
 static int
 loongarch_issue_rate (void)
 {
-  if ((unsigned long) LARCH_ACTUAL_TUNE < N_TUNE_TYPES)
-    return loongarch_cpu_issue_rate[LARCH_ACTUAL_TUNE];
+  if ((unsigned long) la_target.cpu_tune < N_TUNE_TYPES)
+    return loongarch_cpu_issue_rate[la_target.cpu_tune];
   else
     return 1;
 }
@@ -6019,8 +6019,8 @@  loongarch_issue_rate (void)
 static int
 loongarch_multipass_dfa_lookahead (void)
 {
-  if ((unsigned long) LARCH_ACTUAL_TUNE < N_ARCH_TYPES)
-    return loongarch_cpu_multipass_dfa_lookahead[LARCH_ACTUAL_TUNE];
+  if ((unsigned long) la_target.cpu_tune < N_ARCH_TYPES)
+    return loongarch_cpu_multipass_dfa_lookahead[la_target.cpu_tune];
   else
     return 0;
 }
@@ -6197,17 +6197,54 @@  loongarch_init_machine_status (void)
 }
 
 static void
-loongarch_option_override_internal (struct gcc_options *opts)
+loongarch_cpu_option_override (struct loongarch_target *target,
+			       struct gcc_options *opts,
+			       struct gcc_options *opts_set)
+{
+  /* alignments */
+  if (opts->x_flag_align_functions && !opts->x_str_align_functions)
+    opts->x_str_align_functions
+      = loongarch_cpu_align[target->cpu_tune].function;
+
+  if (opts->x_flag_align_labels && !opts->x_str_align_labels)
+    opts->x_str_align_labels = loongarch_cpu_align[target->cpu_tune].label;
+
+  /* Set up parameters to be used in prefetching algorithm.  */
+  int simultaneous_prefetches
+    = loongarch_cpu_cache[target->cpu_tune].simultaneous_prefetches;
+
+  SET_OPTION_IF_UNSET (opts, opts_set, param_simultaneous_prefetches,
+		       simultaneous_prefetches);
+
+  SET_OPTION_IF_UNSET (opts, opts_set, param_l1_cache_line_size,
+		       loongarch_cpu_cache[target->cpu_tune].l1d_line_size);
+
+  SET_OPTION_IF_UNSET (opts, opts_set, param_l1_cache_size,
+		       loongarch_cpu_cache[target->cpu_tune].l1d_size);
+
+  SET_OPTION_IF_UNSET (opts, opts_set, param_l2_cache_size,
+		       loongarch_cpu_cache[target->cpu_tune].l2d_size);
+}
+
+static void
+loongarch_option_override_internal (struct gcc_options *opts,
+				    struct gcc_options *opts_set)
 {
   int i, regno, mode;
 
   if (flag_pic)
     g_switch_value = 0;
 
+  loongarch_init_target (&la_target,
+			 la_opt_cpu_arch, la_opt_cpu_tune, la_opt_fpu,
+			 la_opt_simd, la_opt_abi_base, la_opt_abi_ext,
+			 la_opt_cmodel);
+
   /* Handle target-specific options: compute defaults/conflicts etc.  */
-  loongarch_config_target (&la_target, la_opt_switches,
-			   la_opt_cpu_arch, la_opt_cpu_tune, la_opt_fpu,
-			   la_opt_abi_base, la_opt_abi_ext, la_opt_cmodel, 0);
+  loongarch_config_target (&la_target, NULL, 0);
+
+  loongarch_update_gcc_opt_status (&la_target, opts, opts_set);
+  loongarch_cpu_option_override (&la_target, opts, opts_set);
 
   if (TARGET_ABI_LP64)
     flag_pcc_struct_return = 0;
@@ -6216,33 +6253,13 @@  loongarch_option_override_internal (struct gcc_options *opts)
   if (optimize_size)
     loongarch_cost = &loongarch_rtx_cost_optimize_size;
   else
-    loongarch_cost = &loongarch_cpu_rtx_cost_data[LARCH_ACTUAL_TUNE];
+    loongarch_cost = &loongarch_cpu_rtx_cost_data[la_target.cpu_tune];
 
   /* If the user hasn't specified a branch cost, use the processor's
      default.  */
   if (loongarch_branch_cost == 0)
     loongarch_branch_cost = loongarch_cost->branch_cost;
 
-  /* Set up parameters to be used in prefetching algorithm.  */
-  int simultaneous_prefetches
-    = loongarch_cpu_cache[LARCH_ACTUAL_TUNE].simultaneous_prefetches;
-
-  SET_OPTION_IF_UNSET (opts, &global_options_set,
-		       param_simultaneous_prefetches,
-		       simultaneous_prefetches);
-
-  SET_OPTION_IF_UNSET (opts, &global_options_set,
-		       param_l1_cache_line_size,
-		       loongarch_cpu_cache[LARCH_ACTUAL_TUNE].l1d_line_size);
-
-  SET_OPTION_IF_UNSET (opts, &global_options_set,
-		       param_l1_cache_size,
-		       loongarch_cpu_cache[LARCH_ACTUAL_TUNE].l1d_size);
-
-  SET_OPTION_IF_UNSET (opts, &global_options_set,
-		       param_l2_cache_size,
-		       loongarch_cpu_cache[LARCH_ACTUAL_TUNE].l2d_size);
-
 
   /* Enable sw prefetching at -O3 and higher.  */
   if (opts->x_flag_prefetch_loop_arrays < 0
@@ -6250,12 +6267,6 @@  loongarch_option_override_internal (struct gcc_options *opts)
       && !opts->x_optimize_size)
     opts->x_flag_prefetch_loop_arrays = 1;
 
-  if (opts->x_flag_align_functions && !opts->x_str_align_functions)
-    opts->x_str_align_functions = loongarch_cpu_align[LARCH_ACTUAL_TUNE].function;
-
-  if (opts->x_flag_align_labels && !opts->x_str_align_labels)
-    opts->x_str_align_labels = loongarch_cpu_align[LARCH_ACTUAL_TUNE].label;
-
   if (TARGET_DIRECT_EXTERN_ACCESS && flag_shlib)
     error ("%qs cannot be used for compiling a shared library",
 	   "-mdirect-extern-access");
@@ -6325,7 +6336,7 @@  loongarch_option_override_internal (struct gcc_options *opts)
 static void
 loongarch_option_override (void)
 {
-  loongarch_option_override_internal (&global_options);
+  loongarch_option_override_internal (&global_options, &global_options_set);
 }
 
 /* Implement TARGET_CONDITIONAL_REGISTER_USAGE.  */
diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt
index 68018ade73f..8be2b0bcb4f 100644
--- a/gcc/config/loongarch/loongarch.opt
+++ b/gcc/config/loongarch/loongarch.opt
@@ -24,22 +24,12 @@ 
 ; <http://www.gnu.org/licenses/>.
 ;
 
-; Variables (macros) that should be exported by loongarch.opt:
-;   la_opt_switches,
-;   la_opt_abi_base, la_opt_abi_ext,
-;   la_opt_cpu_arch, la_opt_cpu_tune,
-;   la_opt_fpu,
-;   la_cmodel.
-
 HeaderInclude
 config/loongarch/loongarch-opts.h
 
 HeaderInclude
 config/loongarch/loongarch-str.h
 
-Variable
-HOST_WIDE_INT la_opt_switches = 0
-
 ; ISA related options
 ;; Base ISA
 Enum
@@ -49,14 +39,13 @@  Basic ISAs of LoongArch:
 EnumValue
 Enum(isa_base) String(la64) Value(ISA_BASE_LA64V100)
 
-
 ;; ISA extensions / adjustments
 Enum
 Name(isa_ext_fpu) Type(int)
 FPU types of LoongArch:
 
 EnumValue
-Enum(isa_ext_fpu) String(none) Value(ISA_EXT_NOFPU)
+Enum(isa_ext_fpu) String(none) Value(ISA_EXT_NONE)
 
 EnumValue
 Enum(isa_ext_fpu) String(32) Value(ISA_EXT_FPU32)
@@ -65,24 +54,48 @@  EnumValue
 Enum(isa_ext_fpu) String(64) Value(ISA_EXT_FPU64)
 
 mfpu=
-Target RejectNegative Joined ToLower Enum(isa_ext_fpu) Var(la_opt_fpu) Init(M_OPTION_NOT_SEEN)
+Target RejectNegative Joined ToLower Enum(isa_ext_fpu) Var(la_opt_fpu) Init(M_OPT_UNSET)
 -mfpu=FPU	Generate code for the given FPU.
 
 mfpu=0
 Target RejectNegative Alias(mfpu=,none)
 
 msoft-float
-Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_SOFTF) Negative(msingle-float)
+Target Driver Defer Var(la_deferred_options) RejectNegative Negative(msingle-float)
 Prevent the use of all hardware floating-point instructions.
 
 msingle-float
-Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_F32) Negative(mdouble-float)
+Target Driver Defer Var(la_deferred_options) RejectNegative Negative(mdouble-float)
 Restrict the use of hardware floating-point instructions to 32-bit operations.
 
 mdouble-float
-Target Driver RejectNegative Var(la_opt_switches) Mask(FORCE_F64) Negative(msoft-float)
+Target Driver Defer Var(la_deferred_options) RejectNegative Negative(msoft-float)
 Allow hardware floating-point instructions to cover both 32-bit and 64-bit operations.
 
+Enum
+Name(isa_ext_simd) Type(int)
+SIMD extension levels of LoongArch:
+
+EnumValue
+Enum(isa_ext_simd) String(none) Value(ISA_EXT_NONE)
+
+EnumValue
+Enum(isa_ext_simd) String(lsx) Value(ISA_EXT_SIMD_LSX)
+
+EnumValue
+Enum(isa_ext_simd) String(lasx) Value(ISA_EXT_SIMD_LASX)
+
+msimd=
+Target RejectNegative Joined ToLower Enum(isa_ext_simd) Var(la_opt_simd) Init(M_OPT_UNSET)
+-msimd=SIMD	Generate code for the given SIMD extension.
+
+mlsx
+Target Driver Defer Var(la_deferred_options)
+Enable LoongArch SIMD Extension (LSX, 128-bit).
+
+mlasx
+Target Driver Defer Var(la_deferred_options)
+Enable LoongArch Advanced SIMD Extension (LASX, 256-bit).
 
 ;; Base target models (implies ISA & tune parameters)
 Enum
@@ -92,6 +105,9 @@  LoongArch CPU types:
 EnumValue
 Enum(cpu_type) String(native) Value(CPU_NATIVE)
 
+EnumValue
+Enum(cpu_type) String(abi-default) Value(CPU_ABI_DEFAULT)
+
 EnumValue
 Enum(cpu_type) String(loongarch64) Value(CPU_LOONGARCH64)
 
@@ -99,11 +115,11 @@  EnumValue
 Enum(cpu_type) String(la464) Value(CPU_LA464)
 
 march=
-Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_arch) Init(M_OPTION_NOT_SEEN)
+Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_arch) Init(M_OPT_UNSET)
 -march=PROCESSOR	Generate code for the given PROCESSOR ISA.
 
 mtune=
-Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_tune) Init(M_OPTION_NOT_SEEN)
+Target RejectNegative Joined Enum(cpu_type) Var(la_opt_cpu_tune) Init(M_OPT_UNSET)
 -mtune=PROCESSOR	Generate optimized code for PROCESSOR.
 
 
@@ -125,13 +141,13 @@  EnumValue
 Enum(abi_base) String(lp64s) Value(ABI_BASE_LP64S)
 
 mabi=
-Target RejectNegative Joined ToLower Enum(abi_base) Var(la_opt_abi_base) Init(M_OPTION_NOT_SEEN)
+Target RejectNegative Joined ToLower Enum(abi_base) Var(la_opt_abi_base) Init(M_OPT_UNSET)
 -mabi=BASEABI	Generate code that conforms to the given BASEABI.
 
+
 ;; ABI Extension
 Variable
-int la_opt_abi_ext = M_OPTION_NOT_SEEN
-
+int la_opt_abi_ext = M_OPT_UNSET
 
 mbranch-cost=
 Target RejectNegative Joined UInteger Var(loongarch_branch_cost)
@@ -189,7 +205,7 @@  EnumValue
 Enum(cmodel) String(extreme) Value(CMODEL_EXTREME)
 
 mcmodel=
-Target RejectNegative Joined Enum(cmodel) Var(la_opt_cmodel) Init(CMODEL_NORMAL)
+Target RejectNegative Joined Enum(cmodel) Var(la_opt_cmodel) Init(M_OPT_UNSET)
 Specify the code model.
 
 mdirect-extern-access
diff --git a/gcc/config/loongarch/t-linux b/gcc/config/loongarch/t-linux
index e40da179203..75bb430c555 100644
--- a/gcc/config/loongarch/t-linux
+++ b/gcc/config/loongarch/t-linux
@@ -23,7 +23,30 @@  MULTILIB_DIRNAMES = base/lp64d base/lp64f base/lp64s
 # The GCC driver always gets all abi-related options on the command line.
 # (see loongarch-driver.c:driver_get_normalized_m_opts)
 comma=,
-MULTILIB_REQUIRED = $(subst $(comma), ,$(TM_MULTILIB_CONFIG))
+MULTILIB_REQUIRED = $(foreach mlib,$(subst $(comma), ,$(TM_MULTILIB_CONFIG)),\
+		    $(firstword $(subst /, ,$(mlib))))
+
+SPECS = specs.install
+
+# temporary self_spec when building libraries (e.g. libgcc)
+gen_mlib_spec = $(if $(word 2,$1),\
+		%{$(firstword $1):$(patsubst %,-%,$(wordlist 2,$(words $1),$1))})
+
+# clean up the result of DRIVER_SELF_SPEC to avoid conflict
+lib_build_self_spec  = %<march=* %<mtune=* %<mcmodel=* %<mfpu=* %<msimd=*
+
+# append user-specified build options from --with-multilib-list
+lib_build_self_spec += $(foreach mlib,$(subst $(comma), ,$(TM_MULTILIB_CONFIG)),\
+			$(call gen_mlib_spec,$(subst /, ,$(mlib))))
+
+specs: specs.install
+	sed '/^*self_spec:$$/{ n;s/^$$/$(lib_build_self_spec)/g; }' $< > $@
+
+# Remove lib_build_self_specs before regression tests.
+.PHONY: remove-lib-specs
+check check-host check-target $(CHECK_TARGETS) $(lang_checks): remove-lib-specs
+remove-lib-specs:
+	-mv -f specs.install specs 2>/dev/null
 
 # Multiarch
 ifneq ($(call if_multiarch,yes),yes)
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index e099cd0b568..73b7ec8ee5c 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -1326,12 +1326,31 @@  profile.  The union of these options is considered when specifying both
 @end multitable
 
 @item loongarch*-*-*
-@var{list} is a comma-separated list of the following ABI identifiers:
-@code{lp64d[/base]} @code{lp64f[/base]} @code{lp64d[/base]}, where the
-@code{/base} suffix may be omitted, to enable their respective run-time
-libraries.  If @var{list} is empty or @code{default},
-or if @option{--with-multilib-list} is not specified, then the default ABI
-as specified by @option{--with-abi} or implied by @option{--target} is selected.
+@var{list} is a comma-separated list, with each of the element starting with
+the following ABI identifiers: @code{lp64d[/base]} @code{lp64f[/base]}
+@code{lp64d[/base]} (the @code{/base} suffix may be omitted)
+to enable their respective run-time libraries.
+
+A suffix @code{[/ARCH][/OPTION]...]} may follow immediately after the ABI
+identifier to customize the compiler options for building the given set of libraries.
+@code{ARCH} denotes the architecture name recognized by the @code{-march=ARCH}
+compiler option, which acts as a basic target ISA configuration that can be adjusted
+using the subsequent @code{OPTION} suffixes, where each @code{OPTION} is a compiler
+option itself.
+
+If none of such suffix is present, the configured value of
+@option{--with-multilib-default} can be used as a common default suffix
+for all library ABI variants.  Otherwise, the default build option
+@code{-march=abi-default} is applied when building the variants without
+a suffix.
+
+As a special case, @code{fixed} may be used in the position of @code{ARCH}, which
+means use the architecture configured with @option{--with-arch=ARCH}, or its default
+value (e.g. @code{loongarch64} for @code{loongarch64-*} targets).
+
+If @var{list} is empty or @code{default}, or if @option{--with-multilib-list}
+is not specified, then the default ABI as specified by @option{--with-abi} or
+implied by @option{--target}.
 
 @item riscv*-*-*
 @var{list} is a single ABI name.  The target architecture must be either
@@ -1382,6 +1401,18 @@  If @option{--with-multilib-list} is not given, then only 32-bit and
 64-bit run-time libraries will be enabled.
 @end table
 
+@item --with-multilib-default
+On LoongArch targets, set the default build options for enabled multilibs
+without build options appended to their corresponding @option{--with-multilib-list}
+items.  The format of this value is @code{[/ARCH][/OPTION]...}, where @code{ARCH}
+is an architecture name recognized by @code{-march=ARCH} compiler option,
+and @code{OPTION} can be filled with a compiler option itself.
+
+Multiple @code{OPTION}s may appear consecutively while @code{ARCH} may only
+appear in the beginning or be omitted (which means @code{-march=abi-default}
+is applied when building the libraries).
+
+
 @item --with-multilib-generator=@var{config}
 Specify what multilibs to build.  @var{config} is a semicolon separated list of
 values, possibly consisting of a single value.  Currently only implemented
@@ -1531,6 +1562,7 @@  x86-64, PowerPC, and SPARC@.
 @itemx --with-abi=@var{abi}
 @itemx --with-fpu=@var{type}
 @itemx --with-float=@var{type}
+@itemx --with-simd=@var{type}
 These configure options provide default values for the @option{-mschedule=},
 @option{-march=}, @option{-mtune=}, @option{-mabi=}, and @option{-mfpu=}
 options and for @option{-mhard-float} or @option{-msoft-float}.  As with
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 10f3466052f..259965a2a09 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1028,7 +1028,8 @@  Objective-C and Objective-C++ Dialects}.
 
 @emph{LoongArch Options}
 @gccoptlist{-march=@var{cpu-type}  -mtune=@var{cpu-type} -mabi=@var{base-abi-type}
--mfpu=@var{fpu-type} -msoft-float -msingle-float -mdouble-float
+-mfpu=@var{fpu-type} -msimd=@var{simd-type}
+-msoft-float -msingle-float -mdouble-float -mlsx -mno-lsx -mlasx -mno-lasx
 -mbranch-cost=@var{n}  -mcheck-zero-division -mno-check-zero-division
 -mcond-move-int  -mno-cond-move-int
 -mcond-move-float  -mno-cond-move-float
@@ -25970,6 +25971,20 @@  operations.
 Prevent the use of hardware floating-point instructions.
 @end table
 
+@opindex msimd
+@item -msimd=@var{simd-type}
+Generate code for the specified SIMD extension(s) when doing vectorization,
+which can be one of:
+@table @samp
+@item lasx
+256-bit LoongArch Advanced SIMD Extension (LASX) and 128-bit LoongArch
+SIMD Extension (LSX).
+@item lsx
+128-bit LoongArch SIMD Extension (LSX).
+@item none
+Disable vectorization.
+@end table
+
 @opindex msoft-float
 @item -msoft-float
 Force @option{-mfpu=none} and prevents the use of floating-point
@@ -25988,6 +26003,18 @@  Force @option{-mfpu=64} and allow the use of 32/64-bit floating-point
 registers for parameter passing.  This option may change the target
 ABI.
 
+@opindex mlasx
+@item -mlasx
+@itemx -mno-lasx
+Use (do not use) LoongArch Advanced SIMD Extension (LASX) for vectorization.
+Enabling LASX with @option{mlasx} automatically enables LSX.
+
+@opindex mlsx
+@item -mlsx
+@itemx -mno-lsx
+Use (do not use) LoongArch SIMD Extension for vectorization.
+Disabling LSX with @option{mlsx} automatically disables LASX.
+
 @opindex mbranch-cost
 @item -mbranch-cost=@var{n}
 Set the cost of branches to roughly @var{n} instructions.