Skip to content

Commit

Permalink
RISC-V: Output mapping symbols with ISA string.
Browse files Browse the repository at this point in the history
RISC-V Psabi pr196,
riscv-non-isa/riscv-elf-psabi-doc#196

bfd/
    * elfxx-riscv.c (riscv_release_subset_list): Free arch_str if needed.
    (riscv_copy_subset_list): Copy arch_str as well.
    * elfxx-riscv.h (riscv_subset_list_t): Store arch_str for each subset list.
gas/
    * config/tc-riscv.c (riscv_reset_subsets_list_arch_str): Update the
    architecture string in the subset_list.
    (riscv_set_arch): Call riscv_reset_subsets_list_arch_str after parsing new
    architecture string.
    (s_riscv_option): Likewise.
    (need_arch_map_symbol): New boolean, used to indicate if .option
    directives do affect instructions.
    (make_mapping_symbol): New boolean parameter reset_seg_arch_str.  Need to
    generate $x+arch for MAP_INSN, and then store it into tc_segment_info_data
    if reset_seg_arch_str is true.
    (riscv_mapping_state): Decide if we need to add $x+arch for MAP_INSN.  For
    now, only add $x+arch if the architecture strings in subset list and segment
    are different.  Besides, always add $x+arch at the start of section, and do
    not add $x+arch for code alignment, since rvc for alignment can be judged
    from addend of R_RISCV_ALIGN.
    (riscv_remove_mapping_symbol): If current and previous mapping symbol have
    same value, then remove the current $x only if the previous is $x+arch;
    Otherwise, always remove previous.
    (riscv_add_odd_padding_symbol): Updated.
    (riscv_check_mapping_symbols): Don't need to add any $x+arch if
    need_arch_map_symbol is false, so changed them to $x.
    (riscv_frag_align_code): Updated since riscv_mapping_state is changed.
    (riscv_init_frag): Likewise.
    (s_riscv_insn): Likewise.
    (riscv_elf_final_processing): Call riscv_release_subset_list to release
    subset_list of riscv_rps_as, rather than only release arch_str in the
    riscv_write_out_attrs.
    (riscv_write_out_attrs): No need to call riscv_arch_str, just get arch_str
    from subset_list of riscv_rps_as.
    * config/tc-riscv.h (riscv_segment_info_type): Record current $x+arch mapping
    symbol of each segment.
    * testsuite/gas/riscv/mapping-0*: Merged and replaced by mapping.s.
    * testsuite/gas/riscv/mapping.s: New testcase, to test most of the cases in
    one file.
    * testsuite/gas/riscv/mapping-symbols.d: Likewise.
    * testsuite/gas/riscv/mapping-dis.d: Likewise.
    * testsuite/gas/riscv/mapping-non-arch.s: New testcase for the case that
    does need any $x+arch.
    * testsuite/gas/riscv/mapping-non-arch.d: Likewise.
    * testsuite/gas/riscv/option-arch-01a.d: Updated.
opcodes/
    * riscv-dis.c (riscv_disassemble_insn): Set riscv_fpr_names back to
    riscv_fpr_names_abi or riscv_fpr_names_numeric when zfinx is disabled
    for some specfic code region.
    (riscv_get_map_state): Recognized mapping symbols $x+arch, and then reset
    the architecture string once the ISA is different.
  • Loading branch information
Nelson Chu committed Oct 28, 2022
1 parent 58b2ba6 commit 40f1a1a
Show file tree
Hide file tree
Showing 27 changed files with 421 additions and 328 deletions.
7 changes: 7 additions & 0 deletions bfd/elfxx-riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,12 @@ riscv_release_subset_list (riscv_subset_list_t *subset_list)
}

subset_list->tail = NULL;

if (subset_list->arch_str != NULL)
{
free ((void*) subset_list->arch_str);
subset_list->arch_str = NULL;
}
}

/* Parsing extension version.
Expand Down Expand Up @@ -2138,6 +2144,7 @@ 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);
new->arch_str = strdup (subset_list->arch_str);
return new;
}

Expand Down
1 change: 1 addition & 0 deletions bfd/elfxx-riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ typedef struct
{
riscv_subset_t *head;
riscv_subset_t *tail;
const char *arch_str;
} riscv_subset_list_t;

extern void
Expand Down
162 changes: 127 additions & 35 deletions gas/config/tc-riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,17 @@ static riscv_parse_subset_t riscv_rps_as =
true, /* check_unknown_prefixed_ext. */
};

/* Update the architecture string in the subset_list. */

static void
riscv_reset_subsets_list_arch_str (void)
{
riscv_subset_list_t *subsets = riscv_rps_as.subset_list;
if (subsets->arch_str != NULL)
free ((void *) subsets->arch_str);
subsets->arch_str = riscv_arch_str (xlen, subsets);
}

/* This structure is used to hold a stack of .option values. */
struct riscv_option_stack
{
Expand Down Expand Up @@ -306,9 +317,11 @@ riscv_set_arch (const char *s)
riscv_rps_as.subset_list = XNEW (riscv_subset_list_t);
riscv_rps_as.subset_list->head = NULL;
riscv_rps_as.subset_list->tail = NULL;
riscv_rps_as.subset_list->arch_str = NULL;
}
riscv_release_subset_list (riscv_rps_as.subset_list);
riscv_parse_subset (&riscv_rps_as, s);
riscv_reset_subsets_list_arch_str ();

riscv_set_rvc (false);
if (riscv_subset_supports (&riscv_rps_as, "c"))
Expand Down Expand Up @@ -457,54 +470,76 @@ static char *expr_end;
#define OPCODE_MATCHES(OPCODE, OP) \
(((OPCODE) & MASK_##OP) == MATCH_##OP)

/* Indicate if .option directives do affect instructions. Set to true means
we need to add $x+arch at somewhere; Otherwise just add $x for instructions
should be enough. */
static bool need_arch_map_symbol = false;

/* Create a new mapping symbol for the transition to STATE. */

static void
make_mapping_symbol (enum riscv_seg_mstate state,
valueT value,
fragS *frag)
fragS *frag,
bool reset_seg_arch_str)
{
const char *name;
char *buff;
switch (state)
{
case MAP_DATA:
name = "$d";
break;
case MAP_INSN:
name = "$x";
if (reset_seg_arch_str)
{
const char *isa = riscv_rps_as.subset_list->arch_str;
size_t size = strlen (isa) + 3; /* "rv" + '\0' */
buff = xmalloc (size);
snprintf (buff, size, "$x%s", isa);
name = buff;
}
else
name = "$x";
break;
default:
abort ();
}

symbolS *symbol = symbol_new (name, now_seg, frag, value);
symbol_get_bfdsym (symbol)->flags |= (BSF_NO_FLAGS | BSF_LOCAL);
if (reset_seg_arch_str)
{
/* Store current $x+arch into tc_segment_info. */
seg_info (now_seg)->tc_segment_info_data.arch_map_symbol = symbol;
xfree ((void *) buff);
}

/* If .fill or other data filling directive generates zero sized data,
or we are adding odd alignemnts, then the mapping symbol for the
following code will have the same value. */
then mapping symbol for the following code will have the same value.
Please see gas/testsuite/gas/riscv/mapping.s: .text.zero.fill.first
and .text.zero.fill.last. */
symbolS *first = frag->tc_frag_data.first_map_symbol;
symbolS *last = frag->tc_frag_data.last_map_symbol;
if (value == 0)
{
if (frag->tc_frag_data.first_map_symbol != NULL)
if (first != NULL)
{
know (S_GET_VALUE (frag->tc_frag_data.first_map_symbol)
== S_GET_VALUE (symbol));
know (S_GET_VALUE (first) == S_GET_VALUE (symbol)
&& first == last);
/* Remove the old one. */
symbol_remove (frag->tc_frag_data.first_map_symbol,
&symbol_rootP, &symbol_lastP);
symbol_remove (first, &symbol_rootP, &symbol_lastP);
}
frag->tc_frag_data.first_map_symbol = symbol;
}
if (frag->tc_frag_data.last_map_symbol != NULL)
else if (last != NULL)
{
/* The mapping symbols should be added in offset order. */
know (S_GET_VALUE (frag->tc_frag_data.last_map_symbol)
<= S_GET_VALUE (symbol));
know (S_GET_VALUE (last) <= S_GET_VALUE (symbol));
/* Remove the old one. */
if (S_GET_VALUE (frag->tc_frag_data.last_map_symbol)
== S_GET_VALUE (symbol))
symbol_remove (frag->tc_frag_data.last_map_symbol,
&symbol_rootP, &symbol_lastP);
if (S_GET_VALUE (last) == S_GET_VALUE (symbol))
symbol_remove (last, &symbol_rootP, &symbol_lastP);
}
frag->tc_frag_data.last_map_symbol = symbol;
}
Expand All @@ -513,27 +548,45 @@ make_mapping_symbol (enum riscv_seg_mstate state,

void
riscv_mapping_state (enum riscv_seg_mstate to_state,
int max_chars)
int max_chars,
bool frag_align_code)
{
enum riscv_seg_mstate from_state =
seg_info (now_seg)->tc_segment_info_data.map_state;
bool reset_seg_arch_str = false;

if (!SEG_NORMAL (now_seg)
/* For now I only add the mapping symbols to text sections.
/* For now we only add the mapping symbols to text sections.
Therefore, the dis-assembler only show the actual contents
distribution for text. Other sections will be shown as
data without the details. */
|| !subseg_text_p (now_seg))
return;

/* The mapping symbol should be emitted if not in the right
mapping state */
if (from_state == to_state)
mapping state. */
symbolS *seg_arch_symbol =
seg_info (now_seg)->tc_segment_info_data.arch_map_symbol;
if (to_state == MAP_INSN && seg_arch_symbol == 0)
{
/* Always add $x+arch at the first instruction of section. */
reset_seg_arch_str = true;
}
else if (seg_arch_symbol != 0
&& to_state == MAP_INSN
&& !frag_align_code
&& strcmp (riscv_rps_as.subset_list->arch_str,
S_GET_NAME (seg_arch_symbol) + 2) != 0)
{
reset_seg_arch_str = true;
need_arch_map_symbol = true;
}
else if (from_state == to_state)
return;

valueT value = (valueT) (frag_now_fix () - max_chars);
seg_info (now_seg)->tc_segment_info_data.map_state = to_state;
make_mapping_symbol (to_state, value, frag_now);
make_mapping_symbol (to_state, value, frag_now, reset_seg_arch_str);
}

/* Add the odd bytes of paddings for riscv_handle_align. */
Expand All @@ -542,9 +595,27 @@ static void
riscv_add_odd_padding_symbol (fragS *frag)
{
/* If there was already a mapping symbol, it should be
removed in the make_mapping_symbol. */
make_mapping_symbol (MAP_DATA, frag->fr_fix, frag);
make_mapping_symbol (MAP_INSN, frag->fr_fix + 1, frag);
removed in the make_mapping_symbol.
Please see gas/testsuite/gas/riscv/mapping.s: .text.odd.align. */
make_mapping_symbol (MAP_DATA, frag->fr_fix, frag, false);
make_mapping_symbol (MAP_INSN, frag->fr_fix + 1, frag, false);
}

/* If previous and current mapping symbol have same value, then remove the
current $x only if the previous is $x+arch; Otherwise, always remove the
previous. */

static void
riscv_remove_mapping_symbol (symbolS *pre, symbolS *cur)
{
know (pre != NULL && cur != NULL
&& S_GET_VALUE (pre) == S_GET_VALUE (cur));
symbolS *removed = pre;
if (strncmp (S_GET_NAME (pre), "$xrv", 4) == 0
&& strcmp (S_GET_NAME (cur), "$x") == 0)
removed = cur;
symbol_remove (removed, &symbol_rootP, &symbol_lastP);
}

/* Remove any excess mapping symbols generated for alignment frags in
Expand All @@ -563,6 +634,13 @@ riscv_check_mapping_symbols (bfd *abfd ATTRIBUTE_UNUSED,
if (seginfo == NULL || seginfo->frchainP == NULL)
return;

/* If we don't set any .option arch directive, then the arch_map_symbol
in each segment must be the first instruction, and we don't need to
add $x+arch for them. */
if (!need_arch_map_symbol
&& seginfo->tc_segment_info_data.arch_map_symbol != 0)
S_SET_NAME (seginfo->tc_segment_info_data.arch_map_symbol, "$x");

for (fragp = seginfo->frchainP->frch_root;
fragp != NULL;
fragp = fragp->fr_next)
Expand All @@ -581,17 +659,24 @@ riscv_check_mapping_symbols (bfd *abfd ATTRIBUTE_UNUSED,

do
{
if (next->tc_frag_data.first_map_symbol != NULL)
symbolS *next_first = next->tc_frag_data.first_map_symbol;
if (next_first != NULL)
{
/* The last mapping symbol overlaps with another one
which at the start of the next frag. */
symbol_remove (last, &symbol_rootP, &symbol_lastP);
which at the start of the next frag.
Please see the gas/testsuite/gas/riscv/mapping.s:
.text.zero.fill.align.A and .text.zero.fill.align.B. */
riscv_remove_mapping_symbol (last, next_first);
break;
}

if (next->fr_next == NULL)
{
/* The last mapping symbol is at the end of the section. */
/* The last mapping symbol is at the end of the section.
Please see the gas/testsuite/gas/riscv/mapping.s:
.text.last.section. */
know (next->fr_fix == 0 && next->fr_var == 0);
symbol_remove (last, &symbol_rootP, &symbol_lastP);
break;
Expand Down Expand Up @@ -3455,7 +3540,7 @@ md_assemble (char *str)
return;
}

riscv_mapping_state (MAP_INSN, 0);
riscv_mapping_state (MAP_INSN, 0, 0/* frag_align_code */);

const struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
&imm_reloc, op_hash);
Expand Down Expand Up @@ -3963,11 +4048,13 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
if (strcmp (name, "rvc") == 0)
{
riscv_update_subset (&riscv_rps_as, "+c");
riscv_reset_subsets_list_arch_str ();
riscv_set_rvc (true);
}
else if (strcmp (name, "norvc") == 0)
{
riscv_update_subset (&riscv_rps_as, "-c");
riscv_reset_subsets_list_arch_str ();
riscv_set_rvc (false);
}
else if (strcmp (name, "pic") == 0)
Expand All @@ -3988,6 +4075,7 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
if (ISSPACE (*name) && *name != '\0')
name++;
riscv_update_subset (&riscv_rps_as, name);
riscv_reset_subsets_list_arch_str ();

riscv_set_rvc (false);
if (riscv_subset_supports (&riscv_rps_as, "c"))
Expand Down Expand Up @@ -4117,6 +4205,10 @@ riscv_frag_align_code (int n)
if (!riscv_opts.relax)
return false;

/* Maybe we should use frag_var to create a new rs_align_code fragment,
rather than just use frag_more to handle an alignment here? So that we
don't need to call riscv_mapping_state again later, and then only need
to check frag->fr_type to see if it is frag_align_code. */
nops = frag_more (worst_case_bytes);

ex.X_op = O_constant;
Expand All @@ -4127,7 +4219,7 @@ riscv_frag_align_code (int n)
fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
&ex, false, BFD_RELOC_RISCV_ALIGN);

riscv_mapping_state (MAP_INSN, worst_case_bytes);
riscv_mapping_state (MAP_INSN, worst_case_bytes, 1/* frag_align_code */);

/* We need to start a new frag after the alignment which may be removed by
the linker, to prevent the assembler from computing static offsets.
Expand Down Expand Up @@ -4201,10 +4293,10 @@ riscv_init_frag (fragS * fragP, int max_chars)
case rs_fill:
case rs_align:
case rs_align_test:
riscv_mapping_state (MAP_DATA, max_chars);
riscv_mapping_state (MAP_DATA, max_chars, 0/* frag_align_code */);
break;
case rs_align_code:
riscv_mapping_state (MAP_INSN, max_chars);
riscv_mapping_state (MAP_INSN, max_chars, 1/* frag_align_code */);
break;
default:
break;
Expand Down Expand Up @@ -4436,6 +4528,7 @@ void
riscv_elf_final_processing (void)
{
riscv_set_abi_by_arch ();
riscv_release_subset_list (riscv_rps_as.subset_list);
elf_elfheader (stdoutput)->e_flags |= elf_flags;
}

Expand Down Expand Up @@ -4477,7 +4570,7 @@ s_riscv_insn (int x ATTRIBUTE_UNUSED)
save_c = *input_line_pointer;
*input_line_pointer = '\0';

riscv_mapping_state (MAP_INSN, 0);
riscv_mapping_state (MAP_INSN, 0, 0/* frag_align_code */);

struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
&imm_reloc, insn_type_hash);
Expand Down Expand Up @@ -4520,9 +4613,8 @@ riscv_write_out_attrs (void)
unsigned int i;

/* Re-write architecture elf attribute. */
arch_str = riscv_arch_str (xlen, riscv_rps_as.subset_list);
arch_str = riscv_rps_as.subset_list->arch_str;
bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
xfree ((void *) arch_str);

/* For the file without any instruction, we don't set the default_priv_spec
according to the privileged elf attributes since the md_assemble isn't
Expand Down
6 changes: 4 additions & 2 deletions gas/config/tc-riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,16 @@ extern void riscv_md_finish (void);
extern int riscv_convert_symbolic_attribute (const char *);

/* Set mapping symbol states. */
#define md_cons_align(nbytes) riscv_mapping_state (MAP_DATA, 0)
void riscv_mapping_state (enum riscv_seg_mstate, int);
#define md_cons_align(nbytes) riscv_mapping_state (MAP_DATA, 0, 0)
void riscv_mapping_state (enum riscv_seg_mstate, int, bool);

/* Define target segment type. */
#define TC_SEGMENT_INFO_TYPE struct riscv_segment_info_type
struct riscv_segment_info_type
{
enum riscv_seg_mstate map_state;
/* The current mapping symbol with architecture string. */
symbolS *arch_map_symbol;
};

/* Define target fragment type. */
Expand Down
Loading

0 comments on commit 40f1a1a

Please sign in to comment.