New short-conditional-branch packing rules for D10V.

This commit is contained in:
Donald Lindsay
2000-05-25 22:35:05 +00:00
parent f0662e279c
commit 0a44c2b16f
2 changed files with 93 additions and 46 deletions

View File

@ -1,3 +1,12 @@
Thu May 4 15:27:07 2000 Donald Lindsay <dlindsay@cygnus.com>
* config/tc-d10v.c (write_2_short, parallel_ok, md_assemble,
d10v_cleanup) implement Mitsubishi's newly explained branch-packing
rules, with warning when a GAS statement specifies a packing that
will result in an instruction being squashed.
Added typdef packing_type and enumerals, changed various integer literals
to use the enumerals.
2000-05-24 David Mosberger <davidm@hpl.hp.com> 2000-05-24 David Mosberger <davidm@hpl.hp.com>
* config/tc-ia64.c (dot_restorereg_p): New function. * config/tc-ia64.c (dot_restorereg_p): New function.

View File

@ -64,6 +64,13 @@ static Fixups *fixups;
static int do_not_ignore_hash = 0; static int do_not_ignore_hash = 0;
typedef int packing_type;
#define PACK_UNSPEC (0) /* packing order not specified */
#define PACK_PARALLEL (1) /* "||" */
#define PACK_LEFT_RIGHT (2) /* "->" */
#define PACK_RIGHT_LEFT (3) /* "<-" */
static packing_type etype = PACK_UNSPEC; /* used by d10v_cleanup */
/* True if instruction swapping warnings should be inhibited. */ /* True if instruction swapping warnings should be inhibited. */
static unsigned char flag_warn_suppress_instructionswap; /* --nowarnswap */ static unsigned char flag_warn_suppress_instructionswap; /* --nowarnswap */
@ -79,13 +86,13 @@ static unsigned long build_insn PARAMS ((struct d10v_opcode *opcode, expressionS
static void write_long PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx)); static void write_long PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx));
static void write_1_short PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx)); static void write_1_short PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx));
static int write_2_short PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1, static int write_2_short PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1,
struct d10v_opcode *opcode2, unsigned long insn2, int exec_type, Fixups *fx)); struct d10v_opcode *opcode2, unsigned long insn2, packing_type exec_type, Fixups *fx));
static unsigned long do_assemble PARAMS ((char *str, struct d10v_opcode **opcode)); static unsigned long do_assemble PARAMS ((char *str, struct d10v_opcode **opcode));
static unsigned long d10v_insert_operand PARAMS (( unsigned long insn, int op_type, static unsigned long d10v_insert_operand PARAMS (( unsigned long insn, int op_type,
offsetT value, int left, fixS *fix)); offsetT value, int left, fixS *fix));
static int parallel_ok PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1, static int parallel_ok PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1,
struct d10v_opcode *opcode2, unsigned long insn2, struct d10v_opcode *opcode2, unsigned long insn2,
int exec_type)); packing_type exec_type));
static symbolS * find_symbol_matching_register PARAMS ((expressionS *)); static symbolS * find_symbol_matching_register PARAMS ((expressionS *));
struct option md_longopts[] = struct option md_longopts[] =
@ -724,38 +731,40 @@ write_1_short (opcode, insn, fx)
fx->fc = 0; fx->fc = 0;
} }
/* write out a short form instruction if possible */ /* Expects two short instructions.
/* return number of instructions not written out */ If possible, writes out both as a single packed instruction.
Otherwise, writes out the first one, packed with a NOP.
Returns number of instructions not written out. */
static int static int
write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx) write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx)
struct d10v_opcode *opcode1, *opcode2; struct d10v_opcode *opcode1, *opcode2;
unsigned long insn1, insn2; unsigned long insn1, insn2;
int exec_type; packing_type exec_type;
Fixups *fx; Fixups *fx;
{ {
unsigned long insn; unsigned long insn;
char *f; char *f;
int i,j, where; int i,j, where;
if ( (exec_type != 1) && ((opcode1->exec_type & PARONLY) if ( (exec_type != PACK_PARALLEL) && ((opcode1->exec_type & PARONLY)
|| (opcode2->exec_type & PARONLY))) || (opcode2->exec_type & PARONLY)))
as_fatal (_("Instruction must be executed in parallel")); as_fatal (_("Instruction must be executed in parallel"));
if ( (opcode1->format & LONG_OPCODE) || (opcode2->format & LONG_OPCODE)) if ( (opcode1->format & LONG_OPCODE) || (opcode2->format & LONG_OPCODE))
as_fatal (_("Long instructions may not be combined.")); as_fatal (_("Long instructions may not be combined."));
if(opcode1->exec_type & BRANCH_LINK && exec_type == 0)
{
/* Instructions paired with a subroutine call are executed before the
subroutine, so don't do these pairings unless explicitly requested. */
write_1_short (opcode1, insn1, fx->next);
return (1);
}
switch (exec_type) switch (exec_type)
{ {
case 0: /* order not specified */ case PACK_UNSPEC: /* order not specified */
if ( Optimizing && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type)) if (opcode1->exec_type & ALONE)
{
/* Case of a short branch on a separate GAS line. Pack with NOP. */
write_1_short (opcode1, insn1, fx->next);
return 1;
}
if (Optimizing && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type))
{ {
/* parallel */ /* parallel */
if (opcode1->unit == IU) if (opcode1->unit == IU)
@ -765,25 +774,27 @@ write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx)
else else
{ {
insn = FM00 | (insn1 << 15) | insn2; insn = FM00 | (insn1 << 15) | insn2;
/* Advance over dummy fixup since packed insn1 in L */
fx = fx->next; fx = fx->next;
} }
} }
else if (opcode1->unit == IU) else if (opcode1->unit == IU)
{ /* reverse sequential with IU opcode1 on right and done first */
/* reverse sequential */ insn = FM10 | (insn2 << 15) | insn1;
insn = FM10 | (insn2 << 15) | insn1;
}
else else
{ {
/* sequential */ /* sequential with non-IU opcode1 on left and done first */
insn = FM01 | (insn1 << 15) | insn2; insn = FM01 | (insn1 << 15) | insn2;
fx = fx->next; /* Advance over dummy fixup since packed insn1 in L */
fx = fx->next;
} }
break; break;
case 1: /* parallel */
if (opcode1->exec_type & SEQ || opcode2->exec_type & SEQ)
as_fatal (_("One of these instructions may not be executed in parallel."));
case PACK_PARALLEL:
if (opcode1->exec_type & SEQ || opcode2->exec_type & SEQ)
as_fatal
(_("One of these instructions may not be executed in parallel."));
if (opcode1->unit == IU) if (opcode1->unit == IU)
{ {
if (opcode2->unit == IU) if (opcode2->unit == IU)
@ -803,23 +814,31 @@ write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx)
else else
{ {
insn = FM00 | (insn1 << 15) | insn2; insn = FM00 | (insn1 << 15) | insn2;
/* Advance over dummy fixup since packed insn1 in L */
fx = fx->next; fx = fx->next;
} }
break; break;
case 2: /* sequential */
case PACK_LEFT_RIGHT:
if (opcode1->unit != IU) if (opcode1->unit != IU)
insn = FM01 | (insn1 << 15) | insn2; insn = FM01 | (insn1 << 15) | insn2;
else if (opcode2->unit == MU || opcode2->unit == EITHER) else if (opcode2->unit == MU || opcode2->unit == EITHER)
{ {
if (!flag_warn_suppress_instructionswap) if (!flag_warn_suppress_instructionswap)
as_warn (_("Swapping instruction order")); as_warn (_("Swapping instruction order"));
insn = FM10 | (insn2 << 15) | insn1; insn = FM10 | (insn2 << 15) | insn1;
} }
else else
as_fatal (_("IU instruction may not be in the left container")); as_fatal (_("IU instruction may not be in the left container"));
if (opcode1->exec_type & ALONE)
as_warn (_("Instruction in R container is squashed by flow control instruction in L container."));
/* Advance over dummy fixup */
fx = fx->next; fx = fx->next;
break; break;
case 3: /* reverse sequential */
case PACK_RIGHT_LEFT:
if (opcode2->unit != MU) if (opcode2->unit != MU)
insn = FM10 | (insn1 << 15) | insn2; insn = FM10 | (insn1 << 15) | insn2;
else if (opcode1->unit == IU || opcode1->unit == EITHER) else if (opcode1->unit == IU || opcode1->unit == EITHER)
@ -830,15 +849,29 @@ write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx)
} }
else else
as_fatal (_("MU instruction may not be in the right container")); as_fatal (_("MU instruction may not be in the right container"));
if (opcode2->exec_type & ALONE)
as_warn (_("Instruction in R container is squashed by flow control instruction in L container."));
/* Advance over dummy fixup */
fx = fx->next; fx = fx->next;
break; break;
default: default:
as_fatal (_("unknown execution type passed to write_2_short()")); as_fatal (_("unknown execution type passed to write_2_short()"));
} }
f = frag_more(4); f = frag_more(4);
number_to_chars_bigendian (f, insn, 4); number_to_chars_bigendian (f, insn, 4);
/* Process fixup chains.
Note that the packing code above advanced fx conditionally.
dlindsay@cygnus.com: There's something subtle going on here involving
_dummy_first_bfd_reloc_code_real. This is related to the
difference between BFD_RELOC_D10V_10_PCREL_R and _L, ie whether
a fixup is done in the L or R container. A bug in this code
can pass Plum Hall fine, yet still affect hand-written assembler. */
for (j=0; j<2; j++) for (j=0; j<2; j++)
{ {
for (i=0; i < fx->fc; i++) for (i=0; i < fx->fc; i++)
@ -876,7 +909,7 @@ static int
parallel_ok (op1, insn1, op2, insn2, exec_type) parallel_ok (op1, insn1, op2, insn2, exec_type)
struct d10v_opcode *op1, *op2; struct d10v_opcode *op1, *op2;
unsigned long insn1, insn2; unsigned long insn1, insn2;
int exec_type; packing_type exec_type;
{ {
int i, j, flags, mask, shift, regno; int i, j, flags, mask, shift, regno;
unsigned long ins, mod[2], used[2]; unsigned long ins, mod[2], used[2];
@ -889,9 +922,10 @@ parallel_ok (op1, insn1, op2, insn2, exec_type)
|| (op1->unit == MU && op2->unit == MU)) || (op1->unit == MU && op2->unit == MU))
return 0; return 0;
/* If the first instruction is a branch and this is auto parallazation, /* If this is auto parallization, and either instruction is a branch,
don't combine with any second instruction. */ don't parallel. */
if (exec_type == 0 && (op1->exec_type & BRANCH) != 0) if (exec_type == PACK_UNSPEC
&& (op1->exec_type & ALONE || op2->exec_type & ALONE))
return 0; return 0;
/* The idea here is to create two sets of bitmasks (mod and used) /* The idea here is to create two sets of bitmasks (mod and used)
@ -1005,33 +1039,35 @@ static unsigned long prev_insn;
static struct d10v_opcode *prev_opcode = 0; static struct d10v_opcode *prev_opcode = 0;
static subsegT prev_subseg; static subsegT prev_subseg;
static segT prev_seg = 0;; static segT prev_seg = 0;;
static int etype = 0; /* saved extype. used for multiline instructions */
void void
md_assemble (str) md_assemble (str)
char *str; char *str;
{ {
/* etype is saved extype. for multiline instructions */
packing_type extype = PACK_UNSPEC; /* parallel, etc */
struct d10v_opcode * opcode; struct d10v_opcode * opcode;
unsigned long insn; unsigned long insn;
int extype = 0; /* execution type; parallel, etc */
char * str2; char * str2;
if (etype == 0) if (etype == PACK_UNSPEC)
{ {
/* look for the special multiple instruction separators */ /* look for the special multiple instruction separators */
str2 = strstr (str, "||"); str2 = strstr (str, "||");
if (str2) if (str2)
extype = 1; extype = PACK_PARALLEL;
else else
{ {
str2 = strstr (str, "->"); str2 = strstr (str, "->");
if (str2) if (str2)
extype = 2; extype = PACK_LEFT_RIGHT;
else else
{ {
str2 = strstr (str, "<-"); str2 = strstr (str, "<-");
if (str2) if (str2)
extype = 3; extype = PACK_RIGHT_LEFT;
} }
} }
/* str2 points to the separator, if one */ /* str2 points to the separator, if one */
@ -1040,7 +1076,7 @@ md_assemble (str)
*str2 = 0; *str2 = 0;
/* if two instructions are present and we already have one saved /* if two instructions are present and we already have one saved
then first write it out */ then first write out the save one */
d10v_cleanup (); d10v_cleanup ();
/* assemble first instruction and save it */ /* assemble first instruction and save it */
@ -1055,7 +1091,7 @@ md_assemble (str)
insn = do_assemble (str, &opcode); insn = do_assemble (str, &opcode);
if (insn == -1) if (insn == -1)
{ {
if (extype) if (extype != PACK_UNSPEC)
{ {
etype = extype; etype = extype;
return; return;
@ -1063,16 +1099,16 @@ md_assemble (str)
as_fatal (_("can't find opcode ")); as_fatal (_("can't find opcode "));
} }
if (etype) if (etype != PACK_UNSPEC)
{ {
extype = etype; extype = etype;
etype = 0; etype = PACK_UNSPEC;
} }
/* if this is a long instruction, write it and any previous short instruction */ /* if this is a long instruction, write it and any previous short instruction */
if (opcode->format & LONG_OPCODE) if (opcode->format & LONG_OPCODE)
{ {
if (extype) if (extype != PACK_UNSPEC)
as_fatal (_("Unable to mix instructions as specified")); as_fatal (_("Unable to mix instructions as specified"));
d10v_cleanup (); d10v_cleanup ();
write_long (opcode, insn, fixups); write_long (opcode, insn, fixups);
@ -1081,7 +1117,7 @@ md_assemble (str)
} }
if (prev_opcode && prev_seg && ((prev_seg != now_seg) || (prev_subseg != now_subseg))) if (prev_opcode && prev_seg && ((prev_seg != now_seg) || (prev_subseg != now_subseg)))
d10v_cleanup(); d10v_cleanup ();
if (prev_opcode && (write_2_short (prev_opcode, prev_insn, opcode, insn, extype, fixups) == 0)) if (prev_opcode && (write_2_short (prev_opcode, prev_insn, opcode, insn, extype, fixups) == 0))
{ {
@ -1090,7 +1126,7 @@ md_assemble (str)
} }
else else
{ {
if (extype) if (extype != PACK_UNSPEC)
as_fatal (_("Unable to mix instructions as specified")); as_fatal (_("Unable to mix instructions as specified"));
/* save off last instruction so it may be packed on next pass */ /* save off last instruction so it may be packed on next pass */
prev_opcode = opcode; prev_opcode = opcode;
@ -1570,14 +1606,16 @@ md_apply_fix3 (fixp, valuep, seg)
/* d10v_cleanup() is called after the assembler has finished parsing the input /* d10v_cleanup() is called after the assembler has finished parsing the input
file or after a label is defined. Because the D10V assembler sometimes saves short file or after a label is defined. Because the D10V assembler sometimes saves short
instructions to see if it can package them with the next instruction, there may instructions to see if it can package them with the next instruction, there may
be a short instruction that still needs written. */ be a short instruction that still needs written.
NOTE: accesses a global, etype.
NOTE: invoked by various macros such as md_cleanup: see. */
int int
d10v_cleanup () d10v_cleanup ()
{ {
segT seg; segT seg;
subsegT subseg; subsegT subseg;
if (prev_opcode && etype == 0) if (prev_opcode && etype == PACK_UNSPEC)
{ {
seg = now_seg; seg = now_seg;
subseg = now_subseg; subseg = now_subseg;