aboutsummaryrefslogtreecommitdiff
path: root/bfd/elfxx-riscv.c
diff options
context:
space:
mode:
authorNelson Chu <nelson.chu@sifive.com>2021-04-07 12:50:19 +0800
committerNelson Chu <nelson.chu@sifive.com>2021-04-12 10:05:37 +0800
commite601909a3287bf541c6a7d82214bb387d2c76d82 (patch)
treeb007c2468451cb6e85531e60b22bc9aa1cb5e94d /bfd/elfxx-riscv.c
parent6803e1cb210ea0662afe8e783a9e8dd08a7dca9d (diff)
downloadbinutils-e601909a3287bf541c6a7d82214bb387d2c76d82.zip
binutils-e601909a3287bf541c6a7d82214bb387d2c76d82.tar.gz
binutils-e601909a3287bf541c6a7d82214bb387d2c76d82.tar.bz2
RISC-V: Support to parse the multi-letter prefix in the architecture string.
The original discussion is as follows, https://github.com/riscv/riscv-isa-manual/issues/637 I never considered the prefixes may have multiple letters, like zxm. But the ISA spec has been updated for a long time that I haven't noticed. This patch rewrites the part of architecture parser to support parsing the multi-letter prefixes. Besides, I also improve the parser to report errors in details. One of the most obvious improvement is - Do not parse the prefixed extensions according to the orders in the parse_config. If we do so, then we used to get "unexpected ISA string at end" errors, but the message is a little bit hard to know what is happening. I Remove the confused message, and let riscv_parse_prefixed_ext to report the details. bfd/ * elfxx-riscv.c (riscv_std_z_ext_strtab): Moved forward. (riscv_std_s_ext_strtab): Likewise. (riscv_std_h_ext_strtab): Likewise. (riscv_std_zxm_ext_strtab): Added for the zxm prefix. (enum riscv_prefix_ext_class): Moved forward and renamed from riscv_isa_ext_class. Reorder them according to the parsing order, since the enum values are used to check the orders in the riscv_compare_subsets. (struct riscv_parse_prefix_config): Moved forward and renamed from riscv_parse_config_t. Also removed the ext_valid_p field, the related functions are replaced by riscv_valid_prefixed_ext. (parse_config): Moved forward and updated. The more letters of the prefix string, the more forward it must be defined. Otherwise, we will get the wrong mapping when using strncmp in riscv_get_prefix_class. (riscv_get_prefix_class): Moved forward. Support to parse the multi-letter prefix, like zxm. (riscv_known_prefixed_ext): New function, check if the prefixed extension is supported according to the right riscv_std_*_ext_strtab. (riscv_valid_prefixed_ext): New function, used to replace the riscv_ext_*_valid_p functions. (riscv_init_ext_order): Do not set the values for prefix keywords since they may have multiple letters for now. (riscv_compare_subsets): Set the order values of prefix keywords to negative numbers according to the riscv_prefix_ext_class. (riscv_parse_std_ext): Call riscv_get_prefix_class to see if we have parsed the prefixed extensions. (riscv_parse_prefixed_ext): Updated and removed the parameter config. Report error when the prefix is unknown. (riscv_parse_subset): Do not parse the prefixed extensions according to the orders in the parse_config. Remove the confused message and let riscv_parse_prefixed_ext to report the details. * elfxx-riscv.h (enum riscv_isa_ext_class): Moved to elfxx-riscv.c. (riscv_get_prefix_class): Removed to static. gas/ * testsuite/gas/riscv/march-fail-order-x-std.d: Renamed from march-fail-porder-x-std.d. * testsuite/gas/riscv/march-fail-order-z-std.d: Renamed from march-fail-porder-z-std.d. * testsuite/gas/riscv/march-fail-order-x-z.d: Renamed from march-fail-porder-x-z.d. * testsuite/gas/riscv/march-fail-order-zx-std.l: Added to replace march-fail-porder.l. * testsuite/gas/riscv/march-fail-order-x-z.l: Likewise. * testsuite/gas/riscv/march-fail-order-x.l: Updated. * testsuite/gas/riscv/march-fail-order-z.l: Likewise. * testsuite/gas/riscv/march-fail-single-prefix-h.d: Renamed from march-fail-single-char-h.d. * testsuite/gas/riscv/march-fail-single-prefix-s.d: Renamed from march-fail-single-char-s.d. * testsuite/gas/riscv/march-fail-single-prefix-x.d: Renamed from march-fail-single-char-x.d. * testsuite/gas/riscv/march-fail-single-prefix-z.d: Renamed from march-fail-single-char-z.d. * testsuite/gas/riscv/march-fail-single-prefix-zmx.d: Added. * testsuite/gas/riscv/march-fail-single-prefix.l: Added to replace march-fail-single-prefix.l. * testsuite/gas/riscv/march-fail-unknown-zxm.d: Added. * testsuite/gas/riscv/march-fail-unknown-std.l: Updated. * testsuite/gas/riscv/march-fail-unknown.l: Likewise.
Diffstat (limited to 'bfd/elfxx-riscv.c')
-rw-r--r--bfd/elfxx-riscv.c395
1 files changed, 196 insertions, 199 deletions
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index f6a2509..a8613d8 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -1028,16 +1028,167 @@ riscv_elf_add_sub_reloc (bfd *abfd,
#define RISCV_UNKNOWN_VERSION -1
-/* Array is used to compare the orders of all extensions quickly.
+/* Lists of prefixed class extensions that binutils should know about.
+ Whether or not a particular entry is in these lists will dictate if
+ gas/ld will accept its presence in the architecture string.
+
+ Please add the extensions to the lists in lower case. However, keep
+ these subsets in alphabetical order in these tables is recommended,
+ although there is no impact on the current implementation. */
+
+static const char * const riscv_std_z_ext_strtab[] =
+{
+ "zba", "zbb", "zbc", "zicsr", "zifencei", "zihintpause", NULL
+};
+
+static const char * const riscv_std_s_ext_strtab[] =
+{
+ NULL
+};
+
+static const char * const riscv_std_h_ext_strtab[] =
+{
+ NULL
+};
+
+static const char * const riscv_std_zxm_ext_strtab[] =
+{
+ NULL
+};
+
+/* ISA extension prefixed name class. Must define them in parsing order. */
+enum riscv_prefix_ext_class
+{
+ RV_ISA_CLASS_Z = 1,
+ RV_ISA_CLASS_S,
+ RV_ISA_CLASS_H,
+ RV_ISA_CLASS_ZXM,
+ RV_ISA_CLASS_X,
+ RV_ISA_CLASS_UNKNOWN
+};
+
+/* Record the strings of the prefixed extensions, and their corresponding
+ classes. The more letters of the prefix string, the more forward it must
+ be defined. Otherwise, the riscv_get_prefix_class will map it to the
+ wrong classes. */
+struct riscv_parse_prefix_config
+{
+ /* Class of the extension. */
+ enum riscv_prefix_ext_class class;
+
+ /* Prefix string for error printing and internal parser usage. */
+ const char *prefix;
+};
+static const struct riscv_parse_prefix_config parse_config[] =
+{
+ {RV_ISA_CLASS_ZXM, "zxm"},
+ {RV_ISA_CLASS_Z, "z"},
+ {RV_ISA_CLASS_S, "s"},
+ {RV_ISA_CLASS_H, "h"},
+ {RV_ISA_CLASS_X, "x"},
+ {RV_ISA_CLASS_UNKNOWN, NULL}
+};
+
+/* Get the prefixed name class for the extensions, the class also
+ means the order of the prefixed extensions. */
+
+static enum riscv_prefix_ext_class
+riscv_get_prefix_class (const char *arch)
+{
+ int i = 0;
+ while (parse_config[i].class != RV_ISA_CLASS_UNKNOWN)
+ {
+ if (strncmp (arch, parse_config[i].prefix,
+ strlen (parse_config[i].prefix)) == 0)
+ return parse_config[i].class;
+ i++;
+ }
+ return RV_ISA_CLASS_UNKNOWN;
+}
+
+/* Check KNOWN_EXTS to see if the EXT is supported. */
+
+static bool
+riscv_known_prefixed_ext (const char *ext,
+ const char *const *known_exts)
+{
+ size_t i;
+ for (i = 0; known_exts[i]; ++i)
+ if (strcmp (ext, known_exts[i]) == 0)
+ return true;
+ return false;
+}
- Zero value: Preserved keyword.
- Negative value: Prefixed keyword (s, h, x, z).
- Positive value: Standard extension. */
+/* Check whether the prefixed extension is valid or not. Return
+ true if valid, otehrwise return false. */
+
+static bool
+riscv_valid_prefixed_ext (const char *ext)
+{
+ enum riscv_prefix_ext_class class = riscv_get_prefix_class (ext);
+ switch (class)
+ {
+ case RV_ISA_CLASS_Z:
+ return riscv_known_prefixed_ext (ext, riscv_std_z_ext_strtab);
+ case RV_ISA_CLASS_ZXM:
+ return riscv_known_prefixed_ext (ext, riscv_std_zxm_ext_strtab);
+ case RV_ISA_CLASS_S:
+ return riscv_known_prefixed_ext (ext, riscv_std_s_ext_strtab);
+ case RV_ISA_CLASS_H:
+ return riscv_known_prefixed_ext (ext, riscv_std_h_ext_strtab);
+ case RV_ISA_CLASS_X:
+ /* Only the single x is invalid. */
+ if (strcmp (ext, "x") != 0)
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/* Array is used to compare the orders of standard extensions quickly. */
static int riscv_ext_order[26] = {0};
+/* Init the riscv_ext_order array. */
+
+static void
+riscv_init_ext_order (void)
+{
+ static bool inited = false;
+ const char *std_base_exts = "eig";
+ const char *std_remain_exts = riscv_supported_std_ext ();
+ const char *ext;
+ int order;
+
+ if (inited)
+ return;
+
+ /* The orders of all standard extensions are positive. */
+ order = 1;
+
+ /* Init the standard base extensions first. */
+ for (ext = std_base_exts; *ext; ext++)
+ riscv_ext_order[(*ext - 'a')] = order++;
+
+ /* Init the standard remaining extensions. */
+ for (ext = std_remain_exts; *ext; ext++)
+ riscv_ext_order[(*ext - 'a')] = order++;
+
+ /* Some of the prefixed keyword are not single letter, so we set
+ their prefixed orders in the riscv_compare_subsets directly,
+ not through the riscv_ext_order. */
+
+ inited = true;
+}
+
/* Similar to the strcmp. It returns an integer less than, equal to,
or greater than zero if `subset2` is found, respectively, to be less
- than, to match, or be greater than `subset1`. */
+ than, to match, or be greater than `subset1`.
+
+ The order values,
+ Zero: Preserved keywords.
+ Positive number: Standard extensions.
+ Negative number: Prefixed keywords. */
int
riscv_compare_subsets (const char *subset1, const char *subset2)
@@ -1049,10 +1200,19 @@ riscv_compare_subsets (const char *subset1, const char *subset2)
if (order1 > 0 && order2 > 0)
return order1 - order2;
- if (order1 == order2 && order1 < 0)
+ /* Set the prefixed orders to negative numbers. */
+ enum riscv_prefix_ext_class class1 = riscv_get_prefix_class (subset1);
+ enum riscv_prefix_ext_class class2 = riscv_get_prefix_class (subset2);
+
+ if (class1 != RV_ISA_CLASS_UNKNOWN)
+ order1 = - (int) class1;
+ if (class2 != RV_ISA_CLASS_UNKNOWN)
+ order2 = - (int) class2;
+
+ if (order1 == order2)
{
/* Compare the standard addition z extensions. */
- if (*subset1 == 'z')
+ if (class1 == RV_ISA_CLASS_Z)
{
order1 = riscv_ext_order[(*++subset1 - 'a')];
order2 = riscv_ext_order[(*++subset2 - 'a')];
@@ -1403,7 +1563,9 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
while (p != NULL && *p != '\0')
{
- if (*p == 'x' || *p == 's' || *p == 'h' || *p == 'z')
+ /* Stop when we parsed the known prefix class. */
+ enum riscv_prefix_ext_class class = riscv_get_prefix_class (p);
+ if (class != RV_ISA_CLASS_UNKNOWN)
break;
if (*p == '_')
@@ -1419,10 +1581,10 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
if (std_ext != *std_exts)
{
- if (strchr (all_std_exts, std_ext) == NULL)
+ if (riscv_ext_order[(std_ext - 'a')] == 0)
rps->error_handler
- (_("-march=%s: unknown standard ISA extension `%c'"),
- march, std_ext);
+ (_("-march=%s: unknown standard and prefixed ISA "
+ "extension `%s'"), march, p);
else
rps->error_handler
(_("-march=%s: standard ISA extension `%c' is not "
@@ -1443,38 +1605,6 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
return p;
}
-/* Classify ARCH into one of riscv_isa_ext_class_t. */
-
-riscv_isa_ext_class_t
-riscv_get_prefix_class (const char *arch)
-{
- switch (*arch)
- {
- case 's': return RV_ISA_CLASS_S;
- case 'h': return RV_ISA_CLASS_H;
- 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;
-
- /* Prefix string for error printing and internal parser usage. */
- 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. */
- bool (*ext_valid_p) (const char *);
-} riscv_parse_config_t;
-
/* Parsing function for prefixed extensions.
Return Value:
@@ -1490,13 +1620,12 @@ typedef struct riscv_parse_config
static const char *
riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
const char *march,
- const char *p,
- const riscv_parse_config_t *config)
+ const char *p)
{
int major_version;
int minor_version;
const char *last_name;
- riscv_isa_ext_class_t class;
+ enum riscv_prefix_ext_class class;
while (*p)
{
@@ -1506,12 +1635,14 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
continue;
}
- /* Assert that the current extension specifier matches our parsing
- class. */
class = riscv_get_prefix_class (p);
- if (class != config->class
- || class == RV_ISA_CLASS_UNKNOWN)
- break;
+ if (class == RV_ISA_CLASS_UNKNOWN)
+ {
+ rps->error_handler
+ (_("-march=%s: unknown prefix class for the ISA extension `%s'"),
+ march, p);
+ return NULL;
+ }
char *subset = xstrdup (p);
char *q = subset;
@@ -1532,18 +1663,18 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
return NULL;
}
- /* Check that the prefix extension is known.
+ /* Check if the prefix extension is known.
For 'x', anything goes but it cannot simply be 'x'.
For 's', it must be known from a list and cannot simply be 's'.
For 'h', it must be known from a list and cannot simply be 'h'.
For 'z', it must be known from a list and cannot simply be 'z'. */
/* Check that the extension name is well-formed. */
- if (!config->ext_valid_p (subset))
+ if (!riscv_valid_prefixed_ext (subset))
{
rps->error_handler
- (_("-march=%s: unknown %s ISA extension `%s'"),
- march, config->prefix, subset);
+ (_("-march=%s: unknown prefixed ISA extension `%s'"),
+ march, subset);
free (subset);
return NULL;
}
@@ -1553,19 +1684,19 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
if (!strcasecmp (last_name, subset))
{
rps->error_handler
- (_("-march=%s: duplicate %s ISA extension `%s'"),
- march, config->prefix, subset);
+ (_("-march=%s: duplicate prefixed ISA extension `%s'"),
+ march, subset);
free (subset);
return NULL;
}
- /* Check that the extension is in alphabetical order. */
+ /* Check that the extension is in expected order. */
if (riscv_compare_subsets (last_name, subset) > 0)
{
rps->error_handler
- (_("-march=%s: %s ISA extension `%s' is not in alphabetical "
+ (_("-march=%s: prefixed ISA extension `%s' is not in expected "
"order. It must come before `%s'"),
- march, config->prefix, subset, last_name);
+ march, subset, last_name);
free (subset);
return NULL;
}
@@ -1579,8 +1710,8 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
if (*p != '\0' && *p != '_')
{
rps->error_handler
- (_("-march=%s: %s ISA extension must separate with _"),
- march, config->prefix);
+ (_("-march=%s: prefixed ISA extension must separate with _"),
+ march);
return NULL;
}
}
@@ -1588,132 +1719,6 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
return p;
}
-/* Lists of prefixed class extensions that binutils should know about.
- Whether or not a particular entry is in these lists will dictate if
- gas/ld will accept its presence in the architecture string.
-
- Please add the extensions to the lists in lower case. However, keep
- these subsets in alphabetical order in these tables is recommended,
- although there is no impact on the current implementation. */
-
-static const char * const riscv_std_z_ext_strtab[] =
-{
- "zba", "zbb", "zbc", "zicsr", "zifencei", "zihintpause", NULL
-};
-
-static const char * const riscv_std_s_ext_strtab[] =
-{
- NULL
-};
-
-static const char * const riscv_std_h_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 bool
-riscv_multi_letter_ext_valid_p (const char *ext,
- const char *const *known_exts)
-{
- size_t i;
-
- for (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 bool
-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 bool
-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.
- Only known s-extensions are permitted. */
-
-static bool
-riscv_ext_s_valid_p (const char *arg)
-{
- return riscv_multi_letter_ext_valid_p (arg, riscv_std_s_ext_strtab);
-}
-
-/* Predicator function for 'h' prefixed extensions.
- Only known h-extensions are permitted. */
-
-static bool
-riscv_ext_h_valid_p (const char *arg)
-{
- return riscv_multi_letter_ext_valid_p (arg, riscv_std_h_ext_strtab);
-}
-
-/* Parsing order of the prefixed extensions that is specified by
- the ISA spec. */
-static const riscv_parse_config_t parse_config[] =
-{
- {RV_ISA_CLASS_S, "s", riscv_ext_s_valid_p},
- {RV_ISA_CLASS_H, "h", riscv_ext_h_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}
-};
-
-/* Init the riscv_ext_order array. */
-
-static void
-riscv_init_ext_order (void)
-{
- static bool inited = false;
- const char *std_base_exts = "eig";
- const char *std_remain_exts = riscv_supported_std_ext ();
- const char *ext;
- unsigned int i;
- int order;
-
- if (inited)
- return;
-
- /* The orders of all standard extensions are positive. */
- order = 1;
-
- /* Init the standard base extensions first. */
- for (ext = std_base_exts; *ext; ext++)
- riscv_ext_order[(*ext - 'a')] = order++;
-
- /* Init the standard remaining extensions. */
- for (ext = std_remain_exts; *ext; ext++)
- riscv_ext_order[(*ext - 'a')] = order++;
-
- /* Init the order for prefixed keywords. The orders are negative. */
- order = -1;
- for (i = 0; parse_config[i].class != RV_ISA_CLASS_UNKNOWN; i++)
- {
- ext = parse_config[i].prefix;
- riscv_ext_order[(*ext - 'a')] = order--;
- }
-
- inited = true;
-}
-
/* Add the implicit extensions. */
static void
@@ -1787,7 +1792,6 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
{
riscv_subset_t *subset = NULL;
const char *p;
- size_t i;
bool no_conflict = true;
for (p = arch; *p != '\0'; p++)
@@ -1837,19 +1841,12 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
return false;
/* Parse the different classes of extensions in the specified order. */
- for (i = 0; i < ARRAY_SIZE (parse_config); ++i)
+ while (*p != '\0')
{
- p = riscv_parse_prefixed_ext (rps, arch, p, &parse_config[i]);
+ p = riscv_parse_prefixed_ext (rps, arch, p);
if (p == NULL)
- return false;
- }
-
- if (*p != '\0')
- {
- rps->error_handler (_("-march=%s: unexpected ISA string at end: %s"),
- arch, p);
- return false;
+ return false;
}
/* Finally add implicit extensions according to the current