RISC-V: Support new .option arch directive.
https://github.com/riscv/riscv-asm-manual/pull/67 Format: .option arch, +<extension><version>, ... .option arch, -<extension> .option arch, =<ISA string> 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.
This commit is contained in:
parent
fd0ff19bf4
commit
d3ffd7f776
@ -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
|
||||
(_("riscv_update_subset: cannot remove extension i from "
|
||||
"the subset list"));
|
||||
(_("extensions must begin with +/-/= in .option arch `%s'"), str);
|
||||
return false;
|
||||
}
|
||||
riscv_remove_subset (rps->subset_list, subset);
|
||||
|
||||
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 <major>p<minor>. */
|
||||
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 <number>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
|
||||
(_("unknown ISA extension `%s' in .option arch `%s'"),
|
||||
subset, str);
|
||||
free (subset);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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 *);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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}
|
||||
|
10
gas/testsuite/gas/riscv/option-arch-01.s
Normal file
10
gas/testsuite/gas/riscv/option-arch-01.s
Normal file
@ -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
|
14
gas/testsuite/gas/riscv/option-arch-01a.d
Normal file
14
gas/testsuite/gas/riscv/option-arch-01a.d
Normal file
@ -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
|
||||
#...
|
8
gas/testsuite/gas/riscv/option-arch-01b.d
Normal file
8
gas/testsuite/gas/riscv/option-arch-01b.d
Normal file
@ -0,0 +1,8 @@
|
||||
#as:
|
||||
#readelf: -A
|
||||
#source: option-arch-01.s
|
||||
|
||||
Attribute Section: riscv
|
||||
File Attributes
|
||||
Tag_RISCV_arch: "rv64i2p0_c2p0"
|
||||
#...
|
8
gas/testsuite/gas/riscv/option-arch-02.d
Normal file
8
gas/testsuite/gas/riscv/option-arch-02.d
Normal file
@ -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"
|
||||
#...
|
8
gas/testsuite/gas/riscv/option-arch-02.s
Normal file
8
gas/testsuite/gas/riscv/option-arch-02.s
Normal file
@ -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
|
8
gas/testsuite/gas/riscv/option-arch-03.d
Normal file
8
gas/testsuite/gas/riscv/option-arch-03.d
Normal file
@ -0,0 +1,8 @@
|
||||
#as:
|
||||
#readelf: -A
|
||||
#source: option-arch-03.s
|
||||
|
||||
Attribute Section: riscv
|
||||
File Attributes
|
||||
Tag_RISCV_arch: "rv32i2p0_c2p0"
|
||||
#...
|
3
gas/testsuite/gas/riscv/option-arch-03.s
Normal file
3
gas/testsuite/gas/riscv/option-arch-03.s
Normal file
@ -0,0 +1,3 @@
|
||||
.attribute arch, "rv64ic"
|
||||
.option arch, +d2p0, -c
|
||||
.option arch, =rv32ic
|
3
gas/testsuite/gas/riscv/option-arch-fail.d
Normal file
3
gas/testsuite/gas/riscv/option-arch-fail.d
Normal file
@ -0,0 +1,3 @@
|
||||
#as:
|
||||
#source: option-arch-fail.s
|
||||
#error_output: option-arch-fail.l
|
8
gas/testsuite/gas/riscv/option-arch-fail.l
Normal file
8
gas/testsuite/gas/riscv/option-arch-fail.l
Normal file
@ -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 <number>p in .option arch `\+xvendor2p'
|
||||
.*Error: .option pop with no .option push
|
10
gas/testsuite/gas/riscv/option-arch-fail.s
Normal file
10
gas/testsuite/gas/riscv/option-arch-fail.s
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user