aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-riscv.c
diff options
context:
space:
mode:
authorNelson Chu <nelson@rivosinc.com>2022-09-29 19:07:46 +0800
committerNelson Chu <nelson@rivosinc.com>2022-10-28 11:11:23 +0800
commit40f1a1a4564b2e5822cf19a43a8c15154aa4c488 (patch)
tree35f083a2767845714fce545b2789d0b903fa41b5 /gas/config/tc-riscv.c
parent58b2ba6d888019278887286e3896ff7ffd12cb2f (diff)
downloadgdb-40f1a1a4564b2e5822cf19a43a8c15154aa4c488.zip
gdb-40f1a1a4564b2e5822cf19a43a8c15154aa4c488.tar.gz
gdb-40f1a1a4564b2e5822cf19a43a8c15154aa4c488.tar.bz2
RISC-V: Output mapping symbols with ISA string.
RISC-V Psabi pr196, https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/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.
Diffstat (limited to 'gas/config/tc-riscv.c')
-rw-r--r--gas/config/tc-riscv.c162
1 files changed, 127 insertions, 35 deletions
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 22385d1..91cff7d 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -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
{
@@ -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"))
@@ -457,21 +470,37 @@ 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 ();
@@ -479,32 +508,38 @@ make_mapping_symbol (enum riscv_seg_mstate state,
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;
}
@@ -513,13 +548,15 @@ 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. */
@@ -527,13 +564,29 @@ riscv_mapping_state (enum riscv_seg_mstate to_state,
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. */
@@ -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
@@ -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)
@@ -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;
@@ -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);
@@ -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)
@@ -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"))
@@ -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;
@@ -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.
@@ -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;
@@ -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;
}
@@ -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);
@@ -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