This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[patch] rx .fetchalign pseudo
- From: DJ Delorie <dj at redhat dot com>
- To: binutils at sourceware dot org
- Date: Mon, 14 May 2012 23:04:49 -0400
- Subject: [patch] rx .fetchalign pseudo
The RX chip incurs a one-cycle penalty if you branch to an opcode that
spans a fetch boundary (8 bytes). This new pseudo acts like a
.p2align with a max-skip one byte less than the size of the next
opcode, so that the next opcode (and any interceeding labels, of
course) will be known to not span a fetch boundary.
Committed.
* config/tc-rx.c (rx_fetchalign): Declare.
(md_pseudo_table): Add .fetchalign.
(RX_NBASE_FETCHALIGN): New.
(fetchalign_bytes): New.
(rx_fetchalign): New.
(rx_frag_init): If a "magic" value is found, also init the
machine-specific data.
(md_assemble): Note following opcode size if called for.
(rx_next_opcode): New.
(rx_relax_frag): Support .fetchalign.
(md_convert_frag): Likewise.
* doc/c-rx.texi (RX-Directives): Add .fetchalign.
Index: gas/config/tc-rx.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-rx.c,v
retrieving revision 1.11
diff -p -U5 -r1.11 gas/config/tc-rx.c
--- gas/config/tc-rx.c 5 Oct 2011 14:13:28 -0000 1.11
+++ gas/config/tc-rx.c 15 May 2012 02:43:14 -0000
@@ -54,10 +54,12 @@ static bfd_boolean rx_use_small_data_lim
static bfd_boolean rx_pid_mode = FALSE;
static int rx_num_int_regs = 0;
int rx_pid_register;
int rx_gp_register;
+static void rx_fetchalign (int ignore ATTRIBUTE_UNUSED);
+
enum options
{
OPTION_BIG = OPTION_MD_BASE,
OPTION_LITTLE,
OPTION_32BIT_DOUBLES,
@@ -598,10 +600,12 @@ const pseudo_typeS md_pseudo_table[] =
{ "bss", s_bss, 0 },
{ "3byte", cons, 3 },
{ "int", cons, 4 },
{ "word", cons, 4 },
+ { "fetchalign", rx_fetchalign, 0 },
+
/* End of list marker. */
{ NULL, NULL, 0 }
};
static asymbol * gp_symbol;
@@ -646,13 +650,18 @@ md_begin (void)
}
char * rx_lex_start;
char * rx_lex_end;
+/* These negative numbers are found in rx_bytesT.n_base for non-opcode
+ md_frags */
+#define RX_NBASE_FETCHALIGN -1
+
typedef struct rx_bytesT
{
char base[4];
+ /* If this is negative, it's a special-purpose frag as per the defines above. */
int n_base;
char ops[8];
int n_ops;
struct
{
@@ -676,10 +685,35 @@ typedef struct rx_bytesT
char times_grown;
char times_shrank;
} rx_bytesT;
static rx_bytesT rx_bytes;
+/* We set n_ops to be "size of next opcode" if the next opcode doesn't relax. */
+static rx_bytesT *fetchalign_bytes = NULL;
+
+static void
+rx_fetchalign (int ignore ATTRIBUTE_UNUSED)
+{
+ char * bytes;
+ fragS * frag_then;
+
+ memset (& rx_bytes, 0, sizeof (rx_bytes));
+ rx_bytes.n_base = RX_NBASE_FETCHALIGN;
+
+ bytes = frag_more (8);
+ frag_then = frag_now;
+ frag_variant (rs_machine_dependent,
+ 0 /* max_chars */,
+ 0 /* var */,
+ 0 /* subtype */,
+ 0 /* symbol */,
+ 0 /* offset */,
+ 0 /* opcode */);
+ frag_then->fr_opcode = bytes;
+ frag_then->fr_subtype = 0;
+ fetchalign_bytes = frag_then->tc_frag_data;
+}
void
rx_relax (int type, int pos)
{
rx_bytes.relax[rx_bytes.n_relax].type = type;
@@ -931,11 +965,11 @@ rx_wrap (void)
}
void
rx_frag_init (fragS * fragP)
{
- if (rx_bytes.n_relax || rx_bytes.link_relax)
+ if (rx_bytes.n_relax || rx_bytes.link_relax || rx_bytes.n_base < 0)
{
fragP->tc_frag_data = malloc (sizeof (rx_bytesT));
memcpy (fragP->tc_frag_data, & rx_bytes, sizeof (rx_bytesT));
}
else
@@ -1047,12 +1081,16 @@ md_assemble (char * str)
}
else
{
bytes = frag_more (rx_bytes.n_base + rx_bytes.n_ops);
frag_then = frag_now;
+ if (fetchalign_bytes)
+ fetchalign_bytes->n_ops = rx_bytes.n_base + rx_bytes.n_ops;
}
+ fetchalign_bytes = NULL;
+
APPEND (base, n_base);
APPEND (ops, n_ops);
if (rx_bytes.link_relax && rx_bytes.n_fixups)
{
@@ -1411,10 +1449,22 @@ md_estimate_size_before_relax (fragS * f
tprintf (" -> opfixsize %d delta %d\n", opfixsize, delta);
return delta;
}
+/* Given a frag FRAGP, return the "next" frag that contains an
+ opcode. Assumes the next opcode is relaxable, and thus rs_machine_dependent. */
+
+static fragS *
+rx_next_opcode (fragS *fragP)
+{
+ do {
+ fragP = fragP->fr_next;
+ } while (fragP && fragP->fr_type != rs_machine_dependent);
+ return fragP;
+}
+
/* Given the new addresses for this relax pass, figure out how big
each opcode must be. We store the total number of bytes needed in
fr_subtype. The return value is the difference between the size
after the last pass and the size after this pass, so we use the old
fr_subtype to calculate the difference. */
@@ -1435,10 +1485,38 @@ rx_relax_frag (segT segment ATTRIBUTE_UN
(unsigned long) (fragP->fr_address
+ (fragP->fr_opcode - fragP->fr_literal)),
(long) fragP->fr_fix, (long) fragP->fr_var, (long) fragP->fr_offset,
fragP->fr_literal, fragP->fr_opcode, fragP->fr_type, fragP->fr_subtype, stretch);
+ mypc = fragP->fr_address + (fragP->fr_opcode - fragP->fr_literal);
+
+ if (fragP->tc_frag_data->n_base == RX_NBASE_FETCHALIGN)
+ {
+ unsigned int next_size;
+ if (fragP->fr_next == NULL)
+ return 0;
+
+ next_size = fragP->tc_frag_data->n_ops;
+ if (next_size == 0)
+ {
+ fragS *n = rx_next_opcode (fragP);
+ next_size = n->fr_subtype;
+ }
+
+ fragP->fr_subtype = (8-(mypc & 7)) & 7;
+ tprintf("subtype %u\n", fragP->fr_subtype);
+ if (fragP->fr_subtype >= next_size)
+ fragP->fr_subtype = 0;
+ tprintf ("\033[34m -> mypc %lu next_size %u new %d old %d delta %d (fetchalign)\033[0m\n",
+ mypc & 7,
+ next_size, fragP->fr_subtype, oldsize, fragP->fr_subtype-oldsize);
+
+ newsize = fragP->fr_subtype;
+
+ return newsize - oldsize;
+ }
+
optype = rx_opcode_type (fragP->fr_opcode);
/* In the one case where we have both a disp and imm relaxation, we want
the imm relaxation here. */
ri = 0;
@@ -1483,11 +1561,10 @@ rx_relax_frag (segT segment ATTRIBUTE_UN
fragP->fr_subtype = newsize;
tprintf (" -> new %d old %d delta %d (external)\n", newsize, oldsize, newsize-oldsize);
return newsize - oldsize;
}
- mypc = fragP->fr_address + (fragP->fr_opcode - fragP->fr_literal);
if (sym_addr > mypc)
addr0 += stretch;
switch (fragP->tc_frag_data->relax[ri].type)
{
@@ -1642,17 +1719,33 @@ md_convert_frag (bfd * abfd ATTRIBUTE_
#if TRACE_RELAX
{
int i;
- printf ("lit %08x opc %08x", (int) fragP->fr_literal, (int) fragP->fr_opcode);
+ printf ("lit 0x%p opc 0x%p", fragP->fr_literal, fragP->fr_opcode);
for (i = 0; i < 10; i++)
printf (" %02x", (unsigned char) (fragP->fr_opcode[i]));
printf ("\n");
}
#endif
+ if (fragP->tc_frag_data->n_base == RX_NBASE_FETCHALIGN)
+ {
+ int count = fragP->fr_subtype;
+ if (count == 0)
+ ;
+ else if (count > BIGGEST_NOP)
+ {
+ op[0] = 0x2e;
+ op[1] = count;
+ }
+ else if (count > 0)
+ {
+ memcpy (op, nops[count], count);
+ }
+ }
+
/* In the one case where we have both a disp and imm relaxation, we want
the imm relaxation here. */
ri = 0;
if (fragP->tc_frag_data->n_relax > 1
&& fragP->tc_frag_data->relax[0].type == RX_RELAX_DISP)
Index: gas/doc/c-rx.texi
===================================================================
RCS file: /cvs/src/src/gas/doc/c-rx.texi,v
retrieving revision 1.3
diff -p -U5 -r1.3 gas/doc/c-rx.texi
--- gas/doc/c-rx.texi 5 Oct 2011 14:13:28 -0000 1.3
+++ gas/doc/c-rx.texi 15 May 2012 02:43:15 -0000
@@ -153,10 +153,20 @@ assembler directives:
@item .3byte
@cindex assembler directive .3byte, RX
@cindex RX assembler directive .3byte
Inserts a 3-byte value into the output file at the current location.
+@item .fetchalign
+@cindex assembler directive .fetchalign, RX
+@cindex RX assembler directive .fetchalign
+If the next opcode following this directive spans a fetch line
+boundary (8 byte boundary), the opcode is aligned to that boundary.
+If the next opcode does not span a fetch line, this directive has no
+effect. Note that one or more labels may be between this directive
+and the opcode; those labels are aligned as well. Any inserted bytes
+due to alignment will form a NOP opcode.
+
@end table
@node RX-Float
@section Floating Point