diff --git a/gcc/common/config/riscv/riscv-common.c b/gcc/common/config/riscv/riscv-common.c
index 82c5154b6118..4b6bdf8685d7 100644
--- a/gcc/common/config/riscv/riscv-common.c
+++ b/gcc/common/config/riscv/riscv-common.c
@@ -60,6 +60,14 @@ riscv_implied_info_t riscv_implied_info[] =
   {NULL, NULL}
 };
 
+static const riscv_cpu_info riscv_cpu_tables[] =
+{
+#define RISCV_CORE(CORE_NAME, ARCH, TUNE) \
+    {CORE_NAME, ARCH, TUNE},
+#include "../../../config/riscv/riscv-cores.def"
+    {NULL, NULL, NULL}
+};
+
 /* Subset list.  */
 class riscv_subset_list
 {
@@ -604,8 +612,10 @@ fail:
 std::string
 riscv_arch_str (bool version_p)
 {
-  gcc_assert (current_subset_list);
-  return current_subset_list->to_string (version_p);
+  if (current_subset_list)
+    return current_subset_list->to_string (version_p);
+  else
+    return std::string();
 }
 
 /* Parse a RISC-V ISA string into an option mask.  Must clear or set all arch
@@ -653,6 +663,21 @@ riscv_parse_arch_string (const char *isa, int *flags, location_t loc)
   current_subset_list = subset_list;
 }
 
+/* Return the riscv_cpu_info entry for CPU, NULL if not found.  */
+
+const riscv_cpu_info *
+riscv_find_cpu (const char *cpu)
+{
+  const riscv_cpu_info *cpu_info = &riscv_cpu_tables[0];
+  for (;cpu_info->name != NULL; ++cpu_info)
+    {
+      const char *name = cpu_info->name;
+      if (strcmp (cpu, name) == 0)
+	return cpu_info;
+    }
+  return NULL;
+}
+
 /* Implement TARGET_HANDLE_OPTION.  */
 
 static bool
@@ -667,6 +692,12 @@ riscv_handle_option (struct gcc_options *opts,
       riscv_parse_arch_string (decoded->arg, &opts->x_target_flags, loc);
       return true;
 
+    case OPT_mcpu_:
+      if (riscv_find_cpu (decoded->arg) == NULL)
+	error_at (loc, "%<-mcpu=%s%>: unknown CPU",
+		  decoded->arg);
+      return true;
+
     default:
       return true;
     }
@@ -678,15 +709,65 @@ const char *
 riscv_expand_arch (int argc ATTRIBUTE_UNUSED,
 		   const char **argv)
 {
-  static char *_arch_buf;
   gcc_assert (argc == 1);
   int flags;
   location_t loc = UNKNOWN_LOCATION;
   riscv_parse_arch_string (argv[0], &flags, loc);
-  _arch_buf = xstrdup (riscv_arch_str (false).c_str ());
-  return _arch_buf;
+  const std::string arch = riscv_arch_str (false);
+  if (arch.length())
+    return xasprintf ("-march=%s", arch.c_str());
+  else
+    return "";
 }
 
+/* Expand default -mtune option from -mcpu option, use default --with-tune value
+   if -mcpu don't have valid value.  */
+
+const char *
+riscv_default_mtune (int argc, const char **argv)
+{
+  gcc_assert (argc == 2);
+  const riscv_cpu_info *cpu = riscv_find_cpu (argv[0]);
+  const char *default_mtune = argv[1];
+  if (cpu)
+    return cpu->tune;
+  else
+    return default_mtune;
+}
+
+/* Expand arch string with implied extensions from -mcpu option.  */
+
+const char *
+riscv_expand_arch_from_cpu (int argc ATTRIBUTE_UNUSED,
+			    const char **argv)
+{
+  gcc_assert (argc > 0 && argc <= 2);
+  const char *default_arch_str = NULL;
+  const char *arch_str = NULL;
+  if (argc >= 2)
+    default_arch_str = argv[1];
+
+  const riscv_cpu_info *cpu = riscv_find_cpu (argv[0]);
+
+  if (cpu == NULL)
+    {
+      if (default_arch_str == NULL)
+	return "";
+      else
+	arch_str = default_arch_str;
+    }
+  else
+    arch_str = cpu->arch;
+
+  location_t loc = UNKNOWN_LOCATION;
+  int flags;
+
+  riscv_parse_arch_string (arch_str, &flags, loc);
+  const std::string arch = riscv_arch_str (false);
+  return xasprintf ("-march=%s", arch.c_str());
+}
+
+
 /* Implement TARGET_OPTION_OPTIMIZATION_TABLE.  */
 static const struct default_options riscv_option_optimization_table[] =
   {
diff --git a/gcc/config/riscv/riscv-cores.def b/gcc/config/riscv/riscv-cores.def
new file mode 100644
index 000000000000..6a13f3e453c4
--- /dev/null
+++ b/gcc/config/riscv/riscv-cores.def
@@ -0,0 +1,49 @@
+/* List of supported core and tune info for RISC-V.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC 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, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This is a list of cores that implement RISC-V.
+
+   Before using #include to read this file, define a macro:
+
+      RISCV_CORE(CORE_NAME, ARCH, MICRO_ARCH, TUNE_INFO)
+
+   The CORE_NAME is the name of the core, represented as a string.
+   The ARCH is the default arch of the core, represented as a string,
+   can be NULL if no default arch.
+   The MICRO_ARCH is the name of the core for which scheduling decisions
+   will be made, represented as an identifier.
+   The TUNE_INFO is the detail cost model for this core, represented as an
+   identifier, reference to riscv-tunes.def.  */
+
+RISCV_CORE("sifive-e20",      "rv32imc",    "rocket")
+RISCV_CORE("sifive-e21",      "rv32imac",   "rocket")
+RISCV_CORE("sifive-e24",      "rv32imafc",  "rocket")
+RISCV_CORE("sifive-e31",      "rv32imac",   "sifive-3-series")
+RISCV_CORE("sifive-e34",      "rv32imafc",  "sifive-3-series")
+RISCV_CORE("sifive-e76",      "rv32imafc",  "sifive-7-series")
+
+RISCV_CORE("sifive-s21",      "rv64imac",   "rocket")
+RISCV_CORE("sifive-s51",      "rv64imac",   "sifive-5-series")
+RISCV_CORE("sifive-s54",      "rv64imafdc", "sifive-5-series")
+RISCV_CORE("sifive-s76",      "rv64imafdc", "sifive-7-series")
+
+RISCV_CORE("sifive-u54",      "rv64imafdc", "sifive-5-series")
+RISCV_CORE("sifive-u74",      "rv64imafdc", "sifive-7-series")
+
+#undef RISCV_CORE
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 358224af8988..256dab1d0cff 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -94,4 +94,18 @@ extern bool riscv_hard_regno_rename_ok (unsigned, unsigned);
 
 rtl_opt_pass * make_pass_shorten_memrefs (gcc::context *ctxt);
 
+/* Information about one CPU we know about.  */
+struct riscv_cpu_info {
+  /* This CPU's canonical name.  */
+  const char *name;
+
+  /* Default arch for this CPU, could be NULL if no default arch.  */
+  const char *arch;
+
+  /* Which automaton to use for tuning.  */
+  const char *tune;
+};
+
+extern const riscv_cpu_info *riscv_find_cpu (const char *);
+
 #endif /* ! GCC_RISCV_PROTOS_H */
diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c
index 63b0c3877b0d..989a9f15250c 100644
--- a/gcc/config/riscv/riscv.c
+++ b/gcc/config/riscv/riscv.c
@@ -209,7 +209,7 @@ struct riscv_integer_op {
 
 /* Costs of various operations on the different architectures.  */
 
-struct riscv_tune_info
+struct riscv_tune_param
 {
   unsigned short fp_add[2];
   unsigned short fp_mul[2];
@@ -222,16 +222,16 @@ struct riscv_tune_info
   bool slow_unaligned_access;
 };
 
-/* Information about one CPU we know about.  */
-struct riscv_cpu_info {
-  /* This CPU's canonical name.  */
+/* Information about one micro-arch we know about.  */
+struct riscv_tune_info {
+  /* This micro-arch canonical name.  */
   const char *name;
 
   /* Which automaton to use for tuning.  */
   enum riscv_microarchitecture_type microarchitecture;
 
-  /* Tuning parameters for this CPU.  */
-  const struct riscv_tune_info *tune_info;
+  /* Tuning parameters for this micro-arch.  */
+  const struct riscv_tune_param *tune_param;
 };
 
 /* Global variables for machine-dependent things.  */
@@ -248,7 +248,7 @@ unsigned riscv_stack_boundary;
 static int epilogue_cfa_sp_offset;
 
 /* Which tuning parameters to use.  */
-static const struct riscv_tune_info *tune_info;
+static const struct riscv_tune_param *tune_param;
 
 /* Which automaton to use for tuning.  */
 enum riscv_microarchitecture_type riscv_microarchitecture;
@@ -275,7 +275,7 @@ const enum reg_class riscv_regno_to_class[FIRST_PSEUDO_REGISTER] = {
 };
 
 /* Costs to use when optimizing for rocket.  */
-static const struct riscv_tune_info rocket_tune_info = {
+static const struct riscv_tune_param rocket_tune_info = {
   {COSTS_N_INSNS (4), COSTS_N_INSNS (5)},	/* fp_add */
   {COSTS_N_INSNS (4), COSTS_N_INSNS (5)},	/* fp_mul */
   {COSTS_N_INSNS (20), COSTS_N_INSNS (20)},	/* fp_div */
@@ -288,7 +288,7 @@ static const struct riscv_tune_info rocket_tune_info = {
 };
 
 /* Costs to use when optimizing for Sifive 7 Series.  */
-static const struct riscv_tune_info sifive_7_tune_info = {
+static const struct riscv_tune_param sifive_7_tune_info = {
   {COSTS_N_INSNS (4), COSTS_N_INSNS (5)},	/* fp_add */
   {COSTS_N_INSNS (4), COSTS_N_INSNS (5)},	/* fp_mul */
   {COSTS_N_INSNS (20), COSTS_N_INSNS (20)},	/* fp_div */
@@ -301,7 +301,7 @@ static const struct riscv_tune_info sifive_7_tune_info = {
 };
 
 /* Costs to use when optimizing for size.  */
-static const struct riscv_tune_info optimize_size_tune_info = {
+static const struct riscv_tune_param optimize_size_tune_info = {
   {COSTS_N_INSNS (1), COSTS_N_INSNS (1)},	/* fp_add */
   {COSTS_N_INSNS (1), COSTS_N_INSNS (1)},	/* fp_mul */
   {COSTS_N_INSNS (1), COSTS_N_INSNS (1)},	/* fp_div */
@@ -343,7 +343,7 @@ static const unsigned gpr_save_reg_order[] = {
 };
 
 /* A table describing all the processors GCC knows about.  */
-static const struct riscv_cpu_info riscv_cpu_info_table[] = {
+static const struct riscv_tune_info riscv_tune_info_table[] = {
   { "rocket", generic, &rocket_tune_info },
   { "sifive-3-series", generic, &rocket_tune_info },
   { "sifive-5-series", generic, &rocket_tune_info },
@@ -351,17 +351,22 @@ static const struct riscv_cpu_info riscv_cpu_info_table[] = {
   { "size", generic, &optimize_size_tune_info },
 };
 
-/* Return the riscv_cpu_info entry for the given name string.  */
+/* Return the riscv_tune_info entry for the given name string.  */
 
-static const struct riscv_cpu_info *
-riscv_parse_cpu (const char *cpu_string)
+static const struct riscv_tune_info *
+riscv_parse_tune (const char *tune_string)
 {
-  for (unsigned i = 0; i < ARRAY_SIZE (riscv_cpu_info_table); i++)
-    if (strcmp (riscv_cpu_info_table[i].name, cpu_string) == 0)
-      return riscv_cpu_info_table + i;
+  const riscv_cpu_info *cpu = riscv_find_cpu (tune_string);
 
-  error ("unknown cpu %qs for %<-mtune%>", cpu_string);
-  return riscv_cpu_info_table;
+  if (cpu)
+    tune_string = cpu->tune;
+
+  for (unsigned i = 0; i < ARRAY_SIZE (riscv_tune_info_table); i++)
+    if (strcmp (riscv_tune_info_table[i].name, tune_string) == 0)
+      return riscv_tune_info_table + i;
+
+  error ("unknown cpu %qs for %<-mtune%>", tune_string);
+  return riscv_tune_info_table;
 }
 
 /* Helper function for riscv_build_integer; arguments are as for
@@ -1703,7 +1708,7 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
 	 instructions it needs.  */
       if ((cost = riscv_address_insns (XEXP (x, 0), mode, true)) > 0)
 	{
-	  *total = COSTS_N_INSNS (cost + tune_info->memory_cost);
+	  *total = COSTS_N_INSNS (cost + tune_param->memory_cost);
 	  return true;
 	}
       /* Otherwise use the default handling.  */
@@ -1770,7 +1775,7 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
 	 mode instead.  */
       mode = GET_MODE (XEXP (x, 0));
       if (float_mode_p)
-	*total = tune_info->fp_add[mode == DFmode];
+	*total = tune_param->fp_add[mode == DFmode];
       else
 	*total = riscv_binary_cost (x, 1, 3);
       return false;
@@ -1779,19 +1784,19 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
     case ORDERED:
       /* (FEQ(A, A) & FEQ(B, B)) compared against 0.  */
       mode = GET_MODE (XEXP (x, 0));
-      *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (2);
+      *total = tune_param->fp_add[mode == DFmode] + COSTS_N_INSNS (2);
       return false;
 
     case UNEQ:
       /* (FEQ(A, A) & FEQ(B, B)) compared against FEQ(A, B).  */
       mode = GET_MODE (XEXP (x, 0));
-      *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (3);
+      *total = tune_param->fp_add[mode == DFmode] + COSTS_N_INSNS (3);
       return false;
 
     case LTGT:
       /* (FLT(A, A) || FGT(B, B)).  */
       mode = GET_MODE (XEXP (x, 0));
-      *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (2);
+      *total = tune_param->fp_add[mode == DFmode] + COSTS_N_INSNS (2);
       return false;
 
     case UNGE:
@@ -1800,13 +1805,13 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
     case UNLT:
       /* FLT or FLE, but guarded by an FFLAGS read and write.  */
       mode = GET_MODE (XEXP (x, 0));
-      *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (4);
+      *total = tune_param->fp_add[mode == DFmode] + COSTS_N_INSNS (4);
       return false;
 
     case MINUS:
     case PLUS:
       if (float_mode_p)
-	*total = tune_info->fp_add[mode == DFmode];
+	*total = tune_param->fp_add[mode == DFmode];
       else
 	*total = riscv_binary_cost (x, 1, 4);
       return false;
@@ -1816,7 +1821,7 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
 	rtx op = XEXP (x, 0);
 	if (GET_CODE (op) == FMA && !HONOR_SIGNED_ZEROS (mode))
 	  {
-	    *total = (tune_info->fp_mul[mode == DFmode]
+	    *total = (tune_param->fp_mul[mode == DFmode]
 		      + set_src_cost (XEXP (op, 0), mode, speed)
 		      + set_src_cost (XEXP (op, 1), mode, speed)
 		      + set_src_cost (XEXP (op, 2), mode, speed));
@@ -1825,23 +1830,23 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
       }
 
       if (float_mode_p)
-	*total = tune_info->fp_add[mode == DFmode];
+	*total = tune_param->fp_add[mode == DFmode];
       else
 	*total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1);
       return false;
 
     case MULT:
       if (float_mode_p)
-	*total = tune_info->fp_mul[mode == DFmode];
+	*total = tune_param->fp_mul[mode == DFmode];
       else if (!TARGET_MUL)
 	/* Estimate the cost of a library call.  */
 	*total = COSTS_N_INSNS (speed ? 32 : 6);
       else if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
-	*total = 3 * tune_info->int_mul[0] + COSTS_N_INSNS (2);
+	*total = 3 * tune_param->int_mul[0] + COSTS_N_INSNS (2);
       else if (!speed)
 	*total = COSTS_N_INSNS (1);
       else
-	*total = tune_info->int_mul[mode == DImode];
+	*total = tune_param->int_mul[mode == DImode];
       return false;
 
     case DIV:
@@ -1849,7 +1854,7 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
     case MOD:
       if (float_mode_p)
 	{
-	  *total = tune_info->fp_div[mode == DFmode];
+	  *total = tune_param->fp_div[mode == DFmode];
 	  return false;
 	}
       /* Fall through.  */
@@ -1860,7 +1865,7 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
 	/* Estimate the cost of a library call.  */
 	*total = COSTS_N_INSNS (speed ? 32 : 6);
       else if (speed)
-	*total = tune_info->int_div[mode == DImode];
+	*total = tune_param->int_div[mode == DImode];
       else
 	*total = COSTS_N_INSNS (1);
       return false;
@@ -1882,11 +1887,11 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
     case FIX:
     case FLOAT_EXTEND:
     case FLOAT_TRUNCATE:
-      *total = tune_info->fp_add[mode == DFmode];
+      *total = tune_param->fp_add[mode == DFmode];
       return false;
 
     case FMA:
-      *total = (tune_info->fp_mul[mode == DFmode]
+      *total = (tune_param->fp_mul[mode == DFmode]
 		+ set_src_cost (XEXP (x, 0), mode, speed)
 		+ set_src_cost (XEXP (x, 1), mode, speed)
 		+ set_src_cost (XEXP (x, 2), mode, speed));
@@ -4546,7 +4551,7 @@ riscv_class_max_nregs (reg_class_t rclass, machine_mode mode)
 static int
 riscv_memory_move_cost (machine_mode mode, reg_class_t rclass, bool in)
 {
-  return (tune_info->memory_cost
+  return (tune_param->memory_cost
 	  + memory_move_secondary_cost (mode, rclass, in));
 }
 
@@ -4555,7 +4560,7 @@ riscv_memory_move_cost (machine_mode mode, reg_class_t rclass, bool in)
 static int
 riscv_issue_rate (void)
 {
-  return tune_info->issue_rate;
+  return tune_param->issue_rate;
 }
 
 /* Auxiliary function to emit RISC-V ELF attribute. */
@@ -4683,7 +4688,7 @@ riscv_init_machine_status (void)
 static void
 riscv_option_override (void)
 {
-  const struct riscv_cpu_info *cpu;
+  const struct riscv_tune_info *cpu;
 
 #ifdef SUBTARGET_OVERRIDE_OPTIONS
   SUBTARGET_OVERRIDE_OPTIONS;
@@ -4705,26 +4710,28 @@ riscv_option_override (void)
   if (TARGET_HARD_FLOAT && (target_flags_explicit & MASK_FDIV) == 0)
     target_flags |= MASK_FDIV;
 
-  /* Handle -mtune.  */
-  cpu = riscv_parse_cpu (riscv_tune_string ? riscv_tune_string :
-			 RISCV_TUNE_STRING_DEFAULT);
+  /* Handle -mtune, use -mcpu if -mtune is not given, and use default -mtune
+     if -mtune and -mcpu both not not given.  */
+  cpu = riscv_parse_tune (riscv_tune_string ? riscv_tune_string :
+			  (riscv_cpu_string ? riscv_cpu_string :
+			   RISCV_TUNE_STRING_DEFAULT));
   riscv_microarchitecture = cpu->microarchitecture;
-  tune_info = optimize_size ? &optimize_size_tune_info : cpu->tune_info;
+  tune_param = optimize_size ? &optimize_size_tune_info : cpu->tune_param;
 
   /* Use -mtune's setting for slow_unaligned_access, even when optimizing
      for size.  For architectures that trap and emulate unaligned accesses,
      the performance cost is too great, even for -Os.  Similarly, if
      -m[no-]strict-align is left unspecified, heed -mtune's advice.  */
-  riscv_slow_unaligned_access_p = (cpu->tune_info->slow_unaligned_access
+  riscv_slow_unaligned_access_p = (cpu->tune_param->slow_unaligned_access
 				   || TARGET_STRICT_ALIGN);
   if ((target_flags_explicit & MASK_STRICT_ALIGN) == 0
-      && cpu->tune_info->slow_unaligned_access)
+      && cpu->tune_param->slow_unaligned_access)
     target_flags |= MASK_STRICT_ALIGN;
 
   /* If the user hasn't specified a branch cost, use the processor's
      default.  */
   if (riscv_branch_cost == 0)
-    riscv_branch_cost = tune_info->branch_cost;
+    riscv_branch_cost = tune_param->branch_cost;
 
   /* Function to allocate machine-dependent function status.  */
   init_machine_status = &riscv_init_machine_status;
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index b7b4a1c88a52..172c7ca7c98b 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -41,17 +41,27 @@ along with GCC; see the file COPYING3.  If not see
 #endif
 
 extern const char *riscv_expand_arch (int argc, const char **argv);
+extern const char *riscv_expand_arch_from_cpu (int argc, const char **argv);
+extern const char *riscv_default_mtune (int argc, const char **argv);
 
 # define EXTRA_SPEC_FUNCTIONS						\
-  { "riscv_expand_arch", riscv_expand_arch },
+  { "riscv_expand_arch", riscv_expand_arch },				\
+  { "riscv_expand_arch_from_cpu", riscv_expand_arch_from_cpu },		\
+  { "riscv_default_mtune", riscv_default_mtune },
 
 /* Support for a compile-time default CPU, et cetera.  The rules are:
-   --with-arch is ignored if -march is specified.
+   --with-arch is ignored if -march or -mcpu is specified.
    --with-abi is ignored if -mabi is specified.
-   --with-tune is ignored if -mtune is specified.  */
+   --with-tune is ignored if -mtune or -mcpu is specified.
+
+   But using default -march/-mtune value if -mcpu don't have valid option.  */
 #define OPTION_DEFAULT_SPECS \
-  {"tune", "%{!mtune=*:-mtune=%(VALUE)}" }, \
-  {"arch", "%{!march=*:-march=%(VALUE)}" }, \
+  {"tune", "%{!mtune=*:"						\
+	   "  %{!mcpu=*:-mtune=%(VALUE)}"				\
+	   "  %{mcpu=*:-mtune=%:riscv_default_mtune(%* %(VALUE))}}" },	\
+  {"arch", "%{!march=*:"						\
+	   "  %{!mcpu=*:-march=%(VALUE)}"				\
+	   "  %{mcpu=*:%:riscv_expand_arch_from_cpu(%* %(VALUE))}}" },	\
   {"abi", "%{!mabi=*:-mabi=%(VALUE)}" }, \
 
 #ifdef IN_LIBGCC2
@@ -69,8 +79,9 @@ extern const char *riscv_expand_arch (int argc, const char **argv);
 %(subtarget_asm_spec)"
 
 #undef DRIVER_SELF_SPECS
-#define DRIVER_SELF_SPECS \
-"%{march=*:-march=%:riscv_expand_arch(%*)}"
+#define DRIVER_SELF_SPECS					\
+"%{march=*:%:riscv_expand_arch(%*)} "				\
+"%{!march=*:%{mcpu=*:%:riscv_expand_arch_from_cpu(%*)}} "
 
 #define TARGET_DEFAULT_CMODEL CM_MEDLOW
 
diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
index f01d3ab79c3b..808b4a044054 100644
--- a/gcc/config/riscv/riscv.opt
+++ b/gcc/config/riscv/riscv.opt
@@ -79,6 +79,10 @@ mtune=
 Target RejectNegative Joined Var(riscv_tune_string)
 -mtune=PROCESSOR	Optimize the output for PROCESSOR.
 
+mcpu=
+Target RejectNegative Joined Var(riscv_cpu_string)
+-mcpu=PROCESSOR	Use architecture of and optimize the output for PROCESSOR.
+
 msmall-data-limit=
 Target Joined Separate UInteger Var(g_switch_value) Init(8)
 -msmall-data-limit=N	Put global and static data smaller than <number> bytes into a special section (on some targets).
diff --git a/gcc/config/riscv/t-riscv b/gcc/config/riscv/t-riscv
index 4820fb35d317..702767c17362 100644
--- a/gcc/config/riscv/t-riscv
+++ b/gcc/config/riscv/t-riscv
@@ -24,3 +24,5 @@ riscv-shorten-memrefs.o: $(srcdir)/config/riscv/riscv-shorten-memrefs.c
 	$(POSTCOMPILE)
 
 PASSES_EXTRA += $(srcdir)/config/riscv/riscv-passes.def
+
+$(common_out_file): $(srcdir)/config/riscv/riscv-cores.def
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index c8281ecf5021..b8429592bb64 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -25998,14 +25998,30 @@ Generate code for given RISC-V ISA (e.g.@: @samp{rv64im}).  ISA strings must be
 lower-case.  Examples include @samp{rv64i}, @samp{rv32g}, @samp{rv32e}, and
 @samp{rv32imaf}.
 
+When @option{-march=} is not specified, use the setting from @option{-mcpu}.
+
+If both @option{-march} and @option{-mcpu=} are not specified, the default for
+this argument is system dependent, users who want a specific architecture
+extensions should specify one explicitly.
+
+@item -mcpu=@var{processor-string}
+@opindex mcpu
+Use architecture of and optimize the output for the given processor, specified
+by particular CPU name.
+Permissible values for this option are: @samp{sifive-e20}, @samp{sifive-e21},
+@samp{sifive-e24}, @samp{sifive-e31}, @samp{sifive-e34}, @samp{sifive-e76},
+@samp{sifive-s21}, @samp{sifive-s51}, @samp{sifive-s54}, @samp{sifive-s76},
+@samp{sifive-u54}, and @samp{sifive-u74}.
+
 @item -mtune=@var{processor-string}
 @opindex mtune
-Optimize the output for the given processor, specified by microarchitecture
-name.  Permissible values for this option are: @samp{rocket},
+Optimize the output for the given processor, specified by microarchitecture or
+particular CPU name.  Permissible values for this option are: @samp{rocket},
 @samp{sifive-3-series}, @samp{sifive-5-series}, @samp{sifive-7-series},
-and @samp{size}.
+@samp{size}, and all valid options for @option{-mcpu=}.
 
-When @option{-mtune=} is not specified, the default is @samp{rocket}.
+When @option{-mtune=} is not specified, use the setting from @option{-mcpu},
+the default is @samp{rocket} if both are not specified.
 
 The @samp{size} choice is not intended for use by end-users.  This is used
 when @option{-Os} is specified.  It overrides the instruction cost info
diff --git a/gcc/testsuite/gcc.target/riscv/mcpu-1.c b/gcc/testsuite/gcc.target/riscv/mcpu-1.c
new file mode 100644
index 000000000000..6f6005c79eb0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/mcpu-1.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-skip-if "-march given" { *-*-* } { "-march=*" } } */
+/* { dg-options "-mcpu=sifive-e20 -mabi=ilp32" } */
+/* sifive-e20 = rv32imc */
+
+#if !((__riscv_xlen == 32)		\
+      && !defined(__riscv_32e)		\
+      && defined(__riscv_mul)		\
+      && !defined(__riscv_atomic)	\
+      && !defined(__riscv_flen)		\
+      && defined(__riscv_compressed))
+#error "unexpected arch"
+#endif
+
+int main()
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/mcpu-2.c b/gcc/testsuite/gcc.target/riscv/mcpu-2.c
new file mode 100644
index 000000000000..2992f4e10836
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/mcpu-2.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-skip-if "-march given" { *-*-* } { "-march=*" } } */
+/* { dg-options "-mcpu=sifive-e34 -mabi=ilp32" } */
+/* sifive-e34 = rv32imafc */
+
+#if !((__riscv_xlen == 32)		\
+      && !defined(__riscv_32e)		\
+      && defined(__riscv_mul)		\
+      && defined(__riscv_atomic)	\
+      && (__riscv_flen == 32)		\
+      && defined(__riscv_compressed))
+#error "unexpected arch"
+#endif
+
+int main()
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/mcpu-3.c b/gcc/testsuite/gcc.target/riscv/mcpu-3.c
new file mode 100644
index 000000000000..97b3f8190faf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/mcpu-3.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-skip-if "-march given" { *-*-* } { "-march=*" } } */
+/* { dg-options "-mcpu=sifive-s51 -mabi=lp64" } */
+/* sifive-s51 = rv64imac */
+
+#if !((__riscv_xlen == 64)		\
+      && !defined(__riscv_32e)		\
+      && defined(__riscv_mul)		\
+      && defined(__riscv_atomic)	\
+      && !defined(__riscv_flen)		\
+      && defined(__riscv_compressed))
+#error "unexpected arch"
+#endif
+
+int main()
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/mcpu-4.c b/gcc/testsuite/gcc.target/riscv/mcpu-4.c
new file mode 100644
index 000000000000..52c598754227
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/mcpu-4.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-skip-if "-march given" { *-*-* } { "-march=*" } } */
+/* { dg-options "-mcpu=sifive-u74 -mabi=lp64" } */
+/* sifive-u74 = rv64imafdc */
+
+#if !((__riscv_xlen == 64)		\
+      && !defined(__riscv_32e)		\
+      && defined(__riscv_mul)		\
+      && defined(__riscv_atomic)	\
+      && (__riscv_flen == 64)		\
+      && defined(__riscv_compressed))
+#error "unexpected arch"
+#endif
+
+int main()
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/mcpu-5.c b/gcc/testsuite/gcc.target/riscv/mcpu-5.c
new file mode 100644
index 000000000000..c4ea7b5e41a3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/mcpu-5.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-skip-if "-march given" { *-*-* } { "-march=*" } } */
+/* Verify -march will override arch option from -mcpu.  */
+/* { dg-options "-mcpu=sifive-u74 -march=rv32ic -mabi=ilp32" } */
+/* sifive-s51 = rv64imafdc */
+
+#if !((__riscv_xlen == 32)		\
+      && !defined(__riscv_32e)		\
+      && !defined(__riscv_mul)		\
+      && !defined(__riscv_atomic)	\
+      && !defined(__riscv_flen)		\
+      && defined(__riscv_compressed))
+#error "unexpected arch"
+#endif
+
+int main()
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/mcpu-6.c b/gcc/testsuite/gcc.target/riscv/mcpu-6.c
new file mode 100644
index 000000000000..57e3345630c3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/mcpu-6.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* Verify -mtune has higher priority than -mcpu for pipeline model .  */
+/* { dg-options "-mcpu=sifive-u74 -mtune=rocket -fdump-rtl-sched2-details -O3 -march=rv32i -mabi=ilp32" } */
+/* { dg-final { scan-rtl-dump "simple_return\[ \]+:alu" "sched2" } } */
+
+int main()
+{
+  return 0;
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/mcpu-7.c b/gcc/testsuite/gcc.target/riscv/mcpu-7.c
new file mode 100644
index 000000000000..fe3c04be4b6d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/mcpu-7.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* Verify -mtune has higher priority than -mcpu for pipeline model .  */
+/* { dg-options "-mcpu=sifive-s21 -mtune=sifive-u74 -fdump-rtl-sched2-details -O3 -march=rv32i -mabi=ilp32" } */
+/* { dg-final { scan-rtl-dump "simple_return\[ \]+:sifive_7_B" "sched2" } } */
+
+int main()
+{
+  return 0;
+}
+
