aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-tic6x.c
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2010-04-27 23:43:25 +0000
committerJoseph Myers <joseph@codesourcery.com>2010-04-27 23:43:25 +0000
commitd99e5b3995477e20acc8eb9b1e0427a8c0f7f993 (patch)
treea81cb178e7be0faf99a20c812978aaebaf18a36c /gas/config/tc-tic6x.c
parent67f1675e78ba8e08a782ee57486cde949409dd2a (diff)
downloadgdb-d99e5b3995477e20acc8eb9b1e0427a8c0f7f993.zip
gdb-d99e5b3995477e20acc8eb9b1e0427a8c0f7f993.tar.gz
gdb-d99e5b3995477e20acc8eb9b1e0427a8c0f7f993.tar.bz2
gas:
* config/tc-tic6x.h (tic6x_label_list): New. (tic6x_segment_info_type): Keep a list of labels and a current frag instead of a boolean for whether labels seen and a count of instructions. (tic6x_frag_info, TC_FRAG_TYPE, TC_FRAG_INIT, tic6x_frag_init, md_do_align, tic6x_do_align, md_end, tic6x_end): New. * config/tc-tic6x.c (tic6x_frob_label): Put label on list. (tic6x_cleanup): Correct comment. (tic6x_free_label_list): New. (tic6x_cons_align): Free label list and update for tic6x_segment_info_type changes. (tic6x_do_align): New. (md_assemble): Handle list of labels and saved frag for execute packet. Create machine-dependent frag for new execute packet and adjust labels accordingly. (tic6x_adjust_section, tic6x_frag_init, tic6x_end): New. (md_convert_frag, md_estimate_size_before_relax): Update comments. gas/testsuite: * gas/tic6x/align-1-be.d, gas/tic6x/align-1.d, gas/tic6x/align-1.s, gas/tic6x/align-2.d, gas/tic6x/align-2.s: New.
Diffstat (limited to 'gas/config/tc-tic6x.c')
-rw-r--r--gas/config/tc-tic6x.c454
1 files changed, 435 insertions, 19 deletions
diff --git a/gas/config/tc-tic6x.c b/gas/config/tc-tic6x.c
index d746f94..5e63c8b 100644
--- a/gas/config/tc-tic6x.c
+++ b/gas/config/tc-tic6x.c
@@ -465,8 +465,11 @@ tic6x_unrecognized_line (int c)
/* Do any target-specific handling of a label required. */
void
-tic6x_frob_label (symbolS *sym ATTRIBUTE_UNUSED)
+tic6x_frob_label (symbolS *sym)
{
+ segment_info_type *si;
+ tic6x_label_list *list;
+
if (tic6x_line_parallel)
{
as_bad (_("label after '||'"));
@@ -480,7 +483,11 @@ tic6x_frob_label (symbolS *sym ATTRIBUTE_UNUSED)
tic6x_line_z = 0;
}
- seg_info (now_seg)->tc_segment_info_data.seen_label = TRUE;
+ si = seg_info (now_seg);
+ list = si->tc_segment_info_data.label_list;
+ si->tc_segment_info_data.label_list = xmalloc (sizeof (tic6x_label_list));
+ si->tc_segment_info_data.label_list->next = list;
+ si->tc_segment_info_data.label_list->label = sym;
/* Defining tc_frob_label overrides the ELF definition of
obj_frob_label, so we need to apply its effects here. */
@@ -515,8 +522,9 @@ tic6x_start_line_hook (void)
tic6x_end_of_line ();
}
-/* Do target-specific handling immediately after all input files have
- been read. */
+/* Do target-specific handling immediately after an input file from
+ the command line, and any other inputs it includes, have been
+ read. */
void
tic6x_cleanup (void)
@@ -533,6 +541,20 @@ tic6x_init_after_args (void)
elf32_tic6x_set_use_rela_p (stdoutput, tic6x_generate_rela);
}
+/* Free LIST of labels (possibly NULL). */
+
+static void
+tic6x_free_label_list (tic6x_label_list *list)
+{
+ while (list)
+ {
+ tic6x_label_list *old = list;
+
+ list = list->next;
+ free (old);
+ }
+}
+
/* Handle a data alignment of N bytes. */
void
@@ -542,12 +564,63 @@ tic6x_cons_align (int n ATTRIBUTE_UNUSED)
/* Data means there is no current execute packet, and that any label
applies to that data rather than a subsequent instruction. */
- seginfo->tc_segment_info_data.num_execute_packet_insns = 0;
- seginfo->tc_segment_info_data.seen_label = FALSE;
+ tic6x_free_label_list (seginfo->tc_segment_info_data.label_list);
+ seginfo->tc_segment_info_data.label_list = NULL;
+ seginfo->tc_segment_info_data.execute_packet_frag = NULL;
seginfo->tc_segment_info_data.last_insn_lsb = NULL;
seginfo->tc_segment_info_data.spmask_addr = NULL;
}
+/* Handle an alignment directive. Return TRUE if the
+ machine-independent frag generation should be skipped. */
+
+bfd_boolean
+tic6x_do_align (int n, char *fill, int len ATTRIBUTE_UNUSED, int max)
+{
+ /* Given code alignments of 4, 8, 16 or 32 bytes, we try to handle
+ them in the md_end pass by inserting NOPs in parallel with
+ previous instructions. We only do this in sections containing
+ nothing but instructions. Code alignments of 1 or 2 bytes have
+ no effect in such sections (but we record them with
+ machine-dependent frags anyway so they can be skipped or
+ converted to machine-independent), while those of more than 64
+ bytes cannot reliably be handled in this way. */
+ if (n > 0
+ && max >= 0
+ && max < (1 << n)
+ && !need_pass_2
+ && fill == NULL
+ && subseg_text_p (now_seg))
+ {
+ fragS *align_frag;
+ char *p;
+
+ if (n > 5)
+ return FALSE;
+
+ /* Machine-independent code would generate a frag here, but we
+ wish to handle it in a machine-dependent way. */
+ if (frag_now_fix () != 0)
+ {
+ if (frag_now->fr_type != rs_machine_dependent)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+ frag_grow (32);
+ align_frag = frag_now;
+ p = frag_var (rs_machine_dependent, 32, 32, max, NULL, n, NULL);
+ /* This must be the same as the frag to which a pointer was just
+ saved. */
+ if (p != align_frag->fr_literal)
+ abort ();
+ align_frag->tc_frag_data.is_insns = FALSE;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
/* Types of operand for parsing purposes. These are used as bit-masks
to tell tic6x_parse_operand what forms of operand are
permitted. */
@@ -2474,7 +2547,7 @@ md_assemble (char *str)
bfd_boolean this_line_spmask;
unsigned int this_line_creg;
unsigned int this_line_z;
- bfd_boolean this_insn_label;
+ tic6x_label_list *this_insn_label_list;
segment_info_type *seginfo;
tic6x_opcode_list *opc_list, *opc;
tic6x_func_unit_base func_unit_base = tic6x_func_unit_nfu;
@@ -2502,6 +2575,7 @@ md_assemble (char *str)
int fix_pcrel = 0;
bfd_reloc_code_real_type fx_r_type = BFD_RELOC_UNUSED;
bfd_boolean fix_adda = FALSE;
+ fragS *insn_frag;
char *output;
p = str;
@@ -2524,8 +2598,8 @@ md_assemble (char *str)
tic6x_line_creg = 0;
tic6x_line_z = 0;
seginfo = seg_info (now_seg);
- this_insn_label = seginfo->tc_segment_info_data.seen_label;
- seginfo->tc_segment_info_data.seen_label = FALSE;
+ this_insn_label_list = seginfo->tc_segment_info_data.label_list;
+ seginfo->tc_segment_info_data.label_list = NULL;
opc_list = hash_find_n (opcode_hash, str, p - str);
if (opc_list == NULL)
@@ -2979,19 +3053,20 @@ md_assemble (char *str)
if (this_line_parallel)
{
- if (seginfo->tc_segment_info_data.num_execute_packet_insns == 0)
+ insn_frag = seginfo->tc_segment_info_data.execute_packet_frag;
+ if (insn_frag == NULL)
{
as_bad (_("parallel instruction not following another instruction"));
return;
}
- if (seginfo->tc_segment_info_data.num_execute_packet_insns >= 8)
+ if (insn_frag->fr_fix >= 32)
{
as_bad (_("too many instructions in execute packet"));
return;
}
- if (this_insn_label)
+ if (this_insn_label_list != NULL)
as_bad (_("label not at start of execute packet"));
if (opct->flags & TIC6X_FLAG_FIRST)
@@ -2999,11 +3074,40 @@ md_assemble (char *str)
opc_len, str);
*seginfo->tc_segment_info_data.last_insn_lsb |= 0x1;
+ output = insn_frag->fr_literal + insn_frag->fr_fix;
}
else
{
- seginfo->tc_segment_info_data.num_execute_packet_insns = 0;
+ tic6x_label_list *l;
+
seginfo->tc_segment_info_data.spmask_addr = NULL;
+
+ /* Start a new frag for this execute packet. */
+ if (frag_now_fix () != 0)
+ {
+ if (frag_now->fr_type != rs_machine_dependent)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+ frag_grow (32);
+ insn_frag = seginfo->tc_segment_info_data.execute_packet_frag = frag_now;
+ for (l = this_insn_label_list; l; l = l->next)
+ {
+ symbol_set_frag (l->label, frag_now);
+ S_SET_VALUE (l->label, 0);
+ S_SET_SEGMENT (l->label, now_seg);
+ }
+ tic6x_free_label_list (this_insn_label_list);
+ dwarf2_emit_insn (0);
+ output = frag_var (rs_machine_dependent, 32, 32, 0, NULL, 0, NULL);
+ /* This must be the same as the frag to which a pointer was just
+ saved. */
+ if (output != insn_frag->fr_literal)
+ abort ();
+ insn_frag->tc_frag_data.is_insns = TRUE;
+ insn_frag->tc_frag_data.can_cross_fp_boundary
+ = tic6x_can_cross_fp_boundary;
}
if (opct->flags & TIC6X_FLAG_SPLOOP)
@@ -3050,17 +3154,16 @@ md_assemble (char *str)
}
record_alignment (now_seg, 5);
- output = frag_more (4);
md_number_to_chars (output, opcode_value, 4);
if (fix_needed)
- tic6x_fix_new_exp (frag_now, output - frag_now->fr_literal, 4, fix_exp,
+ tic6x_fix_new_exp (insn_frag, output - insn_frag->fr_literal, 4, fix_exp,
fix_pcrel, fx_r_type, fix_adda);
- seginfo->tc_segment_info_data.num_execute_packet_insns++;
+ insn_frag->fr_fix += 4;
+ insn_frag->fr_var -= 4;
seginfo->tc_segment_info_data.last_insn_lsb
= (target_big_endian ? output + 3 : output);
if (opct->flags & TIC6X_FLAG_SPMASK)
seginfo->tc_segment_info_data.spmask_addr = output;
- dwarf2_emit_insn (4);
}
/* Modify NEWVAL (32-bit) by inserting VALUE, shifted right by SHIFT
@@ -3363,7 +3466,319 @@ md_atof (int type, char *litP, int *sizeP)
return ieee_md_atof (type, litP, sizeP, target_big_endian);
}
-/* No machine-dependent frags yet. */
+/* Adjust the frags in SECTION (see tic6x_end). */
+
+static void
+tic6x_adjust_section (bfd *abfd ATTRIBUTE_UNUSED, segT section,
+ void *dummy ATTRIBUTE_UNUSED)
+{
+ segment_info_type *info;
+ frchainS *frchp;
+ fragS *fragp;
+ bfd_boolean have_code = FALSE;
+ bfd_boolean have_non_code = FALSE;
+
+ info = seg_info (section);
+ if (info == NULL)
+ return;
+
+ for (frchp = info->frchainP; frchp; frchp = frchp->frch_next)
+ for (fragp = frchp->frch_root; fragp; fragp = fragp->fr_next)
+ switch (fragp->fr_type)
+ {
+ case rs_machine_dependent:
+ if (fragp->tc_frag_data.is_insns)
+ have_code = TRUE;
+ break;
+
+ case rs_dummy:
+ case rs_fill:
+ if (fragp->fr_fix > 0)
+ have_non_code = TRUE;
+ break;
+
+ default:
+ have_non_code = TRUE;
+ break;
+ }
+
+ /* Process alignment requirements in a code-only section. */
+ if (have_code && !have_non_code)
+ {
+ /* If we need to insert an odd number of instructions to meet an
+ alignment requirement, there must have been an odd number of
+ instructions since the last 8-byte-aligned execute packet
+ boundary. So there must have been an execute packet with an
+ odd number (and so a number fewer than 8) of instructions
+ into which we can insert a NOP without breaking any previous
+ alignments.
+
+ If then we need to insert a number 2 mod 4 of instructions,
+ the number of instructions since the last 16-byte-aligned
+ execute packet boundary must be 2 mod 4. So between that
+ boundary and the following 8-byte-aligned boundary there must
+ either be at least one execute packet with 2-mod-4
+ instructions, or at least two with an odd number of
+ instructions; again, greedily inserting NOPs as soon as
+ possible suffices to meet the alignment requirement.
+
+ If then we need to insert 4 instructions, we look between the
+ last 32-byte-aligned boundary and the following
+ 16-byte-aligned boundary. The sizes of the execute packets
+ in this range total 4 instructions mod 8, so again there is
+ room for greedy insertion of NOPs to meet the alignment
+ requirement, and before any intermediate point with 8-byte
+ (2-instruction) alignment requirement the sizes of execute
+ packets (and so the room for NOPs) will total 2 instructions
+ mod 4 so greedy insertion will not break such alignments.
+
+ So we can always meet these alignment requirements by
+ inserting NOPs in parallel with existing execute packets, and
+ by induction the approach described above inserts the minimum
+ number of such NOPs. */
+
+ /* The number of NOPs we are currently looking to insert, if we
+ have gone back to insert NOPs. */
+ unsigned int want_insert = 0;
+
+ /* Out of that number, the number inserted so far in the current
+ stage of the above algorithm. */
+ unsigned int want_insert_done_so_far = 0;
+
+ /* The position mod 32 at the start of the current frag. */
+ unsigned int pos = 0;
+
+ /* The locations in the frag chain of the most recent frags at
+ the start of which there is the given alignment. */
+ frchainS *frchp_last32, *frchp_last16, *frchp_last8;
+ fragS *fragp_last32, *fragp_last16, *fragp_last8;
+ unsigned int pos_last32, pos_last16, pos_last8;
+
+ frchp_last32 = frchp_last16 = frchp_last8 = info->frchainP;
+ fragp_last32 = fragp_last16 = fragp_last8 = info->frchainP->frch_root;
+ pos_last32 = pos_last16 = pos_last8 = 0;
+
+ for (frchp = info->frchainP; frchp; frchp = frchp->frch_next)
+ for (fragp = frchp->frch_root; fragp; fragp = fragp->fr_next)
+ look_at_frag:
+ {
+ bfd_boolean go_back = FALSE;
+ frchainS *frchp_next;
+ fragS *fragp_next;
+
+ if (fragp->fr_type != rs_machine_dependent)
+ continue;
+
+ if (fragp->tc_frag_data.is_insns
+ && pos + fragp->fr_fix > 32
+ && !fragp->tc_frag_data.can_cross_fp_boundary)
+ {
+ /* As described above, we should always have met an
+ alignment requirement by the time we come back to
+ it. */
+ if (want_insert)
+ abort ();
+
+ if (pos & 3)
+ abort ();
+ want_insert = (32 - pos) >> 2;
+ if (want_insert > 7)
+ abort ();
+ want_insert_done_so_far = 0;
+ go_back = TRUE;
+ }
+
+ if (!fragp->tc_frag_data.is_insns)
+ {
+ unsigned int would_insert_bytes;
+
+ if (!(pos & ((1 << fragp->fr_offset) - 1)))
+ /* This alignment requirement is already met. */
+ continue;
+
+ /* As described above, we should always have met an
+ alignment requirement by the time we come back to
+ it. */
+ if (want_insert)
+ abort ();
+
+ /* We may not be able to meet this requirement within
+ the given number of characters. */
+ would_insert_bytes
+ = ((1 << fragp->fr_offset)
+ - (pos & ((1 << fragp->fr_offset) - 1)));
+
+ if (fragp->fr_subtype != 0
+ && would_insert_bytes > fragp->fr_subtype)
+ continue;
+
+ /* An unmet alignment must be 8, 16 or 32 bytes;
+ smaller ones must always be met within code-only
+ sections and larger ones cause the section not to
+ be code-only. */
+ if (fragp->fr_offset != 3
+ && fragp->fr_offset != 4
+ && fragp->fr_offset != 5)
+ abort ();
+
+ if (would_insert_bytes & 3)
+ abort ();
+ want_insert = would_insert_bytes >> 2;
+ if (want_insert > 7)
+ abort ();
+ want_insert_done_so_far = 0;
+ go_back = TRUE;
+ }
+ else if (want_insert && !go_back)
+ {
+ unsigned int num_insns = fragp->fr_fix >> 2;
+ unsigned int max_poss_nops = 8 - num_insns;
+
+ if (max_poss_nops)
+ {
+ unsigned int cur_want_nops, max_want_nops, do_nops, i;
+
+ if (want_insert & 1)
+ cur_want_nops = 1;
+ else if (want_insert & 2)
+ cur_want_nops = 2;
+ else if (want_insert & 4)
+ cur_want_nops = 4;
+ else
+ abort ();
+
+ max_want_nops = cur_want_nops - want_insert_done_so_far;
+
+ do_nops = (max_poss_nops < max_want_nops
+ ? max_poss_nops
+ : max_want_nops);
+ for (i = 0; i < do_nops; i++)
+ {
+ md_number_to_chars (fragp->fr_literal + fragp->fr_fix,
+ 0, 4);
+ if (target_big_endian)
+ fragp->fr_literal[fragp->fr_fix - 1] |= 0x1;
+ else
+ fragp->fr_literal[fragp->fr_fix - 4] |= 0x1;
+ fragp->fr_fix += 4;
+ fragp->fr_var -= 4;
+ }
+ want_insert_done_so_far += do_nops;
+ if (want_insert_done_so_far == cur_want_nops)
+ {
+ want_insert -= want_insert_done_so_far;
+ want_insert_done_so_far = 0;
+ if (want_insert)
+ go_back = TRUE;
+ }
+ }
+ }
+ if (go_back)
+ {
+ if (want_insert & 1)
+ {
+ frchp = frchp_last8;
+ fragp = fragp_last8;
+ pos = pos_last8;
+ }
+ else if (want_insert & 2)
+ {
+ frchp = frchp_last8 = frchp_last16;
+ fragp = fragp_last8 = fragp_last16;
+ pos = pos_last8 = pos_last16;
+ }
+ else if (want_insert & 4)
+ {
+ frchp = frchp_last8 = frchp_last16 = frchp_last32;
+ fragp = fragp_last8 = fragp_last16 = fragp_last32;
+ pos = pos_last8 = pos_last16 = pos_last32;
+ }
+ else
+ abort ();
+
+ goto look_at_frag;
+ }
+
+ /* Update current position for moving past a code
+ frag. */
+ pos += fragp->fr_fix;
+ pos &= 31;
+ frchp_next = frchp;
+ fragp_next = fragp->fr_next;
+ if (fragp_next == NULL)
+ {
+ frchp_next = frchp->frch_next;
+ if (frchp_next != NULL)
+ fragp_next = frchp_next->frch_root;
+ }
+ if (!(pos & 7))
+ {
+ frchp_last8 = frchp_next;
+ fragp_last8 = fragp_next;
+ pos_last8 = pos;
+ }
+ if (!(pos & 15))
+ {
+ frchp_last16 = frchp_next;
+ fragp_last16 = fragp_next;
+ pos_last16 = pos;
+ }
+ if (!(pos & 31))
+ {
+ frchp_last32 = frchp_next;
+ fragp_last32 = fragp_next;
+ pos_last32 = pos;
+ }
+ }
+ }
+
+ /* Now convert the machine-dependent frags to machine-independent
+ ones. */
+ for (frchp = info->frchainP; frchp; frchp = frchp->frch_next)
+ for (fragp = frchp->frch_root; fragp; fragp = fragp->fr_next)
+ {
+ if (fragp->fr_type == rs_machine_dependent)
+ {
+ if (fragp->tc_frag_data.is_insns)
+ frag_wane (fragp);
+ else
+ {
+ fragp->fr_type = rs_align_code;
+ fragp->fr_var = 1;
+ *fragp->fr_literal = 0;
+ }
+ }
+ }
+}
+
+/* Initialize the machine-dependent parts of a frag. */
+
+void
+tic6x_frag_init (fragS *fragp)
+{
+ fragp->tc_frag_data.is_insns = FALSE;
+ fragp->tc_frag_data.can_cross_fp_boundary = FALSE;
+}
+
+/* Do machine-dependent manipulations of the frag chains after all
+ input has been read and before the machine-independent sizing and
+ relaxing. */
+
+void
+tic6x_end (void)
+{
+ /* Meeting alignment requirements may require inserting NOPs in
+ parallel in execute packets earlier in the segment. Future
+ 16-bit instruction generation involves whole-segment optimization
+ to determine the best choice and ordering of 32-bit or 16-bit
+ instructions. This doesn't fit will in the general relaxation
+ framework, so handle alignment and 16-bit instruction generation
+ here. */
+ bfd_map_over_sections (stdoutput, tic6x_adjust_section, NULL);
+}
+
+/* No machine-dependent frags at this stage; all converted in
+ tic6x_end. */
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED,
@@ -3372,7 +3787,8 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED,
abort ();
}
-/* No machine-dependent frags yet. */
+/* No machine-dependent frags at this stage; all converted in
+ tic6x_end. */
int
md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,