From: Nelson Chu Date: Fri, 19 Nov 2021 09:11:06 +0000 (+0800) Subject: RISC-V: Support new .option arch directive. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d3ffd7f77654adafe5f1989bdfdbe4a337ff2e8b;p=binutils-gdb.git RISC-V: Support new .option arch directive. https://github.com/riscv/riscv-asm-manual/pull/67 Format: .option arch, +, ... .option arch, - .option arch, = The new direcitve is used to enable/disable extensions for the specific code region. For example, .attribute arch, "rv64ic" # arch = rv64i2p0_c2p0 .option push .option arch, +d2p0, -c # arch = rv64i2p0_f2p0_d2p0, f is added implied .option arch, =rv32gc # arch = rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0 .option pop # arch = rv64i2p0_c2p0 Note that, 1. ".option rvc/norvc" have the same behavior as ".option arch +c/-c". 2. ".option arch -i" is illegal, since we cannot remove base i extension. 3. If arch=rv64i2p0, then ".option arch, +i3p0" will update the i's version from 2.0 to 3.0. 4. If arch=rv64i3p0, then ".option arch, +i" will update the i's version from 2.0 to the default one according to the chosen isa spec. bfd/ * elfxx-riscv.c (riscv_add_subset): If the subset is already added, and the new versions are not RISCV_UNKNOWN_VERSION, then update the versions to the subset list. (riscv_copy_subset): New function. Copy the subset from list. (riscv_copy_subset_list): New function. Return the new copyed list. (riscv_update_subset): Updated to make .option arch directives workable. * elfxx-riscv.h: Updated. gas/ * config/tc-riscv.c (riscv_subsets): Defined as a pointer. (riscv_rps_as): Init the subset_list to NULL, we will set it later once riscv_opts_stack is created or updated. (struct riscv_option_stack, riscv_opts_stack): Moved forward. (riscv_set_arch): Updated. (s_riscv_option): Support new .option arch directive, to add, remove or update subsets for the specific code region. (riscv_write_out_attrs): Updated. * doc/c-riscv.texi: Added document for new .option arch directive. * testsuite/gas/riscv/option-arch-01a.d: New testcase. * testsuite/gas/riscv/option-arch-01b.d: Likewise. * testsuite/gas/riscv/option-arch-01.s: Likewise.. * testsuite/gas/riscv/option-arch-02.d: Likewise. * testsuite/gas/riscv/option-arch-02.s: Likewise. * testsuite/gas/riscv/option-arch-fail.d: Likewise. * testsuite/gas/riscv/option-arch-fail.l: Likewise. * testsuite/gas/riscv/option-arch-fail.s: Likewise. --- diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c index 91afd4c715f..b8da40ce2b4 100644 --- a/bfd/elfxx-riscv.c +++ b/bfd/elfxx-riscv.c @@ -1468,7 +1468,15 @@ riscv_add_subset (riscv_subset_list_t *subset_list, riscv_subset_t *current, *new; if (riscv_lookup_subset (subset_list, subset, ¤t)) - return; + { + if (major != RISCV_UNKNOWN_VERSION + && minor != RISCV_UNKNOWN_VERSION) + { + current->major_version = major; + current->minor_version = minor; + } + return; + } new = xmalloc (sizeof *new); new->name = xstrdup (subset); @@ -2138,6 +2146,37 @@ riscv_arch_str (unsigned xlen, const riscv_subset_list_t *subset) return attr_str; } +/* Copy the subset in the subset list. */ + +static struct riscv_subset_t * +riscv_copy_subset (riscv_subset_list_t *subset_list, + riscv_subset_t *subset) +{ + if (subset == NULL) + return NULL; + + riscv_subset_t *new = xmalloc (sizeof *new); + new->name = xstrdup (subset->name); + new->major_version = subset->major_version; + new->minor_version = subset->minor_version; + new->next = riscv_copy_subset (subset_list, subset->next); + + if (subset->next == NULL) + subset_list->tail = new; + + return new; +} + +/* Copy the subset list. */ + +riscv_subset_list_t * +riscv_copy_subset_list (riscv_subset_list_t *subset_list) +{ + riscv_subset_list_t *new = xmalloc (sizeof *new); + new->head = riscv_copy_subset (new, subset_list->head); + return new; +} + /* Remove the SUBSET from the subset list. */ static void @@ -2164,40 +2203,111 @@ riscv_remove_subset (riscv_subset_list_t *subset_list, } /* Add/Remove an extension to/from the subset list. This is used for - the .option rvc or norvc. */ + the .option rvc or norvc, and .option arch directives. */ bool riscv_update_subset (riscv_parse_subset_t *rps, - const char *subset, - bool removed) + const char *str) { - if (strlen (subset) == 0 - || (strlen (subset) == 1 - && riscv_ext_order[(*subset - 'a')] == 0) - || (strlen (subset) > 1 - && rps->check_unknown_prefixed_ext - && !riscv_recognized_prefixed_ext (subset))) - { - rps->error_handler - (_("riscv_update_subset: unknown ISA extension `%s'"), subset); - return false; - } + const char *p = str; - if (removed) + do { - if (strcmp (subset, "i") == 0) + int major_version = RISCV_UNKNOWN_VERSION; + int minor_version = RISCV_UNKNOWN_VERSION; + + bool removed = false; + switch (*p++) + { + case '+': removed = false; break; + case '-': removed = true; break; + case '=': + riscv_release_subset_list (rps->subset_list); + return riscv_parse_subset (rps, p); + default: + rps->error_handler + (_("extensions must begin with +/-/= in .option arch `%s'"), str); + return false; + } + + char *subset = xstrdup (p); + char *q = subset; + const char *end_of_version; + /* Extract the whole prefixed extension by ','. */ + while (*q != '\0' && *q != ',') + q++; + /* Look forward to the first letter which is not p. */ + bool find_any_version = false; + bool find_minor_version = false; + while (1) + { + q--; + if (ISDIGIT (*q)) + find_any_version = true; + else if (find_any_version + && !find_minor_version + && *q == 'p' + && ISDIGIT (*(q - 1))) + find_minor_version = true; + else + break; + } + q++; + /* Check if the end of extension is 'p' or not. If yes, then + the second letter from the end cannot be number. */ + if (*(q - 1) == 'p' && ISDIGIT (*(q - 2))) + { + *q = '\0'; + rps->error_handler + (_("invalid ISA extension ends with p " + "in .option arch `%s'"), str); + free (subset); + return false; + } + end_of_version = + riscv_parsing_subset_version (q, &major_version, &minor_version); + *q = '\0'; + if (end_of_version == NULL) + { + free (subset); + return false; + } + + if (strlen (subset) == 0 + || (strlen (subset) == 1 + && riscv_ext_order[(*subset - 'a')] == 0) + || (strlen (subset) > 1 + && rps->check_unknown_prefixed_ext + && !riscv_recognized_prefixed_ext (subset))) { rps->error_handler - (_("riscv_update_subset: cannot remove extension i from " - "the subset list")); + (_("unknown ISA extension `%s' in .option arch `%s'"), + subset, str); + free (subset); return false; } - riscv_remove_subset (rps->subset_list, subset); + + if (removed) + { + if (strcmp (subset, "i") == 0) + { + rps->error_handler + (_("cannot remove extension `i' in .option arch `%s'"), str); + free (subset); + return false; + } + riscv_remove_subset (rps->subset_list, subset); + } + else + riscv_parse_add_subset (rps, subset, major_version, minor_version, true); + p += end_of_version - subset; + free (subset); } - else - riscv_parse_add_subset (rps, subset, - RISCV_UNKNOWN_VERSION, - RISCV_UNKNOWN_VERSION, true); + while (*p++ == ','); + + if (*(--p) != '\0') + rps->error_handler + (_("unexpected value in .option arch `%s'"), str); riscv_parse_add_implicit_subsets (rps); return riscv_parse_check_conflicts (rps); diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h index 8de9adca0b1..ea7edc42f35 100644 --- a/bfd/elfxx-riscv.h +++ b/bfd/elfxx-riscv.h @@ -92,8 +92,11 @@ riscv_estimate_digit (unsigned); extern int riscv_compare_subsets (const char *, const char *); +extern riscv_subset_list_t * +riscv_copy_subset_list (riscv_subset_list_t *); + extern bool -riscv_update_subset (riscv_parse_subset_t *, const char *, bool); +riscv_update_subset (riscv_parse_subset_t *, const char *); extern bool riscv_subset_supports (riscv_parse_subset_t *, const char *); diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c index 930cfb2125a..da3d9115667 100644 --- a/gas/config/tc-riscv.c +++ b/gas/config/tc-riscv.c @@ -235,16 +235,27 @@ riscv_set_rvc (bool rvc_value) the architecture string. The architecture string can be set by the -march option, the elf architecture attributes, and the --with-arch configure option. */ -static riscv_subset_list_t riscv_subsets; +static riscv_subset_list_t *riscv_subsets = NULL; static riscv_parse_subset_t riscv_rps_as = { - &riscv_subsets, /* subset_list. */ + NULL, /* subset_list, we will set it later once + riscv_opts_stack is created or updated. */ as_bad, /* error_handler. */ &xlen, /* xlen. */ &default_isa_spec, /* isa_spec. */ true, /* check_unknown_prefixed_ext. */ }; +/* This structure is used to hold a stack of .option values. */ +struct riscv_option_stack +{ + struct riscv_option_stack *next; + struct riscv_set_options options; + riscv_subset_list_t *subset_list; +}; + +static struct riscv_option_stack *riscv_opts_stack = NULL; + /* Set which ISA and extensions are available. */ static void @@ -257,7 +268,14 @@ riscv_set_arch (const char *s) return; } - riscv_release_subset_list (&riscv_subsets); + if (riscv_subsets == NULL) + { + riscv_subsets = XNEW (riscv_subset_list_t); + riscv_subsets->head = NULL; + riscv_subsets->tail = NULL; + riscv_rps_as.subset_list = riscv_subsets; + } + riscv_release_subset_list (riscv_subsets); riscv_parse_subset (&riscv_rps_as, s); riscv_set_rvc (false); @@ -3715,15 +3733,6 @@ riscv_pre_output_hook (void) subseg_set (seg, subseg); } -/* This structure is used to hold a stack of .option values. */ -struct riscv_option_stack -{ - struct riscv_option_stack *next; - struct riscv_set_options options; -}; - -static struct riscv_option_stack *riscv_opts_stack; - /* Handle the .option pseudo-op. */ static void @@ -3738,12 +3747,12 @@ s_riscv_option (int x ATTRIBUTE_UNUSED) if (strcmp (name, "rvc") == 0) { - riscv_update_subset (&riscv_rps_as, "c", false); + riscv_update_subset (&riscv_rps_as, "+c"); riscv_set_rvc (true); } else if (strcmp (name, "norvc") == 0) { - riscv_update_subset (&riscv_rps_as, "c", true); + riscv_update_subset (&riscv_rps_as, "-c"); riscv_set_rvc (false); } else if (strcmp (name, "pic") == 0) @@ -3758,14 +3767,24 @@ s_riscv_option (int x ATTRIBUTE_UNUSED) riscv_opts.csr_check = true; else if (strcmp (name, "no-csr-check") == 0) riscv_opts.csr_check = false; + else if (strncmp (name, "arch,", 5) == 0) + { + name += 5; + if (ISSPACE (*name) && *name != '\0') + name++; + riscv_update_subset (&riscv_rps_as, name); + } else if (strcmp (name, "push") == 0) { struct riscv_option_stack *s; - s = (struct riscv_option_stack *) xmalloc (sizeof *s); + s = XNEW (struct riscv_option_stack); s->next = riscv_opts_stack; s->options = riscv_opts; + s->subset_list = riscv_subsets; riscv_opts_stack = s; + riscv_subsets = riscv_copy_subset_list (s->subset_list); + riscv_rps_as.subset_list = riscv_subsets; } else if (strcmp (name, "pop") == 0) { @@ -3776,8 +3795,12 @@ s_riscv_option (int x ATTRIBUTE_UNUSED) as_bad (_(".option pop with no .option push")); else { - riscv_opts = s->options; + riscv_subset_list_t *release_subsets = riscv_subsets; riscv_opts_stack = s->next; + riscv_opts = s->options; + riscv_subsets = s->subset_list; + riscv_rps_as.subset_list = riscv_subsets; + riscv_release_subset_list (release_subsets); free (s); } } @@ -4262,7 +4285,7 @@ riscv_write_out_attrs (void) unsigned int i; /* Re-write architecture elf attribute. */ - arch_str = riscv_arch_str (xlen, &riscv_subsets); + arch_str = riscv_arch_str (xlen, riscv_subsets); bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str); xfree ((void *) arch_str); diff --git a/gas/doc/c-riscv.texi b/gas/doc/c-riscv.texi index bfbf61d64a7..697be3a6996 100644 --- a/gas/doc/c-riscv.texi +++ b/gas/doc/c-riscv.texi @@ -194,7 +194,7 @@ command-line options are respected for the bulk of the file being assembled. @itemx norvc Enables or disables the generation of compressed instructions. Instructions are opportunistically compressed by the RISC-V assembler when possible, but -sometimes this behavior is not desirable. +sometimes this behavior is not desirable, especially when handling alignments. @item pic @itemx nopic @@ -212,6 +212,19 @@ desirable. @itemx no-csr-check Enables or disables the CSR checking. +@item arch, @var{+extension[version]} [,...,@var{+extension_n[version_n]}] +@itemx arch, @var{-extension} [,...,@var{-extension_n}] +@itemx arch, @var{=ISA} +Enables or disables the extensions for specific code region. For example, +@samp{.option arch, +m2p0} means add m extension with version 2.0, and +@samp{.option arch, -f, -d} means remove extensions, f and d, from the +architecture string. Note that, @samp{.option arch, +c, -c} have the same +behavior as @samp{.option rvc, norvc}. However, they are also undesirable +sometimes. Besides, @samp{.option arch, -i} is illegal, since we cannot +remove the base i extension anytime. If you want to reset the whole ISA +string, you can also use @samp{.option arch, =rv32imac} to overwrite the +previous settings. + @cindex INSN directives @item .insn @var{type}, @var{operand} [,...,@var{operand_n}] @itemx .insn @var{insn_length}, @var{value} diff --git a/gas/testsuite/gas/riscv/option-arch-01.s b/gas/testsuite/gas/riscv/option-arch-01.s new file mode 100644 index 00000000000..201f9b37023 --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-01.s @@ -0,0 +1,10 @@ +.attribute arch, "rv64ic" +add a0, a0, a1 +.option push +.option arch, +d2p0, -c, +xvendor1p0 +add a0, a0, a1 +frcsr a0 # Should add mapping symbol with ISA here, and then dump it to frcsr. +.option push +.option arch, +i3p0, +m3p0, +d3p0 +.option pop +.option pop diff --git a/gas/testsuite/gas/riscv/option-arch-01a.d b/gas/testsuite/gas/riscv/option-arch-01a.d new file mode 100644 index 00000000000..59bc1d29664 --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-01a.d @@ -0,0 +1,14 @@ +#as: +#source: option-arch-01.s +#objdump: -d + +.*:[ ]+file format .* + + +Disassembly of section .text: + +0+000 <.text>: +[ ]+[0-9a-f]+:[ ]+952e[ ]+add[ ]+a0,a0,a1 +[ ]+[0-9a-f]+:[ ]+00b50533[ ]+add[ ]+a0,a0,a1 +[ ]+[0-9a-f]+:[ ]+00302573[ ]+csrr[ ]+a0,fcsr +#... diff --git a/gas/testsuite/gas/riscv/option-arch-01b.d b/gas/testsuite/gas/riscv/option-arch-01b.d new file mode 100644 index 00000000000..9a6c2c528ca --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-01b.d @@ -0,0 +1,8 @@ +#as: +#readelf: -A +#source: option-arch-01.s + +Attribute Section: riscv +File Attributes + Tag_RISCV_arch: "rv64i2p0_c2p0" +#... diff --git a/gas/testsuite/gas/riscv/option-arch-02.d b/gas/testsuite/gas/riscv/option-arch-02.d new file mode 100644 index 00000000000..0fe89ecaecb --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-02.d @@ -0,0 +1,8 @@ +#as: +#readelf: -A +#source: option-arch-02.s + +Attribute Section: riscv +File Attributes + Tag_RISCV_arch: "rv64i3p0_m3p0_f2p0_d3p0_c2p0_xvendor32x3p0" +#... diff --git a/gas/testsuite/gas/riscv/option-arch-02.s b/gas/testsuite/gas/riscv/option-arch-02.s new file mode 100644 index 00000000000..f4ceee8426a --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-02.s @@ -0,0 +1,8 @@ +.attribute arch, "rv64ic" +add a0, a0, a1 +.option push +.option arch, +d2p0, -c, +xvendor1p0 +add a0, a0, a1 +frcsr a0 +.option pop +.option arch, +i3p0, +m3p0, +d3p0, +xvendor32x3p0 diff --git a/gas/testsuite/gas/riscv/option-arch-03.d b/gas/testsuite/gas/riscv/option-arch-03.d new file mode 100644 index 00000000000..b621d036c29 --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-03.d @@ -0,0 +1,8 @@ +#as: +#readelf: -A +#source: option-arch-03.s + +Attribute Section: riscv +File Attributes + Tag_RISCV_arch: "rv32i2p0_c2p0" +#... diff --git a/gas/testsuite/gas/riscv/option-arch-03.s b/gas/testsuite/gas/riscv/option-arch-03.s new file mode 100644 index 00000000000..7183140b028 --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-03.s @@ -0,0 +1,3 @@ +.attribute arch, "rv64ic" +.option arch, +d2p0, -c +.option arch, =rv32ic diff --git a/gas/testsuite/gas/riscv/option-arch-fail.d b/gas/testsuite/gas/riscv/option-arch-fail.d new file mode 100644 index 00000000000..bce5d324f37 --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-fail.d @@ -0,0 +1,3 @@ +#as: +#source: option-arch-fail.s +#error_output: option-arch-fail.l diff --git a/gas/testsuite/gas/riscv/option-arch-fail.l b/gas/testsuite/gas/riscv/option-arch-fail.l new file mode 100644 index 00000000000..3e0599e8e0e --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-fail.l @@ -0,0 +1,8 @@ +.*Assembler messages: +.*Error: extensions must begin with \+/\-/\= in .option arch `m2p0' +.*Error: cannot remove extension `i' in .option arch `\-i' +.*Error: unknown ISA extension `zsubset' in .option arch `\+zsubset2p0' +.*Error: unknown ISA extension `f2p0_d' in .option arch `\+f2p0_d2p0' +.*Error: unknown ISA extension `' in .option arch `\+' +.*Error: invalid ISA extension ends with p in .option arch `\+xvendor2p' +.*Error: .option pop with no .option push diff --git a/gas/testsuite/gas/riscv/option-arch-fail.s b/gas/testsuite/gas/riscv/option-arch-fail.s new file mode 100644 index 00000000000..a0b1bdef322 --- /dev/null +++ b/gas/testsuite/gas/riscv/option-arch-fail.s @@ -0,0 +1,10 @@ +.attribute arch, "rv64ic" +.option push +.option arch, m2p0 +.option arch, -i +.option arch, +zsubset2p0 +.option arch, +f2p0_d2p0 +.option arch, + +.option arch, +xvendor2p +.option pop +.option pop