{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
{
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
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
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;
}
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[] =
{
--- /dev/null
+/* 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
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 */
/* 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];
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. */
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;
};
/* 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 */
};
/* 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 */
};
/* 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 */
};
/* 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 },
{ "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
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. */
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;
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:
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;
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));
}
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:
case MOD:
if (float_mode_p)
{
- *total = tune_info->fp_div[mode == DFmode];
+ *total = tune_param->fp_div[mode == DFmode];
return false;
}
/* Fall through. */
/* 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;
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));
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));
}
static int
riscv_issue_rate (void)
{
- return tune_info->issue_rate;
+ return tune_param->issue_rate;
}
/* Auxiliary function to emit RISC-V ELF attribute. */
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;
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;
#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
%(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
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).
$(POSTCOMPILE)
PASSES_EXTRA += $(srcdir)/config/riscv/riscv-passes.def
+
+$(common_out_file): $(srcdir)/config/riscv/riscv-cores.def
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
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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;
+}
+
--- /dev/null
+/* { 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;
+}
+