diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index f560ba023bf..9dc84cc8f0c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -748,6 +748,7 @@ ALL_64_TARGET_OBS = \
 	arch/aarch64-insn.o \
 	arch/aarch64-mte-linux.o \
 	arch/aarch64-scalable-linux.o \
+	arch/amd64-linux-tdesc.o \
 	arch/amd64.o \
 	arch/riscv.o \
 	bpf-tdep.o \
@@ -788,9 +789,11 @@ ALL_TARGET_OBS = \
 	arch/arm.o \
 	arch/arm-get-next-pcs.o \
 	arch/arm-linux.o \
+	arch/i386-linux-tdesc.o \
 	arch/i386.o \
 	arch/loongarch.o \
 	arch/ppc-linux-common.o \
+	arch/x86-linux-tdesc-features.o \
 	arm-bsd-tdep.o \
 	arm-fbsd-tdep.o \
 	arm-linux-tdep.o \
@@ -1559,6 +1562,8 @@ HFILES_NO_SRCDIR = \
 	arch/ppc-linux-common.h \
 	arch/ppc-linux-tdesc.h \
 	arch/riscv.h \
+	arch/x86-linux-tdesc-features.h \
+	arch/x86-linux-tdesc.h \
 	cli/cli-cmds.h \
 	cli/cli-decode.h \
 	cli/cli-script.h \
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index bcb9868e79e..c707745cd9a 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1577,37 +1577,6 @@ amd64_linux_record_signal (struct gdbarch *gdbarch,
   return 0;
 }
 
-const target_desc *
-amd64_linux_read_description (uint64_t xcr0_features_bit, bool is_x32)
-{
-  static target_desc *amd64_linux_tdescs \
-    [2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  static target_desc *x32_linux_tdescs \
-    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/] = {};
-
-  target_desc **tdesc;
-
-  if (is_x32)
-    {
-      tdesc = &x32_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0 ]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-  else
-    {
-      tdesc = &amd64_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_MPX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-
-  if (*tdesc == NULL)
-    *tdesc = amd64_create_target_description (xcr0_features_bit, is_x32,
-					      true, true);
-
-  return *tdesc;
-}
-
 /* Get Linux/x86 target description from core dump.  */
 
 static const struct target_desc *
diff --git a/gdb/arch/amd64-linux-tdesc.c b/gdb/arch/amd64-linux-tdesc.c
new file mode 100644
index 00000000000..63b5ddfcece
--- /dev/null
+++ b/gdb/arch/amd64-linux-tdesc.c
@@ -0,0 +1,61 @@
+/* Target description related code for GNU/Linux x86-64.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc.h"
+#include "arch/amd64-linux-tdesc.h"
+#include "arch/amd64.h"
+#include "arch/x86-linux-tdesc-features.h"
+
+
+/* See arch/amd64-linux-tdesc.h.  */
+
+const struct target_desc *
+amd64_linux_read_description (uint64_t xcr0, bool is_x32)
+{
+  /* The type used for the amd64 and x32 target description caches.  */
+  using tdesc_cache_type = std::unordered_map<uint64_t, const target_desc_up>;
+
+  /* Caches for the previously seen amd64 and x32 target descriptions,
+     indexed by the xcr0 value that created the target description.  These
+     need to be static within this function to ensure they are initialised
+     before first use.  */
+  static tdesc_cache_type amd64_tdesc_cache, x32_tdesc_cache;
+
+  tdesc_cache_type &tdesc_cache = is_x32 ? x32_tdesc_cache : amd64_tdesc_cache;
+
+  xcr0 &= is_x32
+    ? x86_linux_x32_tdesc_feature_mask ()
+    : x86_linux_amd64_tdesc_feature_mask ();
+
+  const auto it = tdesc_cache.find (xcr0);
+  if (it != tdesc_cache.end ())
+    return it->second.get ();
+
+  /* Create the previously unseen target description.  */
+  target_desc_up tdesc (amd64_create_target_description (xcr0, is_x32,
+							 true, true));
+  x86_linux_post_init_tdesc (tdesc.get (), true);
+
+  /* Add to the cache, and return a pointer borrowed from the
+     target_desc_up.  This is safe as the cache (and the pointers contained
+     within it) are not deleted until GDB exits.  */
+  target_desc *ptr = tdesc.get ();
+  tdesc_cache.emplace (xcr0, std::move (tdesc));
+  return ptr;
+}
diff --git a/gdb/arch/i386-linux-tdesc.c b/gdb/arch/i386-linux-tdesc.c
new file mode 100644
index 00000000000..6d1f1793457
--- /dev/null
+++ b/gdb/arch/i386-linux-tdesc.c
@@ -0,0 +1,51 @@
+/* Target description related code for GNU/Linux i386.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc.h"
+#include "arch/i386-linux-tdesc.h"
+#include "arch/i386.h"
+#include "arch/x86-linux-tdesc-features.h"
+
+/* See arch/i386-linux-tdesc.h.  */
+
+const target_desc *
+i386_linux_read_description (uint64_t xcr0)
+{
+  /* Cache of previously seen i386 target descriptions, indexed by the xcr0
+     value that created the target description.  This needs to be static
+     within this function to ensure it is initialised before first use.  */
+  static std::unordered_map<uint64_t, const target_desc_up> i386_tdesc_cache;
+
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+
+  const auto it = i386_tdesc_cache.find (xcr0);
+  if (it != i386_tdesc_cache.end ())
+    return it->second.get ();
+
+  /* Create the previously unseen target description.  */
+  target_desc_up tdesc (i386_create_target_description (xcr0, true, false));
+  x86_linux_post_init_tdesc (tdesc.get (), false);
+
+  /* Add to the cache, and return a pointer borrowed from the
+     target_desc_up.  This is safe as the cache (and the pointers contained
+     within it) are not deleted until GDB exits.  */
+  target_desc *ptr = tdesc.get ();
+  i386_tdesc_cache.emplace (xcr0, std::move (tdesc));
+  return ptr;
+}
diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
new file mode 100644
index 00000000000..91abd9b1928
--- /dev/null
+++ b/gdb/arch/x86-linux-tdesc-features.c
@@ -0,0 +1,247 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc-features.h"
+
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
+
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  return x86_linux_amd64_tdesc_feature_mask_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  return x86_linux_x32_tdesc_feature_mask_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_i386_tdesc_feature_mask ()
+{
+  return x86_linux_i386_tdesc_feature_mask_1 ();
+}
+
+#ifdef GDBSERVER
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature)
+	  == x86_linux_all_tdesc_features[i].feature)
+	idx |= (1 << i);
+    }
+
+  return idx;
+}
+
+#endif /* GDBSERVER */
+
+#ifdef IN_PROCESS_AGENT
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_amd64_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_x32_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_i386_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
+    }
+
+  return xcr0;
+}
+
+#endif /* IN_PROCESS_AGENT */
diff --git a/gdbserver/linux-x86-tdesc.h b/gdb/arch/x86-linux-tdesc-features.h
similarity index 52%
rename from gdbserver/linux-x86-tdesc.h
rename to gdb/arch/x86-linux-tdesc-features.h
index 3d6e0e51833..0d3db587174 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdb/arch/x86-linux-tdesc-features.h
@@ -1,7 +1,6 @@
-/* Low level support for x86 (i386 and x86-64), shared between gdbserver
-   and IPA.
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
 
-   Copyright (C) 2016-2024 Free Software Foundation, Inc.
+   Copyright (C) 2024 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -18,33 +17,46 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#ifndef GDBSERVER_LINUX_X86_TDESC_H
-#define GDBSERVER_LINUX_X86_TDESC_H
+#ifndef ARCH_X86_LINUX_TDESC_FEATURES_H
+#define ARCH_X86_LINUX_TDESC_FEATURES_H
 
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+#include "gdbsupport/x86-xstate.h"
+#include "gdbsupport/gdb_assert.h"
 
-extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+/* Return a mask of X86_STATE_* feature flags.  The returned mask indicates
+   the set of features which are checked for when creating the target
+   description for each of amd64, x32, and i386.  */
 
-#ifdef IN_PROCESS_AGENT
+extern uint64_t x86_linux_amd64_tdesc_feature_mask ();
+extern uint64_t x86_linux_x32_tdesc_feature_mask ();
+extern uint64_t x86_linux_i386_tdesc_feature_mask ();
 
-/* Convert an index number (as returned from x86_linux_xcr0_to_tdesc_idx)
-   into an xcr0 value which can then be used to create a target
-   description.  */
+#ifdef GDBSERVER
 
-extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+/* Convert an xcr0 value into an integer.  The integer will be passed from
+   gdbserver to the in-process-agent where it will then be passed through
+   x86_linux_tdesc_idx_to_xcr0 to get back the original xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
 
-/* Within the in-process-agent we need to pre-initialise all of the target
-   descriptions, to do this we need to know how many target descriptions
-   there are for each different target type.  These functions return the
-   target description count for the relevant target.  */
+#endif /* GDBSERVER */
+
+#ifdef IN_PROCESS_AGENT
+
+/* Return the maximum possible number of target descriptions for each of
+   amd64, x32, and i386.  These are used by the in-process-agent to
+   generate every possible target description.  */
 
 extern int x86_linux_amd64_tdesc_count ();
 extern int x86_linux_x32_tdesc_count ();
 extern int x86_linux_i386_tdesc_count ();
 
+/* Convert an index number (as returned from x86_linux_xcr0_to_tdesc_idx)
+   into an xcr0 value which can then be used to create a target
+   description.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
 
 #endif /* IN_PROCESS_AGENT */
 
-#endif /* GDBSERVER_LINUX_X86_TDESC_H */
+#endif /* ARCH_X86_LINUX_TDESC_FEATURES_H */
diff --git a/gdb/arch/x86-linux-tdesc.h b/gdb/arch/x86-linux-tdesc.h
new file mode 100644
index 00000000000..152592fcf76
--- /dev/null
+++ b/gdb/arch/x86-linux-tdesc.h
@@ -0,0 +1,37 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_X86_LINUX_TDESC_H
+#define ARCH_X86_LINUX_TDESC_H
+
+struct target_desc;
+
+/* This function is called from amd64_linux_read_description and
+   i386_linux_read_description after a new target description has been
+   created, TDESC is the new target description, IS_64BIT will be true
+   when called from amd64_linux_read_description, otherwise IS_64BIT will
+   be false.  If the *_linux_read_description functions found a cached
+   target description then this function will not be called.
+
+   Both GDB and gdbserver have their own implementations of this
+   function.  */
+
+extern void x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit);
+
+#endif /* ARCH_X86_LINUX_TDESC_H */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 4bcc0696027..24e1824b01c 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -256,7 +256,8 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o \
 		i386-linux-nat.o x86-linux-nat.o nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o"
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
+		arch/i386-linux-tdesc.o arch/x86-linux-tdesc-features.o"
 		;;
 	    ia64)
 		# Host: Intel IA-64 running GNU/Linux
@@ -323,7 +324,8 @@ case ${gdb_host} in
 		nat/x86-xstate.o amd64-nat.o amd64-linux-nat.o x86-linux-nat.o \
 		nat/linux-btrace.o \
 		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
-		nat/amd64-linux-siginfo.o"
+		nat/amd64-linux-siginfo.o arch/x86-linux-tdesc-features.o \
+		arch/i386-linux-tdesc.o arch/amd64-linux-tdesc.o"
 		;;
 	    sparc)
 		# Host: GNU/Linux UltraSPARC
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 47a674201f9..8326c458eb1 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -320,10 +320,13 @@ i[34567]86-*-linux*)
 	gdb_target_obs="i386-linux-tdep.o \
 			glibc-tdep.o \
 			solib-svr4.o symfile-mem.o \
-			linux-tdep.o linux-record.o"
+			linux-tdep.o linux-record.o \
+			arch/i386-linux-tdesc.o \
+			arch/x86-linux-tdesc-features.o"
 	if test "x$enable_64_bit_bfd" = "xyes"; then
 	    # Target: GNU/Linux x86-64
-	    gdb_target_obs="amd64-linux-tdep.o ${gdb_target_obs}"
+	    gdb_target_obs="amd64-linux-tdep.o \
+			    arch/amd64-linux-tdesc.o ${gdb_target_obs}"
 	fi
 	;;
 i[34567]86-*-gnu*)
@@ -718,7 +721,9 @@ x86_64-*-linux*)
 	# Target: GNU/Linux x86-64
 	gdb_target_obs="amd64-linux-tdep.o ${i386_tobjs}  \
 			i386-linux-tdep.o glibc-tdep.o \
-			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o"
+			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o \
+			arch/i386-linux-tdesc.o arch/amd64-linux-tdesc.o \
+			arch/x86-linux-tdesc-features.o"
 	;;
 x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu)
 	# Target: FreeBSD/amd64
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index c796f87780b..d4d820b9f98 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -40,6 +40,7 @@
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
@@ -679,29 +680,12 @@ i386_linux_core_read_x86_xsave_layout (struct gdbarch *gdbarch,
 					  layout) != 0;
 }
 
-/* See i386-linux-tdep.h.  */
+/* See arch/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  if (xcr0 == 0)
-    return NULL;
-
-  static struct target_desc *i386_linux_tdescs \
-    [2/*X87*/][2/*SSE*/][2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  struct target_desc **tdesc;
-
-  tdesc = &i386_linux_tdescs[(xcr0 & X86_XSTATE_X87) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_MPX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0];
-
-  if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, true, false);
-
-  return *tdesc;
+  /* Nothing.  */
 }
 
 /* Get Linux/x86 target description from core dump.  */
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 7a2702d78bf..e17b5cf280c 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -110,12 +110,16 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
+			srv_tgtobj="${srv_tgtobj} arch/x86-linux-tdesc-features.o"
+			srv_tgtobj="${srv_tgtobj} arch/i386-linux-tdesc.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			srv_linux_btrace=yes
 			ipa_obj="linux-i386-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/i386-ipa.o"
+			ipa_obj="${ipa_obj} arch/x86-linux-tdesc-features-ipa.o"
+			ipa_obj="${ipa_obj} arch/i386-linux-tdesc-ipa.o"
 			;;
   i[34567]86-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o win32-low.o"
@@ -374,12 +378,17 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o"
+			srv_tgtobj="${srv_tgtobj} arch/x86-linux-tdesc-features.o"
+			srv_tgtobj="${srv_tgtobj} arch/amd64-linux-tdesc.o"
+			srv_tgtobj="${srv_tgtobj} arch/i386-linux-tdesc.o"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			srv_linux_btrace=yes
 			ipa_obj="linux-amd64-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/amd64-ipa.o"
+			ipa_obj="${ipa_obj} arch/x86-linux-tdesc-features-ipa.o"
+			ipa_obj="${ipa_obj} arch/amd64-linux-tdesc-ipa.o"
 			;;
   x86_64-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o"
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 0c80812cc6f..89857f208b8 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -20,9 +20,9 @@
 
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/amd64-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 /* fast tracepoints collect registers.  */
 
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index c1c3152fb04..8100c9f9840 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -20,9 +20,9 @@
 
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 /* GDB register numbers.  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 4f473730efb..63607cae390 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -33,6 +33,7 @@
 #endif
 
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 #include "gdb_proc_service.h"
 /* Don't include elf/common.h if linux/elf.h got included by
@@ -48,7 +49,6 @@
 #include "nat/linux-nat.h"
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
-#include "linux-x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 5e12526bf17..13c80762605 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -17,295 +17,19 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
+#include "arch/x86-linux-tdesc.h"
 #include "tdesc.h"
-#include "linux-x86-tdesc.h"
-#include "arch/i386.h"
-#include "gdbsupport/x86-xstate.h"
-#ifdef __x86_64__
-#include "arch/amd64.h"
-#include "arch/amd64-linux-tdesc.h"
-#endif
 #include "x86-tdesc.h"
-#include "arch/i386-linux-tdesc.h"
-
-/* A structure used to describe a single cpu feature that might, or might
-   not, be checked for when creating a target description for one of i386,
-   amd64, or x32.  */
-
-struct x86_tdesc_feature {
-  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
-  uint64_t feature;
-
-  /* Is this feature checked when creating an i386 target description.  */
-  bool is_i386;
-
-  /* Is this feature checked when creating an amd64 target description.  */
-  bool is_amd64;
-
-  /* Is this feature checked when creating an x32 target description.  */
-  bool is_x32;
-};
-
-/* A constant table that describes all of the cpu features that are
-   checked when building a target description for i386, amd64, or x32.  */
-
-static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
-  /* Feature,           i386,	amd64,	x32.  */
-  { X86_XSTATE_PKRU,	true,	true, 	true },
-  { X86_XSTATE_AVX512,	true,	true, 	true },
-  { X86_XSTATE_AVX,	true,	true, 	true },
-  { X86_XSTATE_MPX,	true,	true, 	false },
-  { X86_XSTATE_SSE,	true,	false, 	false },
-  { X86_XSTATE_X87,	true,	false, 	false }
-};
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an i386 target description.  */
-
-static constexpr uint64_t
-x86_linux_i386_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an amd64 target description.  */
-
-static constexpr uint64_t
-x86_linux_amd64_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      mask |= entry.feature;
-
-  return mask;
-}
 
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an x32 target description.  */
+/* See arch/x86-linux-tdesc.h.  */
 
-static constexpr uint64_t
-x86_linux_x32_tdesc_feature_mask ()
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an i386 target description.  */
-
-static constexpr int
-x86_linux_i386_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an amd64 target description.  */
-
-static constexpr int
-x86_linux_amd64_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an x32 target description.  */
-
-static constexpr int
-x86_linux_x32_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-#ifdef IN_PROCESS_AGENT
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_amd64_tdesc_count ()
-{
-  return x86_linux_amd64_tdesc_count_1 ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_x32_tdesc_count ()
-{
-  return x86_linux_x32_tdesc_count_1 ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_i386_tdesc_count ()
-{
-  return x86_linux_i386_tdesc_count_1 ();
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-int
-x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
-{
-  /* The following table shows which features are checked for when creating
-     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
-     represents the bit order within the generated index number.
-
-     i386  | x87 sse mpx avx avx512 pkru
-     amd64 |         mpx avx avx512 pkru
-     i32   |             avx avx512 pkru
-
-     The features are ordered so that for each mode (i386, amd64, i32) the
-     generated index will form a continuous range.  */
-
-  int idx = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-     if ((xcr0 & x86_linux_all_tdesc_features[i].feature)
-	  == x86_linux_all_tdesc_features[i].feature)
-	idx |= (1 << i);
-    }
-
-  return idx;
-}
-
-
-#ifdef IN_PROCESS_AGENT
-
-/* Convert an index number (as returned from x86_linux_xcr0_to_tdesc_idx)
-   into an xcr0 value which can then be used to create a target
-   description.  */
-
-uint64_t
-x86_linux_tdesc_idx_to_xcr0 (int idx)
-{
-  uint64_t xcr0 = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((idx & (1 << i)) != 0)
-	xcr0 |= x86_linux_all_tdesc_features[i].feature;
-    }
-
-  return xcr0;
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-
-/* A cache of all possible i386 target descriptions.  */
-
-static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count_1 ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
-{
-  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count_1 ());
-
-  target_desc **tdesc = &i386_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = i386_create_target_description (xcr0, true, false);
-
-      init_target_desc (*tdesc, i386_expedite_regs);
-    }
-
-  return *tdesc;
-}
-#endif
-
 #ifdef __x86_64__
-
-/* A cache of all possible amd64 target descriptions.  */
-
-static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count_1 ()] = { };
-
-/* A cache of all possible x32 target descriptions.  */
-
-static target_desc *x32_tdescs[x86_linux_x32_tdesc_count_1 ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
-{
-  if (is_x32)
-    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  if (is_64bit)
+    init_target_desc (tdesc, amd64_expedite_regs);
   else
-    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
-
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  if (is_x32)
-    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count_1 ());
-  else
-    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count_1 ());
-
-  target_desc **tdesc = nullptr;
-
-  if (is_x32)
-    tdesc = &x32_tdescs[idx];
-  else
-    tdesc = &amd64_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
-
-      init_target_desc (*tdesc, amd64_expedite_regs);
-    }
-  return *tdesc;
-}
-
 #endif
+    init_target_desc (tdesc, i386_expedite_regs);
+}
