diff mbox series

[v2,1/4] LoongArch: improved target configuration interface

Message ID 20230830015808.19870-2-yangyujie@loongson.cn
State New
Headers show
Series LoongArch: target configuration interface update | expand

Commit Message

Yang Yujie Aug. 30, 2023, 1:58 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.

* Make --with-abi obsolete, since it might cause different default ABI
  under the same target triplet, which is undesirable.  The default ABI
  is now purely decided by the target triplet.

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

* 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.

ChangeLog:

	* config-ml.in: Do not build the multilib library variant
	that is duplicate with the toplevel one.

gcc/ChangeLog:

	* config.gcc: Make --with-abi= obsolete, decide the default ABI
	with target triplet.  Allow specifying multilib library build
	options with --with-multilib-list and --with-multilib-default.
	* config/loongarch/t-linux: Likewise.
	* config/loongarch/genopts/loongarch-strings: Likewise.
	* config/loongarch/loongarch-str.h: Likewise.
	* doc/install.texi: Likewise.
	* config/loongarch/genopts/loongarch.opt.in: Introduce
	-m[no-]l[a]sx options.  Only process -m*-float and
	-m[no-]l[a]sx in the GCC driver.
	* config/loongarch/loongarch.opt: Likewise.
	* config/loongarch/la464.md: Likewise.
	* config/loongarch/loongarch-c.cc: Likewise.
	* config/loongarch/loongarch-cpu.cc: Likewise.
	* config/loongarch/loongarch-cpu.h: Likewise.
	* config/loongarch/loongarch-def.c: Likewise.
	* config/loongarch/loongarch-def.h: Likewise.
	* config/loongarch/loongarch-driver.cc: Likewise.
	* config/loongarch/loongarch-driver.h: Likewise.
	* config/loongarch/loongarch-opts.cc: Likewise.
	* config/loongarch/loongarch-opts.h: Likewise.
	* config/loongarch/loongarch.cc: Likewise.
	* doc/invoke.texi: Likewise.
---
 config-ml.in                                  |  10 +
 gcc/config.gcc                                | 379 ++++++++++--------
 .../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           |  19 +-
 gcc/config/loongarch/loongarch-cpu.cc         | 263 +++++++-----
 gcc/config/loongarch/loongarch-cpu.h          |   3 +-
 gcc/config/loongarch/loongarch-def.c          |  67 ++--
 gcc/config/loongarch/loongarch-def.h          |  57 +--
 gcc/config/loongarch/loongarch-driver.cc      | 208 +++++-----
 gcc/config/loongarch/loongarch-driver.h       |  40 +-
 gcc/config/loongarch/loongarch-opts.cc        | 372 ++++++++++++-----
 gcc/config/loongarch/loongarch-opts.h         |  59 +--
 gcc/config/loongarch/loongarch-str.h          |   7 +-
 gcc/config/loongarch/loongarch.cc             |  87 ++--
 gcc/config/loongarch/loongarch.opt            |  60 ++-
 gcc/config/loongarch/t-linux                  |  32 +-
 gcc/doc/install.texi                          |  46 ++-
 gcc/doc/invoke.texi                           |  32 +-
 20 files changed, 1164 insertions(+), 679 deletions(-)

Comments

Joseph Myers Aug. 30, 2023, 9:36 p.m. UTC | #1
On Wed, 30 Aug 2023, Yang Yujie wrote:

> +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.

Since ARCH and OPTION are not literal strings of program source code, you 
should actually be using @var{arch} and @var{option} for them (and @dots{} 
instead of ..., since the ... isn't literal source code either).

This patch series also adds a new configure option --with-strict-align-lib 
that needs documenting in the corresponding patch.
Yang Yujie Aug. 31, 2023, 3:14 a.m. UTC | #2
On Wed, Aug 30, 2023 at 09:36:22PM +0000, Joseph Myers wrote:
> On Wed, 30 Aug 2023, Yang Yujie wrote:
> 
> > +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.
> 
> Since ARCH and OPTION are not literal strings of program source code, you 
> should actually be using @var{arch} and @var{option} for them (and @dots{} 
> instead of ..., since the ... isn't literal source code either).
> 
> This patch series also adds a new configure option --with-strict-align-lib 
> that needs documenting in the corresponding patch.
> 
> -- 
> Joseph S. Myers
> joseph@codesourcery.com

Thanks for the review.  Does the following fix look good?
If so, I will include these in the patchset.

Yujie

---
 gcc/doc/install.texi | 44 +++++++++++++++++++++++---------------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index 05a626280b7..3e589080f4e 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -1236,7 +1236,7 @@ sysv, aix.
 @itemx --without-multilib-list
 Specify what multilibs to build.  @var{list} is a comma separated list of
 values, possibly consisting of a single value.  Currently only implemented
-for aarch64*-*-*, arm*-*-*, loongarch64-*-*, riscv*-*-*, sh*-*-* and
+for aarch64*-*-*, arm*-*-*, loongarch*-*-*, riscv*-*-*, sh*-*-* and
 x86-64-*-linux*.  The accepted values and meaning for each target is given
 below.
 
@@ -1331,27 +1331,27 @@ 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.
+A suffix @code{[/@var{arch}][/@var{option}/@dots{}]} may follow immediately
+after the ABI identifier to customize the compiler options for building the
+given set of libraries.  @var{arch} denotes the architecture name recognized
+by the @option{-march=@var{arch}} compiler option, which acts as a basic target
+ISA configuration that can be adjusted using the subsequent @var{option}
+suffixes, where each @var{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.
+If no such suffix is present for a given multilib variant, the
+configured value of @code{--with-multilib-default} is appended as a default
+suffix.  If @code{--with-multilib-default} is not given, 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).
+As a special case, @code{fixed} may be used in the position of @var{arch},
+which means using the architecture configured with
+@code{--with-arch=@var{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}.
+If @var{list} is empty or @code{default}, or if @code{--with-multilib-list}
+is not specified, then only the default variant of the libraries are built,
+where the default ABI is implied by the configured target triplet.
 
 @item riscv*-*-*
 @var{list} is a single ABI name.  The target architecture must be either
@@ -1414,6 +1414,9 @@ 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-strict-align-lib
+On LoongArch targets, build all enabled multilibs with @code{-mstrict-align}
+(Not enabled by default).
 
 @item --with-multilib-generator=@var{config}
 Specify what multilibs to build.  @var{config} is a semicolon separated list of
@@ -4539,8 +4542,7 @@ Uses @code{lp64s/base} ABI by default.
 @end table
 
 @item loongarch64-linux-gnu
-Same as @code{loongarch64-linux-gnuf64}, but may be used with
-@option{--with-abi=*} to configure the default ABI type.
+Same as @code{loongarch64-linux-gnuf64} for legacy support.
 @end table
 
 More information about LoongArch can be found at
Yang Yujie Aug. 31, 2023, 7:36 a.m. UTC | #3
On Thu, Aug 31, 2023 at 11:14:13AM +0800, Yujie Yang wrote:
> On Wed, Aug 30, 2023 at 09:36:22PM +0000, Joseph Myers wrote:
> > On Wed, 30 Aug 2023, Yang Yujie wrote:
> > 
> > > +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.
> > 
> > Since ARCH and OPTION are not literal strings of program source code, you 
> > should actually be using @var{arch} and @var{option} for them (and @dots{} 
> > instead of ..., since the ... isn't literal source code either).
> > 
> > This patch series also adds a new configure option --with-strict-align-lib 
> > that needs documenting in the corresponding patch.
> > 
> > -- 
> > Joseph S. Myers
> > joseph@codesourcery.com
> 
> Thanks for the review.  Does the following fix look good?
> If so, I will include these in the patchset.

Found something else to fix, I will post them in the next version
of the patchset.
Joseph Myers Aug. 31, 2023, 5:56 p.m. UTC | #4
On Thu, 31 Aug 2023, Yujie Yang wrote:

> -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.
> +If no such suffix is present for a given multilib variant, the
> +configured value of @code{--with-multilib-default} is appended as a default
> +suffix.  If @code{--with-multilib-default} is not given, the default build
> +option @code{-march=abi-default} is applied when building the variants
> +without a suffix.

@option is appropriate for --with-multilib-default and other configure 
options; it shouldn't be changed to @code.
Yang Yujie Sept. 1, 2023, 1:19 a.m. UTC | #5
On Thu, Aug 31, 2023 at 05:56:26PM +0000, Joseph Myers wrote:
> On Thu, 31 Aug 2023, Yujie Yang wrote:
> 
> > -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.
> > +If no such suffix is present for a given multilib variant, the
> > +configured value of @code{--with-multilib-default} is appended as a default
> > +suffix.  If @code{--with-multilib-default} is not given, the default build
> > +option @code{-march=abi-default} is applied when building the variants
> > +without a suffix.
> 
> @option is appropriate for --with-multilib-default and other configure 
> options; it shouldn't be changed to @code.
> 

Thanks! Fixed in the v3 patchset.
diff mbox series

Patch

diff --git a/config-ml.in b/config-ml.in
index 68854a4f16c..ad0db078171 100644
--- a/config-ml.in
+++ b/config-ml.in
@@ -301,6 +301,16 @@  arm-*-*)
 	  done
 	fi
 	;;
+loongarch*-*)
+	old_multidirs="${multidirs}"
+	multidirs=""
+	for x in ${old_multidirs}; do
+	case "$x" in
+	`${CC-gcc} --print-multi-directory`) : ;;
+	*) multidirs="${multidirs} ${x}" ;;
+	esac
+	done
+	;;
 m68*-*-*)
 	if [ x$enable_softfloat = xno ]
 	then
diff --git a/gcc/config.gcc b/gcc/config.gcc
index 415e0e1ebc5..4fae672a3b7 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -4891,43 +4891,46 @@  case "${target}" in
 		esac
 		;;
 
-	loongarch*-*-*)
-		supported_defaults="abi arch tune fpu"
+	loongarch*-*)
+		supported_defaults="abi arch tune fpu simd multilib-default"
 
 		# Local variables
 		unset \
-			abi_pattern      abi_default    \
-			abiext_pattern   abiext_default \
-			arch_pattern     arch_default   \
-			fpu_pattern      fpu_default    \
-			tune_pattern     tune_default   \
-			triplet_os       triplet_abi
+			abi_base	abi_ext \
+			arch_pattern	arch_default \
+			fpu_pattern	fpu_default \
+			triplet_os	triplet_abi \
+			strict_align_opt
+
+		# --with-abi is now obsolete, emit a warning if given.
+		case ${with_abi} in
+		"") ;;
+		*)
+			echo "warning: --with-abi= is now obsolete," \
+			"the default ABI is derived from your target" \
+			"triplet ${target}" 1>&2
+			;;
+		esac
 
 		# Infer ABI from the triplet.
 		case ${target} in
-		loongarch64-*-*-*f64)
-			abi_pattern="lp64d"
-			;;
-		loongarch64-*-*-*f32)
-			abi_pattern="lp64f"
-			;;
-		loongarch64-*-*-*sf)
-			abi_pattern="lp64s"
-			;;
-		loongarch64-*-*-*)
-			abi_pattern="lp64[dfs]"
-			abi_default="lp64d"
-			;;
+		loongarch64-*f64) abi_base="lp64d"; abi_ext="base" ;;
+		loongarch64-*f32) abi_base="lp64f"; abi_ext="base" ;;
+		loongarch64-*sf)  abi_base="lp64s"; abi_ext="base" ;;
+		loongarch64-*)    abi_base="lp64d"; abi_ext="base" ;;
 		*)
 			echo "Unsupported target ${target}." 1>&2
 			exit 1
 			;;
 		esac
 
-		abiext_pattern="*"
-		abiext_default="base"
-
 		# Get the canonical triplet (multiarch specifier).
+		case ${abi_base},${abi_ext} in
+		lp64d,base) triplet_abi="";;
+		lp64f,base) triplet_abi="f32";;
+		lp64s,base) triplet_abi="sf";;
+		esac
+
 		case ${target} in
 		  *-linux-gnu*)  triplet_os="linux-gnu";;
 		  *-linux-musl*) triplet_os="linux-musl";;
@@ -4936,42 +4939,24 @@  case "${target}" in
 			  exit 1
 			  ;;
 		esac
+		la_canonical_triplet="loongarch64-${triplet_os}${triplet_abi}"
+
 
 		# 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
 			;;
 		esac
 
-		case ${with_abi} in
-		"" | lp64d | lp64f | lp64s) ;; # OK, append here.
-		*)
-			echo "Unsupported ABI given in --with-abi=$with_abi" 1>&2
-			exit 1
-			;;
-		esac
-
-		case ${with_abiext} in
-		"" | base) ;; # OK, append here.
-		*)
-			echo "Unsupported ABI extention type $with_abiext" 1>&2
-			exit 1
-			;;
-		esac
 
 		case ${with_fpu} in
 		"" | none | 32 | 64) ;; # OK, append here.
@@ -4985,73 +4970,41 @@  case "${target}" in
 			;;
 		esac
 
-
-		# Set default value for with_abi.
-		case ${with_abi} in
-		"")
-			if test x${abi_default} != x; then
-				with_abi=${abi_default}
-			else
-				with_abi=${abi_pattern}
-			fi
-			;;
-
-		*)
-			if echo "${with_abi}" | grep -E "^${abi_pattern}$" > /dev/null; then
-				: # OK
-			else
-				echo "Incompatible options:" \
-				"--with-abi=${with_abi} and --target=${target}." 1>&2
+		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
-			fi
-			;;
-		esac
-
-		case ${with_abi} in
-		  "lp64d") triplet_abi="";;
-		  "lp64f") triplet_abi="f32";;
-		  "lp64s") triplet_abi="sf";;
-		esac
-		la_canonical_triplet="loongarch64-${triplet_os}${triplet_abi}"
-
-		# Set default value for with_abiext (internal)
-		case ${with_abiext} in
-		"")
-			if test x${abiext_default} != x; then
-				with_abiext=${abiext_default}
-			else
-				with_abiext=${abiext_pattern}
-			fi
+				;;
+			esac
 			;;
 
 		*)
-			if echo "${with_abiext}" | grep -E "^${abiext_pattern}$" > /dev/null; then
-				: # OK
-			else
-				echo "The ABI extension type \"${with_abiext}\"" \
-				"is incompatible with --target=${target}." 1>&2
-				exit 1
-			fi
-
+			echo "Unknown SIMD extension in --with-simd=$with_simd" 1>&2
+			exit 1
 			;;
 		esac
 
 		# Infer ISA-related default options from the ABI: pass 1
-		case ${with_abi}/${with_abiext} in
+		case ${abi_base}/${abi_ext} 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
+			echo "Unsupported ABI type ${abi_base}/${abi_ext}." 1>&2
 			exit 1
 			;;
 		esac
 
 		# Infer ISA-related default options from the ABI: pass 2
-		case ${with_abi}/${with_abiext} in
+		case ${abi_base}/${abi_ext} in
 		lp64d/base)
 			fpu_pattern="64"
 			;;
@@ -5064,7 +5017,7 @@  case "${target}" in
 			fpu_default="none"
 			;;
 		*)
-			echo "Unsupported ABI type ${with_abi}/${with_abiext}." 1>&2
+			echo "Unsupported ABI type ${abi_base}/${abi_ext}." 1>&2
 			exit 1
 			;;
 		esac
@@ -5083,7 +5036,7 @@  case "${target}" in
 			if echo "${with_arch}" | grep -E "^${arch_pattern}$" > /dev/null; then
 				: # OK
 			else
-				echo "${with_abi}/${with_abiext} ABI cannot be implemented with" \
+				echo "${abi_base}/${abi_ext} ABI cannot be implemented with" \
 				"--with-arch=${with_arch}." 1>&2
 				exit 1
 			fi
@@ -5104,7 +5057,7 @@  case "${target}" in
 			if echo "${with_fpu}" | grep -E "^${fpu_pattern}$" > /dev/null; then
 				: # OK
 			else
-				echo "${with_abi}/${with_abiext} ABI cannot be implemented with" \
+				echo "${abi_base}/${abi_ext} ABI cannot be implemented with" \
 				"--with-fpu=${with_fpu}." 1>&2
 				exit 1
 			fi
@@ -5112,32 +5065,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,13 +5089,53 @@  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 \
 		   || test x"${with_multilib_list}" = xdefault \
 		   || test x"${enable_multilib}" != xyes; then
 
-			with_multilib_list="${with_abi}/${with_abiext}"
+			with_multilib_list="${abi_base}/${abi_ext}"
 		fi
 
 		# Check if the configured default ABI combination is included in
@@ -5171,25 +5151,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,57 +5174,111 @@  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
 
-			if test x${elem_abi_base} = x${with_abi} \
-			&& test x${elem_abi_ext} = x${with_abiext}; then
+			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${abi_base} \
+			&& test x${elem_abi_ext} = x${abi_ext}; 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
-			if test x${with_abiext} = xbase; then
-				with_abiext=""
+			if test x${abi_ext} = xbase; then
+				abi_ext=""
 			else
-				with_abiext="/${with_abiext}"
+				abi_ext="/${abi_ext}"
 			fi
 
-			echo "Default ABI combination (${with_abi}${with_abiext})" \
+			echo "Default ABI combination (${abi_base}${abi_ext})" \
 			"not found in --with-multilib-list." 1>&2
 			exit 1
 		fi
@@ -5739,34 +5769,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 ${abi_base} | tr a-z- A-Z_)"
 
-		case ${with_abiext} in
+		# ABI Extension
+		case ${abi_ext} 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..adecaec3eda 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 @@  (define_cpu_unit "la464_falu2" "la464_a_falu")
 ;; 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..7e3b57ff9b1 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)
@@ -99,6 +99,21 @@  loongarch_cpu_cpp_builtins (cpp_reader *pfile)
   else
     builtin_define ("__loongarch_frlen=0");
 
+  if (ISA_HAS_LSX)
+    {
+      builtin_define ("__loongarch_simd");
+      builtin_define ("__loongarch_sx");
+
+      if (!ISA_HAS_LASX)
+	builtin_define ("__loongarch_simd_width=128");
+    }
+
+  if (ISA_HAS_LASX)
+    {
+      builtin_define ("__loongarch_asx");
+      builtin_define ("__loongarch_simd_width=256");
+    }
+
   /* Native Data Sizes.  */
   builtin_define_with_int_value ("_LOONGARCH_SZINT", INT_TYPE_SIZE);
   builtin_define_with_int_value ("_LOONGARCH_SZLONG", LONG_TYPE_SIZE);
diff --git a/gcc/config/loongarch/loongarch-cpu.cc b/gcc/config/loongarch/loongarch-cpu.cc
index 34905f3de30..7a2866f60f9 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,191 @@  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:
+	    tmp = 0;
+	    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]);
+	    break;
+
+	  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..e744ee01d6d 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,29 @@  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..bafd27e67a5 100644
--- a/gcc/config/loongarch/loongarch-driver.cc
+++ b/gcc/config/loongarch/loongarch-driver.cc
@@ -26,122 +26,137 @@  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 ATTRIBUTE_UNUSED,
+		    const char **argv ATTRIBUTE_UNUSED)
+{
+  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);
 
   /* Output normalized option strings.  */
   obstack_blank (&opts_obstack, 0);
@@ -160,11 +175,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 +194,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..e5921189a06 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,10 @@  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 +112,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 +171,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 +239,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 +268,130 @@  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));
+
+  int simd_base = (constrained.arch ? t.isa.simd :
+    (with_default_simd ? DEFAULT_ISA_EXT_SIMD : t.isa.simd));
+
+  t.isa.simd = constrained.simd ? target->isa.simd : simd_base;
+
+  /* If fallback_lsx is set, using -mno-lasx would cause
+     a fall-back to -msimd=lsx instead of -msimd=none.  */
+
+  int fallback_lsx = 0;
+  if (t.isa.simd == ISA_EXT_SIMD_LSX || simd_base != ISA_EXT_NONE)
+    fallback_lsx = 1;
+
+  /* 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)
+	      fallback_lsx = 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 -mlsx or simd=lsx (msimd or march-default) was not
+	       involved, do not fall back to simd=lsx.  */
+	    if (flags->sx[i] < 0 && t.isa.simd == ISA_EXT_SIMD_LASX)
+	      t.isa.simd = fallback_lsx ? ISA_EXT_SIMD_LSX : ISA_EXT_NONE;
+	    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 +414,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 +461,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 +502,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 +517,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 +561,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 +601,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 +625,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 +669,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 +678,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 +702,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 +748,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..7a58dabdea8 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
 
 
@@ -50,11 +65,11 @@  loongarch_config_target (struct loongarch_target *target,
 #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..9997596d722 100644
--- a/gcc/config/loongarch/t-linux
+++ b/gcc/config/loongarch/t-linux
@@ -23,7 +23,37 @@  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; }' $< > $@
+
+# Do some preparation before regression tests:
+# remove lib-build-specs / make symlinks for the toplevel multilib variant
+
+LA_DEFAULT_MULTISUBDIR = $(shell $(GCC_FOR_TARGET) --print-multi-dir)
+.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
+	-mv $(LA_DEFAULT_MULTISUBDIR)/* ./
+	-mkdir -p ../$(target_noncanonical)/`dirname $(LA_DEFAULT_MULTISUBDIR)`
+	-$(LN_S) .. ../$(target_noncanonical)/$(LA_DEFAULT_MULTISUBDIR)
 
 # Multiarch
 ifneq ($(call if_multiarch,yes),yes)
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index e099cd0b568..05a626280b7 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -1326,12 +1326,32 @@  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 +1402,19 @@  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 +1564,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..2f01baff3e7 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,21 @@  operations.
 Prevent the use of hardware floating-point instructions.
 @end table
 
+@opindex msimd
+@item -msimd=@var{simd-type}
+Enable generation of LoongArch SIMD instructions for vectorization
+and via builtin functions.  The value can be one of:
+@table @samp
+@item lasx
+Enable generating instructions from the 256-bit LoongArch Advanced
+SIMD Extension (LASX) and the 128-bit LoongArch SIMD Extension (LSX).
+@item lsx
+Enable generating instructions from the 128-bit LoongArch SIMD
+Extension (LSX).
+@item none
+No LoongArch SIMD instruction may be generated.
+@end table
+
 @opindex msoft-float
 @item -msoft-float
 Force @option{-mfpu=none} and prevents the use of floating-point
@@ -25988,6 +26004,19 @@  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 ml[a]sx
+@item -mlasx
+@itemx -mno-lasx
+@item -mlsx
+@itemx -mno-lsx
+Incrementally adjust the scope of the SIMD extensions (none / LSX / LASX)
+that can be used by the compiler for code generation.  Enabling LASX with
+@option{mlasx} automatically enables LSX, and diabling LSX with @option{mno-lsx}
+automatically disables LASX.  These driver-only options act upon the final
+@option{msimd} configuration state and make incremental chagnes in the order
+they appear on the GCC driver's command line, deriving the final / canonicalized
+@option{msimd} option that is passed to the compiler proper.
+
 @opindex mbranch-cost
 @item -mbranch-cost=@var{n}
 Set the cost of branches to roughly @var{n} instructions.
@@ -26088,6 +26117,7 @@  kernels, executables linked with @option{-static} or @option{-static-pie}.
 @option{-fpic}.
 @end table
 
+
 @node M32C Options
 @subsection M32C Options
 @cindex M32C options