aboutsummaryrefslogtreecommitdiff
path: root/bfd/elfxx-riscv.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elfxx-riscv.c')
-rw-r--r--bfd/elfxx-riscv.c213
1 files changed, 172 insertions, 41 deletions
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 66e343f..fdcf902 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -1193,7 +1193,7 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
{
char subset[2] = {0, 0};
- if (*p == 'x' || *p == 's')
+ if (*p == 'x' || *p == 's' || *p == 'z')
break;
if (*p == '_')
@@ -1237,28 +1237,58 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
return p;
}
-/* Parsing function for non-standard and supervisor extensions.
+/* Classify the argument 'arch' into one of riscv_isa_ext_class_t. */
- Return Value:
- Points to the end of extensions.
+riscv_isa_ext_class_t
+riscv_get_prefix_class (const char *arch)
+{
+ switch (*arch)
+ {
+ case 's':
+ return RV_ISA_CLASS_S;
- Arguments:
- `rps`: Hooks and status for parsing subset.
- `march`: Full arch string.
- `p`: Curent parsing position.
- `ext_type`: What kind of extensions, 'x', 's' or 'sx'.
- `ext_type_str`: Full name for kind of extension. */
+ case 'x': return RV_ISA_CLASS_X;
+ case 'z': return RV_ISA_CLASS_Z;
+ default: return RV_ISA_CLASS_UNKNOWN;
+ }
+}
+
+/* Structure describing parameters to use when parsing a particular
+ riscv_isa_ext_class_t. One of these should be provided for each
+ possible class, except RV_ISA_CLASS_UNKNOWN. */
+
+typedef struct riscv_parse_config
+{
+ /* Class of the extension. */
+ riscv_isa_ext_class_t class;
+
+ /* Lower-case prefix string for error printing
+ and internal parser usage, e.g. "z", "x". */
+ const char *prefix;
+
+ /* Predicate which is used for checking whether
+ this is a "known" extension. For 'x',
+ it always returns true (since they are by
+ definition non-standard and cannot be known. */
+ bfd_boolean (*ext_valid_p) (const char *);
+} riscv_parse_config_t;
+
+/* Parse a generic prefixed extension.
+ march: The full architecture string as passed in by "-march=...".
+ p: Point from which to start parsing the -march string.
+ config: What class of extensions to parse, predicate funcs,
+ and strings to use in error reporting. */
static const char *
-riscv_parse_sv_or_non_std_ext (riscv_parse_subset_t *rps,
- const char *march,
- const char *p,
- const char *ext_type,
- const char *ext_type_str)
+riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
+ const char *march,
+ const char *p,
+ const riscv_parse_config_t *config)
{
unsigned major_version = 0;
unsigned minor_version = 0;
- size_t ext_type_len = strlen (ext_type);
+ const char *last_name;
+ riscv_isa_ext_class_t class;
while (*p)
{
@@ -1268,12 +1298,10 @@ riscv_parse_sv_or_non_std_ext (riscv_parse_subset_t *rps,
continue;
}
- if (strncmp (p, ext_type, ext_type_len) != 0)
- break;
-
- /* It's non-standard supervisor extension if it prefix with sx. */
- if ((ext_type[0] == 's') && (ext_type_len == 1)
- && (*(p + 1) == 'x'))
+ /* Assert that the current extension specifier matches our parsing
+ class. */
+ class = riscv_get_prefix_class (p);
+ if (class != config->class)
break;
char *subset = xstrdup (p);
@@ -1294,6 +1322,43 @@ riscv_parse_sv_or_non_std_ext (riscv_parse_subset_t *rps,
*q = '\0';
+ /* Check that the name is valid.
+ For 'x', anything goes but it cannot simply be 'x'.
+ For 'z', it must be known from a list and also cannot simply be 'z'.
+ For 's', it must be known from a list and also *can* simply be 's'. */
+
+ /* Check that the extension name is well-formed. */
+ if (!config->ext_valid_p (subset))
+ {
+ rps->error_handler
+ ("-march=%s: Invalid or unknown %s ISA extension: '%s'",
+ march, config->prefix, subset);
+ free (subset);
+ return NULL;
+ }
+
+ /* Check that the last item is not the same as this. */
+ last_name = rps->subset_list->tail->name;
+
+ if (!strcasecmp (last_name, subset))
+ {
+ rps->error_handler ("-march=%s: Duplicate %s ISA extension: \'%s\'",
+ march, config->prefix, subset);
+ free (subset);
+ return NULL;
+ }
+
+ /* Check that we are in alphabetical order within the subset. */
+ if (!strncasecmp (last_name, config->prefix, 1)
+ && strcasecmp (last_name, subset) > 0)
+ {
+ rps->error_handler ("-march=%s: %s ISA extension not in alphabetical "
+ "order: \'%s\' must come before \'%s\'.",
+ march, config->prefix, subset, last_name);
+ free (subset);
+ return NULL;
+ }
+
riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
free (subset);
p += end_of_version - subset;
@@ -1301,7 +1366,7 @@ riscv_parse_sv_or_non_std_ext (riscv_parse_subset_t *rps,
if (*p != '\0' && *p != '_')
{
rps->error_handler ("-march=%s: %s must separate with _",
- march, ext_type_str);
+ march, config->prefix);
return NULL;
}
}
@@ -1309,6 +1374,84 @@ riscv_parse_sv_or_non_std_ext (riscv_parse_subset_t *rps,
return p;
}
+/* List of Z-class extensions that binutils should know about.
+ Whether or not a particular entry is in this list will
+ dictate if gas/ld will accept its presence in the -march
+ string.
+
+ Example: To add an extension called "Zbb" (bitmanip base extension),
+ add "zbb" string to the list (all lowercase).
+
+ Keep this list alphabetically ordered. */
+
+static const char * const riscv_std_z_ext_strtab[] =
+ {
+ NULL
+ };
+
+/* Same as `riscv_std_z_ext_strtab', but for S-class extensions. */
+
+static const char * const riscv_std_s_ext_strtab[] =
+ {
+ NULL
+ };
+
+/* For the extension EXT, search through the list of known extensions
+ KNOWN_EXTS for a match, and return TRUE if found. */
+
+static bfd_boolean
+riscv_multi_letter_ext_valid_p (const char *ext,
+ const char *const *known_exts)
+{
+ for (size_t i = 0; known_exts[i]; ++i)
+ {
+ if (!strcmp (ext, known_exts[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Predicator function for x-prefixed extensions.
+ Anything goes, except the literal 'x'. */
+
+static bfd_boolean
+riscv_ext_x_valid_p (const char *arg)
+{
+ if (!strcasecmp (arg, "x"))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Predicator functions for z-prefixed extensions.
+ Only known z-extensions are permitted. */
+
+static bfd_boolean
+riscv_ext_z_valid_p (const char *arg)
+{
+ return riscv_multi_letter_ext_valid_p (arg, riscv_std_z_ext_strtab);
+}
+
+/* Predicator function for 's' prefixed extensions.
+ Must be either literal 's', or a known s-prefixed extension. */
+
+static bfd_boolean
+riscv_ext_s_valid_p (const char *arg)
+{
+ return riscv_multi_letter_ext_valid_p (arg, riscv_std_s_ext_strtab);
+}
+
+/* Parsing order that is specified by the ISA manual. */
+
+static const riscv_parse_config_t parse_config[] =
+{
+ {RV_ISA_CLASS_S, "s", riscv_ext_s_valid_p},
+ {RV_ISA_CLASS_Z, "z", riscv_ext_z_valid_p},
+ {RV_ISA_CLASS_X, "x", riscv_ext_x_valid_p},
+ {RV_ISA_CLASS_UNKNOWN, NULL, NULL}
+};
+
/* Function for parsing arch string.
Return Value:
@@ -1347,26 +1490,14 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
if (p == NULL)
return FALSE;
- /* Parsing non-standard extension. */
- p = riscv_parse_sv_or_non_std_ext (
- rps, arch, p, "x", "non-standard extension");
+ /* Parse the different classes of extensions in the specified order. */
- if (p == NULL)
- return FALSE;
+ for (size_t i = 0; i < ARRAY_SIZE (parse_config); ++i) {
+ p = riscv_parse_prefixed_ext (rps, arch, p, &parse_config[i]);
- /* Parsing supervisor extension. */
- p = riscv_parse_sv_or_non_std_ext (
- rps, arch, p, "s", "supervisor extension");
-
- if (p == NULL)
- return FALSE;
-
- /* Parsing non-standard supervisor extension. */
- p = riscv_parse_sv_or_non_std_ext (
- rps, arch, p, "sx", "non-standard supervisor extension");
-
- if (p == NULL)
- return FALSE;
+ if (p == NULL)
+ return FALSE;
+ }
if (*p != '\0')
{