This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH] [ARC] GAS: add instruction relaxation.
- From: Claudiu Zissulescu <Claudiu dot Zissulescu at synopsys dot com>
- To: <binutils at sourceware dot org>
- Cc: <Claudiu dot Zissulescu at synopsys dot com>, <Francois dot Bedard at synopsys dot com>
- Date: Tue, 26 Jan 2016 15:09:08 +0100
- Subject: [PATCH] [ARC] GAS: add instruction relaxation.
- Authentication-results: sourceware.org; auth=none
This patch adds instruction relaxation support for ARC-GAS. The assembly-time
relaxation implementation has been made with an extendible design
in mind. This was done by using a table driven design.
The work on assembly-time relaxation was mainly carried on by Janek van
Oirschot as part of his intership at Synopsys. I have cleaned up and
reorganized the code a bit. I also added some tests for relaxation.
Comments are well appreciated,
Claudiu
gas/
2016-01-26 Claudiu Zissulescu <claziss@synopsys.com>
Janek van Oirschot <jvanoirs@synopsys.com>
* config/tc-arc.h (TC_FRAG_TYPE, TC_PCREL_ADJUST, MAX_INSN_ARGS)
(MAX_INSN_FLGS, MAX_FLAG_NAME_LENGHT, TC_GENERIC_RELAX_TABLE):
Define.
(arc_flags, arc_relax_type): New structure.
* config/tc-arc.c (FRAG_MAX_GROWTH, RELAX_TABLE_ENTRY)
(RELAX_TABLE_ENTRY_MAX): New define.
(relaxation_state, md_relax_table, arc_relaxable_insns)
(arc_num_relaxable_ins): New variable.
(rlx_operand_type, arc_rlx_types): New enums.
(arc_relaxable_ins): New structure.
(OPTION_RELAX): New option.
(arc_insn): New relax member.
(arc_flags): Remove.
(relax_insn_p): New function.
(apply_fixups): Likewise.
(relaxable_operand): Likewise.
(may_relax_expr): Likewise.
(relaxable_flag): Likewise.
(arc_pcrel_adjust): Likewise.
(md_estimate_size_before_relax): Implement.
(md_convert_frag): Likewise.
(md_parse_option): Handle new mrelax option.
(md_show_usage): Likewise.
(assemble_insn): Set relax member.
(emit_insn0): New function.
(emit_insn1): Likewise.
(emit_insn): Handle relaxation case.
gas/testsuite
2016-01-26 Claudiu Zissulescu <claziss@synopsys.com>
* gas/arc/relax-avoid1.d: New file.
* gas/arc/relax-avoid1.s: Likewise.
* gas/arc/relax-avoid2.d: Likewise.
* gas/arc/relax-avoid2.s: Likewise.
* gas/arc/relax-avoid3.d: Likewise.
* gas/arc/relax-avoid3.s: Likewise.
* gas/arc/relax-b.d: Likewise.
* gas/arc/relax-b.s: Likewise.
include/opcode/
2016-01-26 Claudiu Zissulescu <claziss@synopsys.com>
Janek van Oirschot <jvanoirs@synopsys.com>
* arc.h (arc_opcode arc_relax_opcodes, arc_num_relax_opcodes):
Declare.
opcodes/
2016-01-26 Claudiu Zissulescu <claziss@synopsys.com>
Janek van Oirschot <jvanoirs@synopsys.com>
* arc-opc.c (arc_relax_opcodes, arc_num_relax_opcodes): New
variable.
---
gas/config/tc-arc.c | 630 +++++++++++++++++++++++++++++++----
gas/config/tc-arc.h | 56 ++++
gas/testsuite/gas/arc/relax-avoid1.d | 13 +
gas/testsuite/gas/arc/relax-avoid1.s | 11 +
gas/testsuite/gas/arc/relax-avoid2.d | 14 +
gas/testsuite/gas/arc/relax-avoid2.s | 4 +
gas/testsuite/gas/arc/relax-avoid3.d | 14 +
gas/testsuite/gas/arc/relax-avoid3.s | 5 +
gas/testsuite/gas/arc/relax-b.d | 19 ++
gas/testsuite/gas/arc/relax-b.s | 11 +
include/opcode/arc.h | 8 +
opcodes/arc-opc.c | 126 +++++++
12 files changed, 844 insertions(+), 67 deletions(-)
create mode 100644 gas/testsuite/gas/arc/relax-avoid1.d
create mode 100644 gas/testsuite/gas/arc/relax-avoid1.s
create mode 100644 gas/testsuite/gas/arc/relax-avoid2.d
create mode 100644 gas/testsuite/gas/arc/relax-avoid2.s
create mode 100644 gas/testsuite/gas/arc/relax-avoid3.d
create mode 100644 gas/testsuite/gas/arc/relax-avoid3.s
create mode 100644 gas/testsuite/gas/arc/relax-b.d
create mode 100644 gas/testsuite/gas/arc/relax-b.s
diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c
index 40ba0ad..c2115a6 100644
--- a/gas/config/tc-arc.c
+++ b/gas/config/tc-arc.c
@@ -31,9 +31,9 @@
/* Defines section. */
-#define MAX_FLAG_NAME_LENGHT 3
#define MAX_INSN_FIXUPS 2
#define MAX_CONSTR_STR 20
+#define FRAG_MAX_GROWTH 8
#ifdef DEBUG
# define pr_debug(fmt, args...) fprintf (stderr, fmt, ##args)
@@ -49,6 +49,45 @@
/* Equal to MAX_PRECISION in atof-ieee.c. */
#define MAX_LITTLENUMS 6
+/* Enum used to enumerate the relaxable ins operands. */
+enum rlx_operand_type
+ {
+ EMPTY = 0,
+ REGISTER,
+ REGISTER_S, /* Register for short instruction(s). */
+ REGISTER_NO_GP, /* Is a register but not gp register specifically. */
+ REGISTER_DUP, /* Duplication of previous operand of type register. */
+ IMMEDIATE,
+ BRACKET
+ };
+
+enum arc_rlx_types
+ {
+ ARC_RLX_NONE = 0,
+ ARC_RLX_BL_S,
+ ARC_RLX_BL,
+ ARC_RLX_B_S,
+ ARC_RLX_B,
+ ARC_RLX_ADD_U3,
+ ARC_RLX_ADD_U6,
+ ARC_RLX_ADD_LIMM,
+ ARC_RLX_LD_U7,
+ ARC_RLX_LD_S9,
+ ARC_RLX_LD_LIMM,
+ ARC_RLX_MOV_U8,
+ ARC_RLX_MOV_S12,
+ ARC_RLX_MOV_LIMM,
+ ARC_RLX_SUB_U3,
+ ARC_RLX_SUB_U6,
+ ARC_RLX_SUB_LIMM,
+ ARC_RLX_MPY_U6,
+ ARC_RLX_MPY_LIMM,
+ ARC_RLX_MOV_RU6,
+ ARC_RLX_MOV_RLIMM,
+ ARC_RLX_ADD_RRU6,
+ ARC_RLX_ADD_RRLIMM,
+ };
+
/* Macros section. */
#define regno(x) ((x) & 0x3F)
@@ -83,6 +122,9 @@ extern int target_big_endian;
const char *arc_target_format = DEFAULT_TARGET_FORMAT;
static int byte_order = DEFAULT_BYTE_ORDER;
+/* By default relaxation is disabled. */
+static int relaxation_state = 0;
+
extern int arc_get_mach (char *);
/* Forward declaration. */
@@ -121,6 +163,7 @@ enum options
OPTION_MCPU,
OPTION_CD,
+ OPTION_RELAX,
/* The following options are deprecated and provided here only for
compatibility reasons. */
@@ -162,6 +205,7 @@ struct option md_longopts[] =
{ "mEM", no_argument, NULL, OPTION_ARCEM },
{ "mHS", no_argument, NULL, OPTION_ARCHS },
{ "mcode-density", no_argument, NULL, OPTION_CD },
+ { "mrelax", no_argument, NULL, OPTION_RELAX },
/* The following options are deprecated and provided here only for
compatibility reasons. */
@@ -242,6 +286,8 @@ struct arc_insn
short. */
bfd_boolean has_limm; /* Boolean value: TRUE if limm field is
valid. */
+ bfd_boolean relax; /* Boolean value: TRUE if needs
+ relaxation. */
};
/* Structure to hold any last two instructions. */
@@ -298,15 +344,6 @@ static const struct cpu_type
{ 0, 0, 0, 0, 0 }
};
-struct arc_flags
-{
- /* Name of the parsed flag. */
- char name[MAX_FLAG_NAME_LENGHT+1];
-
- /* The code of the parsed flag. Valid when is not zero. */
- unsigned char code;
-};
-
/* Used by the arc_reloc_op table. Order is important. */
#define O_gotoff O_md1 /* @gotoff relocation. */
#define O_gotpc O_md2 /* @gotpc relocation. */
@@ -371,6 +408,131 @@ static const struct arc_reloc_op_tag
static const int arc_num_reloc_op
= sizeof (arc_reloc_op) / sizeof (*arc_reloc_op);
+/* Structure for relaxable instruction that have to be swapped with a
+ smaller alternative instruction. */
+struct arc_relaxable_ins
+{
+ /* Mnemonic that should be checked. */
+ const char *mnemonic_r;
+
+ /* Operands that should be checked.
+ Indexes of operands from operand array. */
+ enum rlx_operand_type operands[6];
+
+ /* Flags that should be checked. */
+ unsigned flag_classes[5];
+
+ /* Mnemonic (smaller) alternative to be used later for relaxation. */
+ const char *mnemonic_alt;
+
+ /* Index of operand that generic relaxation has to check. */
+ unsigned opcheckidx;
+
+ /* Base subtype index used. */
+ enum arc_rlx_types subtype;
+};
+
+#define RELAX_TABLE_ENTRY(BITS, ISSIGNED, SIZE, NEXT) \
+ { (ISSIGNED) ? ((1 << ((BITS) - 1)) - 1) : ((1 << (BITS)) - 1), \
+ (ISSIGNED) ? -(1 << ((BITS) - 1)) : 0, \
+ (SIZE), \
+ (NEXT) } \
+
+#define RELAX_TABLE_ENTRY_MAX(ISSIGNED, SIZE, NEXT) \
+ { (ISSIGNED) ? 0x7FFFFFFF : 0xFFFFFFFF, \
+ (ISSIGNED) ? -(0x7FFFFFFF) : 0, \
+ (SIZE), \
+ (NEXT) } \
+
+
+/* ARC relaxation table. */
+const relax_typeS md_relax_table[] =
+ {
+ /* Fake entry. */
+ {0, 0, 0, 0},
+
+ /* BL_S s13 ->
+ BL s25. */
+ RELAX_TABLE_ENTRY(13, 1, 2, ARC_RLX_BL),
+ RELAX_TABLE_ENTRY(25, 1, 4, ARC_RLX_NONE),
+
+ /* B_S s10 ->
+ B s25. */
+ RELAX_TABLE_ENTRY(10, 1, 2, ARC_RLX_B),
+ RELAX_TABLE_ENTRY(25, 1, 4, ARC_RLX_NONE),
+
+ /* ADD_S c,b, u3 ->
+ ADD<.f> a,b,u6 ->
+ ADD<.f> a,b,limm. */
+ RELAX_TABLE_ENTRY(3, 0, 2, ARC_RLX_ADD_U6),
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_ADD_LIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* LD_S a, [b, u7] ->
+ LD<zz><.x><.aa><.di> a, [b, s9] ->
+ LD<zz><.x><.aa><.di> a, [b, limm] */
+ RELAX_TABLE_ENTRY(7, 0, 2, ARC_RLX_LD_S9),
+ RELAX_TABLE_ENTRY(9, 1, 4, ARC_RLX_LD_LIMM),
+ RELAX_TABLE_ENTRY_MAX(1, 8, ARC_RLX_NONE),
+
+ /* MOV_S b, u8 ->
+ MOV<.f> b, s12 ->
+ MOV<.f> b, limm. */
+ RELAX_TABLE_ENTRY(8, 0, 2, ARC_RLX_MOV_S12),
+ RELAX_TABLE_ENTRY(8, 0, 4, ARC_RLX_MOV_LIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* SUB_S c, b, u3 ->
+ SUB<.f> a, b, u6 ->
+ SUB<.f> a, b, limm. */
+ RELAX_TABLE_ENTRY(3, 0, 2, ARC_RLX_SUB_U6),
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_SUB_LIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* MPY<.f> a, b, u6 ->
+ MPY<.f> a, b, limm. */
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_MPY_LIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* MOV<.f><.cc> b, u6 ->
+ MOV<.f><.cc> b, limm. */
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_MOV_RLIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* ADD<.f><.cc> b, b, u6 ->
+ ADD<.f><.cc> b, b, limm. */
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_ADD_RRLIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+ };
+
+/* Order of this table's entries matters! */
+const struct arc_relaxable_ins arc_relaxable_insns[] =
+ {
+ { "bl", { IMMEDIATE }, { 0 }, "bl_s", 0, ARC_RLX_BL_S },
+ { "b", { IMMEDIATE }, { 0 }, "b_s", 0, ARC_RLX_B_S },
+ { "add", { REGISTER, REGISTER_DUP, IMMEDIATE }, { 5, 1, 0 }, "add",
+ 2, ARC_RLX_ADD_RRU6},
+ { "add", { REGISTER_S, REGISTER_S, IMMEDIATE }, { 0 }, "add_s", 2,
+ ARC_RLX_ADD_U3 },
+ { "add", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "add", 2,
+ ARC_RLX_ADD_U6 },
+ { "ld", { REGISTER_S, BRACKET, REGISTER_S, IMMEDIATE, BRACKET },
+ { 0 }, "ld_s", 3, ARC_RLX_LD_U7 },
+ { "ld", { REGISTER, BRACKET, REGISTER_NO_GP, IMMEDIATE, BRACKET },
+ { 11, 4, 14, 17, 0 }, "ld", 3, ARC_RLX_LD_S9 },
+ { "mov", { REGISTER_S, IMMEDIATE }, { 0 }, "mov_s", 1, ARC_RLX_MOV_U8 },
+ { "mov", { REGISTER, IMMEDIATE }, { 5, 0 }, "mov", 1, ARC_RLX_MOV_S12 },
+ { "mov", { REGISTER, IMMEDIATE }, { 5, 1, 0 },"mov", 1, ARC_RLX_MOV_RU6 },
+ { "sub", { REGISTER_S, REGISTER_S, IMMEDIATE }, { 0 }, "sub_s", 2,
+ ARC_RLX_SUB_U3 },
+ { "sub", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "sub", 2,
+ ARC_RLX_SUB_U6 },
+ { "mpy", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "mpy", 2,
+ ARC_RLX_MPY_U6 },
+ };
+
+const unsigned arc_num_relaxable_ins = ARRAY_SIZE (arc_relaxable_insns);
+
/* Flags to set in the elf header. */
static flagword arc_eflag = 0x00;
@@ -406,6 +568,12 @@ static const struct arc_opcode *find_special_case_pseudo (const char *,
expressionS *,
int *,
struct arc_flags *);
+static bfd_boolean relax_insn_p (const struct arc_opcode *opcode,
+ const expressionS *tok,
+ int ntok,
+ const struct arc_flags *pflags,
+ int nflg);
+static void emit_insn0 (struct arc_insn *insn, char *where, bfd_boolean relax);
/* Functions implementation. */
@@ -1470,16 +1638,33 @@ md_apply_fix (fixS *fixP,
fr_var starts with a value. */
int
-md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
- segT segment ATTRIBUTE_UNUSED)
+md_estimate_size_before_relax (fragS *fragP,
+ segT segment)
{
- int growth = 4;
+ int growth;
+
+ /* If the symbol is not located within the same section AND it's not
+ an absolute section, use the maximum. OR if the symbol is a
+ constant AND the insn is by nature not pc-rel, use the maximum.
+ OR if the symbol is being equated against another symbol, use the
+ maximum. OR if the symbol is weak use the maximum. */
+ if ((S_GET_SEGMENT (fragP->fr_symbol) != segment
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ || (symbol_constant_p (fragP->fr_symbol)
+ && !fragP->tc_frag_data.pcrel)
+ || symbol_equated_p (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ while (md_relax_table[fragP->fr_subtype].rlx_more != ARC_RLX_NONE)
+ ++fragP->fr_subtype;
+ }
+
+ growth = md_relax_table[fragP->fr_subtype].rlx_length;
+ fragP->fr_var = growth;
- fragP->fr_var = 4;
pr_debug ("%s:%d: md_estimate_size_before_relax: %d\n",
fragP->fr_file, fragP->fr_line, growth);
- as_fatal (_("md_estimate_size_before_relax\n"));
return growth;
}
@@ -1552,6 +1737,70 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
return reloc;
}
+/* Apply the fixups in order. */
+
+static void
+apply_fixups (struct arc_insn *insn,
+ fragS *fragP,
+ int fix)
+{
+ int i;
+
+ for (i = 0; i < insn->nfixups; i++)
+ {
+ struct arc_fixup *fixup = &insn->fixups[i];
+ int size, pcrel, offset = 0;
+
+ /*FIXME! the reloc size is wrong in the BFD file. When it will
+ be fixed please delete me. */
+ size = (insn->short_insn && !fixup->islong) ? 2 : 4;
+
+ if (fixup->islong)
+ offset = (insn->short_insn) ? 2 : 4;
+
+ /* Some fixups are only used internally, thus no howto. */
+ if ((int) fixup->reloc == 0)
+ {
+ as_fatal (_("Unhandled reloc type"));
+ }
+ if ((int) fixup->reloc < 0)
+ {
+ /*FIXME! the reloc size is wrong in the BFD file. When it
+ will be fixed please enable me.
+ size = (insn->short_insn && !fixup->islong) ? 2 : 4; */
+ pcrel = fixup->pcrel;
+ }
+ else
+ {
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) fixup->reloc);
+ gas_assert (reloc_howto);
+ /*FIXME! the reloc size is wrong in the BFD file. When it
+ will be fixed please enable me.
+ size = bfd_get_reloc_size (reloc_howto); */
+ pcrel = reloc_howto->pc_relative;
+ }
+
+ pr_debug ("%s:%d: apply_fixups: new %s fixup (PCrel:%s) of size %d @ \
+offset %d + %d\n",
+ fragP->fr_file, fragP->fr_line,
+ (fixup->reloc < 0) ? "Internal" :
+ bfd_get_reloc_code_name (fixup->reloc),
+ pcrel ? "Y" : "N",
+ size, fix, offset);
+ fix_new_exp (fragP, fix + offset,
+ size, &fixup->exp, pcrel, fixup->reloc);
+
+ /* Check for ZOLs, and update symbol info if any. */
+ if (LP_INSN (insn->insn))
+ {
+ gas_assert (fixup->exp.X_add_symbol);
+ ARC_SET_FLAG (fixup->exp.X_add_symbol, ARC_FLAG_ZOL);
+ }
+ }
+}
+
/* Perform post-processing of machine-dependent frags after relaxation.
Called after relaxation is finished.
In: Address of frag.
@@ -1563,12 +1812,40 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
segT segment ATTRIBUTE_UNUSED,
- fragS *fragP ATTRIBUTE_UNUSED)
+ fragS *fragP)
{
+ const relax_typeS *table_entry;
+ char *dest;
+ const struct arc_opcode *opcode;
+ struct arc_insn insn;
+ int size, fix;
+ struct arc_relax_type *relax_arg = &fragP->tc_frag_data;
+
+ fix = (fragP->fr_fix < 0 ? 0 : fragP->fr_fix);
+ dest = fragP->fr_literal + fix;
+ table_entry = TC_GENERIC_RELAX_TABLE + fragP->fr_subtype;
+
pr_debug ("%s:%d: md_convert_frag, subtype: %d, fix: %d, var: %d\n",
fragP->fr_file, fragP->fr_line,
- fragP->fr_subtype, fragP->fr_fix, fragP->fr_var);
- abort ();
+ fragP->fr_subtype, fix, fragP->fr_var);
+
+ if (fragP->fr_subtype <= 0
+ && fragP->fr_subtype >= arc_num_relax_opcodes)
+ as_fatal (_("no relaxation found for this instruction."));
+
+ opcode = &arc_relax_opcodes[fragP->fr_subtype];
+
+ assemble_insn (opcode, relax_arg->tok, relax_arg->ntok, relax_arg->pflags,
+ relax_arg->nflg, &insn);
+
+ apply_fixups (&insn, fragP, fix);
+
+ size = insn.short_insn ? (insn.has_limm ? 6 : 2) : (insn.has_limm ? 8 : 4);
+ gas_assert (table_entry->rlx_length == size);
+ emit_insn0 (&insn, dest, TRUE);
+
+ fragP->fr_fix += table_entry->rlx_length;
+ fragP->fr_var = 0;
}
/* We have no need to default values of symbols. We could catch
@@ -1665,6 +1942,7 @@ arc_parse_name (const char *name,
-mcpu=<cpu name> Assemble for selected processor
-EB/-mbig-endian Big-endian
-EL/-mlittle-endian Little-endian
+ -mrelax Enable relaxation
The following CPU names are recognized:
arc700, av2em, av2hs. */
@@ -1741,6 +2019,10 @@ md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
arc_features |= ARC_CD;
break;
+ case OPTION_RELAX:
+ relaxation_state = 1;
+ break;
+
case OPTION_USER_MODE:
case OPTION_LD_EXT_MASK:
case OPTION_SWAP:
@@ -1790,6 +2072,9 @@ md_show_usage (FILE *stream)
-EB assemble code for a big-endian cpu\n"));
fprintf (stream, _("\
-EL assemble code for a little-endian cpu\n"));
+ fprintf (stream, _("\
+ -mrelax Enable relaxation\n"));
+
}
static void
@@ -2859,6 +3144,8 @@ assemble_insn (const struct arc_opcode *opcode,
<< flg_operand->shift;
}
+ insn->relax = relax_insn_p (opcode, tok, ntok, pflags, nflg);
+
/* Short instruction? */
insn->short_insn = ARC_SHORT (opcode->mask) ? TRUE : FALSE;
@@ -2880,10 +3167,9 @@ assemble_insn (const struct arc_opcode *opcode,
/* Actually output an instruction with its fixup. */
static void
-emit_insn (struct arc_insn *insn)
+emit_insn0 (struct arc_insn *insn, char *where, bfd_boolean relax)
{
- char *f;
- int i;
+ char *f = where;
pr_debug ("Emit insn : 0x%x\n", insn->insn);
pr_debug ("\tShort : 0x%d\n", insn->short_insn);
@@ -2894,14 +3180,16 @@ emit_insn (struct arc_insn *insn)
{
if (insn->has_limm)
{
- f = frag_more (6);
+ if (!relax)
+ f = frag_more (6);
md_number_to_chars (f, insn->insn, 2);
md_number_to_chars_midend (f + 2, insn->limm, 4);
dwarf2_emit_insn (6);
}
else
{
- f = frag_more (2);
+ if (!relax)
+ f = frag_more (2);
md_number_to_chars (f, insn->insn, 2);
dwarf2_emit_insn (2);
}
@@ -2910,68 +3198,73 @@ emit_insn (struct arc_insn *insn)
{
if (insn->has_limm)
{
- f = frag_more (8);
+ if (!relax)
+ f = frag_more (8);
md_number_to_chars_midend (f, insn->insn, 4);
md_number_to_chars_midend (f + 4, insn->limm, 4);
dwarf2_emit_insn (8);
}
else
{
- f = frag_more (4);
+ if (!relax)
+ f = frag_more (4);
md_number_to_chars_midend (f, insn->insn, 4);
dwarf2_emit_insn (4);
}
}
- /* Apply the fixups in order. */
- for (i = 0; i < insn->nfixups; i++)
+ if (!relax)
+ apply_fixups (insn, frag_now, (f - frag_now->fr_literal));
+}
+
+static void
+emit_insn1 (struct arc_insn *insn)
+{
+ /* How frag_var's args are currently configured:
+ - rs_machine_dependent, to dictate it's a relaxation frag.
+ - FRAG_MAX_GROWTH, maximum size of instruction
+ - 0, variable size that might grow...unused by generic relaxation.
+ - frag_now->fr_subtype, fr_subtype starting value, set previously.
+ - s, opand expression.
+ - 0, offset but it's unused.
+ - 0, opcode but it's unused. */
+ symbolS *s = make_expr_symbol (&insn->fixups[0].exp);
+ frag_now->tc_frag_data.pcrel = insn->fixups[0].pcrel;
+
+ if (frag_room () < FRAG_MAX_GROWTH)
{
- struct arc_fixup *fixup = &insn->fixups[i];
- int size, pcrel, offset = 0;
+ /* Handle differently when frag literal memory is exhausted.
+ This is used because when there's not enough memory left in
+ the current frag, a new frag is created and the information
+ we put into frag_now->tc_frag_data is disregarded. */
- /*FIXME! the reloc size is wrong in the BFD file. When it will
- be fixed please delete me. */
- size = (insn->short_insn && !fixup->islong) ? 2 : 4;
+ struct arc_relax_type relax_info_copy;
+ relax_substateT subtype = frag_now->fr_subtype;
- if (fixup->islong)
- offset = (insn->short_insn) ? 2 : 4;
+ memcpy (&relax_info_copy, &frag_now->tc_frag_data,
+ sizeof (struct arc_relax_type));
- /* Some fixups are only used internally, thus no howto. */
- if ((int) fixup->reloc < 0)
- {
- /*FIXME! the reloc size is wrong in the BFD file. When it
- will be fixed please enable me.
- size = (insn->short_insn && !fixup->islong) ? 2 : 4; */
- pcrel = fixup->pcrel;
- }
- else
- {
- reloc_howto_type *reloc_howto =
- bfd_reloc_type_lookup (stdoutput,
- (bfd_reloc_code_real_type) fixup->reloc);
- gas_assert (reloc_howto);
- /*FIXME! the reloc size is wrong in the BFD file. When it
- will be fixed please enable me.
- size = bfd_get_reloc_size (reloc_howto); */
- pcrel = reloc_howto->pc_relative;
- }
+ frag_wane (frag_now);
+ frag_grow (FRAG_MAX_GROWTH);
- pr_debug ("%s:%d: emit_insn: new %s fixup (PCrel:%s) of size %d @ offset %d\n",
- frag_now->fr_file, frag_now->fr_line,
- (fixup->reloc < 0) ? "Internal" :
- bfd_get_reloc_code_name (fixup->reloc),
- pcrel ? "Y" : "N",
- size, offset);
- fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
- size, &fixup->exp, pcrel, fixup->reloc);
+ memcpy (&frag_now->tc_frag_data, &relax_info_copy,
+ sizeof (struct arc_relax_type));
- /* Check for ZOLs, and update symbol info if any. */
- if (LP_INSN (insn->insn))
- {
- gas_assert (fixup->exp.X_add_symbol);
- ARC_SET_FLAG (fixup->exp.X_add_symbol, ARC_FLAG_ZOL);
- }
+ frag_var (rs_machine_dependent, FRAG_MAX_GROWTH, 0,
+ subtype, s, 0, 0);
}
+ else
+ frag_var (rs_machine_dependent, FRAG_MAX_GROWTH, 0,
+ frag_now->fr_subtype, s, 0, 0);
+}
+
+static void
+emit_insn (struct arc_insn *insn)
+{
+ if (insn->relax)
+ emit_insn1 (insn);
+ else
+ emit_insn0 (insn, NULL, FALSE);
}
/* Insert an operand value into an instruction. */
@@ -3201,3 +3494,206 @@ arc_frob_label (symbolS * sym)
dwarf2_emit_label (sym);
}
+
+/* Checks if operands are in line with relaxable insn. */
+
+static bfd_boolean
+relaxable_operand (const struct arc_relaxable_ins *ins,
+ const expressionS *tok,
+ int ntok)
+{
+ const enum rlx_operand_type *operand = &ins->operands[0];
+ int i = 0;
+
+ while (*operand != EMPTY)
+ {
+ const expressionS *epr = &tok[i];
+
+ if (i != 0 && i >= ntok)
+ return FALSE;
+
+ switch (*operand)
+ {
+ case IMMEDIATE:
+ if (!(epr->X_op == O_multiply
+ || epr->X_op == O_divide
+ || epr->X_op == O_modulus
+ || epr->X_op == O_add
+ || epr->X_op == O_subtract
+ || epr->X_op == O_symbol))
+ return FALSE;
+ break;
+
+ case REGISTER_DUP:
+ if ((i <= 0)
+ || (epr->X_add_number != tok[i - 1].X_add_number))
+ return FALSE;
+ /* Fall through. */
+ case REGISTER:
+ if (epr->X_op != O_register)
+ return FALSE;
+ break;
+
+ case REGISTER_S:
+ if (epr->X_op != O_register)
+ return FALSE;
+
+ switch (epr->X_add_number)
+ {
+ case 0:case 1:case 2:case 3:
+ case 12:case 13:case 14:case 15:
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+
+ case REGISTER_NO_GP:
+ if ((epr->X_op != O_register)
+ || (epr->X_add_number == 26)) /* 26 is the gp register. */
+ return FALSE;
+ break;
+
+ case BRACKET:
+ if (epr->X_op != O_bracket)
+ return FALSE;
+ break;
+
+ default:
+ /* Don't understand, bail out. */
+ return FALSE;
+ break;
+ }
+
+ ++i;
+ operand = &ins->operands[i];
+ }
+
+ return (i == ntok ? TRUE : FALSE);
+}
+
+/* Checks if flags are in line with relaxable insn. */
+
+static bfd_boolean
+relaxable_flag (const struct arc_relaxable_ins *ins,
+ const struct arc_flags *pflags,
+ int nflgs)
+{
+ unsigned flag_class,
+ flag,
+ flag_class_idx = 0,
+ flag_idx = 0;
+
+ const struct arc_flag_operand *flag_opand;
+ int i, counttrue = 0;
+
+ /* Iterate through flags classes. */
+ while ((flag_class = ins->flag_classes[flag_class_idx]) != 0)
+ {
+ /* Iterate through flags in flag class. */
+ while ((flag = arc_flag_classes[flag_class].flags[flag_idx])
+ != 0)
+ {
+ flag_opand = &arc_flag_operands[flag];
+ /* Iterate through flags in ins to compare. */
+ for (i = 0; i < nflgs; ++i)
+ {
+ if (strcmp (flag_opand->name, pflags[i].name) == 0)
+ ++counttrue;
+ }
+
+ ++flag_idx;
+ }
+
+ ++flag_class_idx;
+ flag_idx = 0;
+ }
+
+ /* If counttrue == nflgs, then all flags have been found. */
+ return (counttrue == nflgs ? TRUE : FALSE);
+}
+
+/* All the symbol types that are allowed to be used for
+ relaxation. */
+
+static bfd_boolean
+may_relax_expr (expressionS tok)
+{
+ /* Check if we have unrelaxable relocs. */
+ switch (tok.X_md)
+ {
+ default:
+ break;
+ case O_plt:
+ return FALSE;
+ }
+
+ switch (tok.X_op)
+ {
+ case O_symbol:
+ case O_multiply:
+ case O_divide:
+ case O_modulus:
+ case O_add:
+ case O_subtract:
+ break;
+
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Return TRUE if this OPDCODE is a candidate for relaxation. */
+
+static bfd_boolean
+relax_insn_p (const struct arc_opcode *opcode,
+ const expressionS *tok,
+ int ntok,
+ const struct arc_flags *pflags,
+ int nflg)
+{
+ unsigned i;
+ bfd_boolean rv = FALSE;
+
+ /* Check the relaxation table. */
+ for (i = 0; i < arc_num_relaxable_ins && relaxation_state; ++i)
+ {
+ const struct arc_relaxable_ins *arc_rlx_ins = &arc_relaxable_insns[i];
+
+ if ((strcmp (opcode->name, arc_rlx_ins->mnemonic_r) == 0)
+ && may_relax_expr (tok[arc_rlx_ins->opcheckidx])
+ && relaxable_operand (arc_rlx_ins, tok, ntok)
+ && relaxable_flag (arc_rlx_ins, pflags, nflg))
+ {
+ rv = TRUE;
+ frag_now->fr_subtype = arc_relaxable_insns[i].subtype;
+ memcpy (&frag_now->tc_frag_data.tok, tok,
+ sizeof (expressionS) * ntok);
+ memcpy (&frag_now->tc_frag_data.pflags, pflags,
+ sizeof (struct arc_flags) * nflg);
+ frag_now->tc_frag_data.nflg = nflg;
+ frag_now->tc_frag_data.ntok = ntok;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+/* Used because generic relaxation assumes a pc-rel value whilst we
+ also relax instructions that use an absolute value resolved out of
+ relative values (if that makes any sense). An example: 'add r1,
+ r2, @.L2 - .' The symbols . and @.L2 are relative to the section
+ but if they're in the same section we can subtract the section
+ offset relocation which ends up in a resolved value. So if @.L2 is
+ .text + 0x50 and . is .text + 0x10, we can say that .text + 0x50 -
+ .text + 0x40 = 0x10. */
+int
+arc_pcrel_adjust (fragS *fragP)
+{
+ if (!fragP->tc_frag_data.pcrel)
+ return fragP->fr_address + fragP->fr_fix;
+
+ return 0;
+}
diff --git a/gas/config/tc-arc.h b/gas/config/tc-arc.h
index ca5b152..acd007b 100644
--- a/gas/config/tc-arc.h
+++ b/gas/config/tc-arc.h
@@ -177,6 +177,14 @@ extern long md_pcrel_from_section (struct fix *, segT);
/* This hook is required to parse register names as operands. */
#define md_parse_name(name, exp, m, c) arc_parse_name (name, exp)
+/* Used within frags to pass some information to some relaxation
+ machine dependent values. */
+#define TC_FRAG_TYPE struct arc_relax_type
+
+/* Adjust non PC-rel values at relaxation time. */
+#define TC_PCREL_ADJUST(F) arc_pcrel_adjust (F)
+
+extern int arc_pcrel_adjust (fragS *);
extern bfd_boolean arc_parse_name (const char *, struct expressionS *);
extern int tc_arc_fix_adjustable (struct fix *);
extern void arc_handle_align (fragS *);
@@ -193,3 +201,51 @@ extern void arc_frob_label (symbolS *);
#define NOP_OPCODE_S 0x000078E0
#define NOP_OPCODE_L 0x264A7000 /* mov 0,0. */
+#define MAX_FLAG_NAME_LENGHT 3
+
+struct arc_flags
+{
+ /* Name of the parsed flag. */
+ char name[MAX_FLAG_NAME_LENGHT + 1];
+
+ /* The code of the parsed flag. Valid when is not zero. */
+ unsigned char code;
+};
+
+#ifndef MAX_INSN_ARGS
+#define MAX_INSN_ARGS 6
+#endif
+
+#ifndef MAX_INSN_FLGS
+#define MAX_INSN_FLGS 3
+#endif
+
+extern const relax_typeS md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* Used to construct instructions at md_convert_frag stage of
+ relaxation. */
+struct arc_relax_type
+{
+ /* Dictates whether the pc-relativity should be kept in mind when
+ relax_frag is called or whether the pc-relativity should be
+ solved outside of relaxation. For clarification: BL(_S) and
+ B(_S) use pcrel == 1 and ADD with a solvable expression as 3rd
+ operand use pcrel == 0. */
+ unsigned char pcrel;
+
+ /* Expressions that dictate the operands. Used for re-assembling in
+ md_convert_frag. */
+ expressionS tok[MAX_INSN_ARGS];
+
+ /* Number of tok (i.e. number of operands). Used for re-assembling
+ in md_convert_frag. */
+ int ntok;
+
+ /* Flags of instruction. Used for re-assembling in
+ md_convert_frag. */
+ struct arc_flags pflags[MAX_INSN_FLGS];
+
+ /* Number of flags. Used for re-assembling in md_convert_frag. */
+ int nflg;
+};
diff --git a/gas/testsuite/gas/arc/relax-avoid1.d b/gas/testsuite/gas/arc/relax-avoid1.d
new file mode 100644
index 0000000..3d6d74e
--- /dev/null
+++ b/gas/testsuite/gas/arc/relax-avoid1.d
@@ -0,0 +1,13 @@
+#as: -mcpu=archs -mrelax
+#objdump: -dr
+
+.*: +file format .*arc.*
+
+
+Disassembly of section .text:
+
+00000000 <.text>:
+ 0: 78e0 nop_s
+ 2: 240a 0f80 0000 0000 mov r4,0
+ 6: R_ARC_32_ME .LC2
+ a: 78e0 nop_s
diff --git a/gas/testsuite/gas/arc/relax-avoid1.s b/gas/testsuite/gas/arc/relax-avoid1.s
new file mode 100644
index 0000000..82fbe63
--- /dev/null
+++ b/gas/testsuite/gas/arc/relax-avoid1.s
@@ -0,0 +1,11 @@
+ .section .rodata
+ .align 4
+.LC2:
+ .word 0x01
+ .word 0x02
+ .word 0x03
+
+ .section .text
+ .align 4
+ nop_s
+ mov r4,@.LC2
diff --git a/gas/testsuite/gas/arc/relax-avoid2.d b/gas/testsuite/gas/arc/relax-avoid2.d
new file mode 100644
index 0000000..fd602b4
--- /dev/null
+++ b/gas/testsuite/gas/arc/relax-avoid2.d
@@ -0,0 +1,14 @@
+#as: -mcpu=archs -mrelax
+#objdump: -dr
+
+.*: +file format .*arc.*
+
+
+Disassembly of section .text:
+
+00000000 <test>:
+ 0: 2000 0000 add r0,r0,r0
+
+00000004 <main>:
+ 4: 0802 0000 bl 0 <test>
+ 4: R_ARC_S25W_PCREL_PLT test
diff --git a/gas/testsuite/gas/arc/relax-avoid2.s b/gas/testsuite/gas/arc/relax-avoid2.s
new file mode 100644
index 0000000..703064d
--- /dev/null
+++ b/gas/testsuite/gas/arc/relax-avoid2.s
@@ -0,0 +1,4 @@
+test:
+ add r0,r0,r0
+main:
+ bl @test@plt
diff --git a/gas/testsuite/gas/arc/relax-avoid3.d b/gas/testsuite/gas/arc/relax-avoid3.d
new file mode 100644
index 0000000..7b177fb
--- /dev/null
+++ b/gas/testsuite/gas/arc/relax-avoid3.d
@@ -0,0 +1,14 @@
+#as: -mcpu=archs -mrelax
+#objdump: -dr
+
+.*: +file format .*arc.*
+
+
+Disassembly of section .text:
+
+00000000 <test>:
+ 0: 2000 0000 add r0,r0,r0
+
+00000004 <main>:
+ 4: 0001 0000 b 0 <test>
+ 4: R_ARC_S25H_PCREL test
diff --git a/gas/testsuite/gas/arc/relax-avoid3.s b/gas/testsuite/gas/arc/relax-avoid3.s
new file mode 100644
index 0000000..dc913a4
--- /dev/null
+++ b/gas/testsuite/gas/arc/relax-avoid3.s
@@ -0,0 +1,5 @@
+test:
+ add r0,r0,r0
+ .weak test
+main:
+ b test
diff --git a/gas/testsuite/gas/arc/relax-b.d b/gas/testsuite/gas/arc/relax-b.d
new file mode 100644
index 0000000..fd8dc47
--- /dev/null
+++ b/gas/testsuite/gas/arc/relax-b.d
@@ -0,0 +1,19 @@
+#as: -mcpu=archs -mrelax
+#objdump: -dr
+
+.*: +file format .*arc.*
+
+
+Disassembly of section .text:
+
+00000000 <foo-0x4>:
+ 0: 78e0 nop_s
+ 2: 78e0 nop_s
+
+00000004 <foo>:
+ 4: 2000 0000 add r0,r0,r0
+
+00000008 <bar>:
+ 8: ffff bl_s 4 <foo>
+ a: 2100 0041 add r1,r1,r1
+ e: f1fc b_s 4 <foo>
diff --git a/gas/testsuite/gas/arc/relax-b.s b/gas/testsuite/gas/arc/relax-b.s
new file mode 100644
index 0000000..3698b14
--- /dev/null
+++ b/gas/testsuite/gas/arc/relax-b.s
@@ -0,0 +1,11 @@
+ .text
+ nop_s
+ .align 4
+foo:
+ add r0,r0,r0
+
+ .align 4
+bar:
+ bl @foo
+ add r1,r1,r1
+ b @foo
diff --git a/include/opcode/arc.h b/include/opcode/arc.h
index a69a561..6f5bc98 100644
--- a/include/opcode/arc.h
+++ b/include/opcode/arc.h
@@ -24,8 +24,13 @@
#ifndef OPCODE_ARC_H
#define OPCODE_ARC_H
+#ifndef MAX_INSN_ARGS
#define MAX_INSN_ARGS 6
+#endif
+
+#ifndef MAX_INSN_FLGS
#define MAX_INSN_FLGS 3
+#endif
/* Instruction Class. */
typedef enum
@@ -410,4 +415,7 @@ struct arc_aux_reg
extern const struct arc_aux_reg arc_aux_regs[];
extern const unsigned arc_num_aux_regs;
+extern const struct arc_opcode arc_relax_opcodes[];
+extern const unsigned arc_num_relax_opcodes;
+
#endif /* OPCODE_ARC_H */
diff --git a/opcodes/arc-opc.c b/opcodes/arc-opc.c
index 2d6e887..9c6a86d 100644
--- a/opcodes/arc-opc.c
+++ b/opcodes/arc-opc.c
@@ -1359,3 +1359,129 @@ const struct arc_aux_reg arc_aux_regs[] =
};
const unsigned arc_num_aux_regs = ARRAY_SIZE (arc_aux_regs);
+
+/* NOTE: The order of this array MUST be consistent with 'enum
+ arc_rlx_types' located in tc-arc.h! */
+const struct arc_opcode arc_relax_opcodes[] =
+ {
+ { NULL, 0x0, 0x0, 0x0, ARITH, NONE, { UNUSED }, { 0 } },
+
+ /* bl_s s13 11111sssssssssss. */
+ { "bl_s", 0x0000F800, 0x0000F800, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, BRANCH, NONE,
+ { SIMM13_A32_5_S }, { 0 }},
+
+ /* bl<.d> s25 00001sssssssss10SSSSSSSSSSNRtttt. */
+ { "bl", 0x08020000, 0xF8030000, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, BRANCH, NONE,
+ { SIMM25_A32_5 }, { C_D }},
+
+ /* b_s s10 1111000sssssssss. */
+ { "b_s", 0x0000F000, 0x0000FE00, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, BRANCH, NONE,
+ { SIMM10_A16_7_S }, { 0 }},
+
+ /* b<.d> s25 00000ssssssssss1SSSSSSSSSSNRtttt. */
+ { "b", 0x00010000, 0xF8010000, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, BRANCH, NONE,
+ { SIMM25_A16_5 }, { C_D }},
+
+ /* add_s c,b,u3 01101bbbccc00uuu. Wants UIMM3_13_S_PCREL. */
+ { "add_s", 0x00006800, 0x0000F818, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, ARITH, NONE,
+ { RC_S, RB_S, UIMM3_13_S }, { 0 }},
+
+ /* add<.f> a,b,u6 00100bbb01000000FBBBuuuuuuAAAAAA. Wants
+ UIMM6_20_PCREL. */
+ { "add", 0x20400000, 0xF8FF0000, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, ARITH, NONE,
+ { RA, RB, UIMM6_20 }, { C_F }},
+
+ /* add<.f> a,b,limm 00100bbb00000000FBBB111110AAAAAA. */
+ { "add", 0x20000F80, 0xF8FF0FC0, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, ARITH, NONE,
+ { RA, RB, LIMM }, { C_F }},
+
+ /* ld_s c,b,u7 10000bbbcccuuuuu. Wants UIMM7_A32_11_S_PCREL. */
+ { "ld_s", 0x00008000, 0x0000F800, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, MEMORY, NONE,
+ { RC_S, BRAKET, RB_S, UIMM7_A32_11_S, BRAKETdup }, { 0 }},
+
+ /* ld<.di><.aa><.x><zz> a,b,s9
+ 00010bbbssssssssSBBBDaaZZXAAAAAA. Wants SIMM9_8_PCREL. */
+ { "ld", 0x10000000, 0xF8000000, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, MEMORY, NONE,
+ { RA, BRAKET, RB, SIMM9_8, BRAKETdup },
+ { C_ZZ23, C_DI20, C_AA21, C_X25 }},
+
+ /* ld<.di><.aa><.x><zz> a,b,limm 00100bbbaa110ZZXDBBB111110AAAAAA. */
+ { "ld", 0x20300F80, 0xF8380FC0, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, MEMORY, NONE,
+ { RA, BRAKET, RB, LIMM, BRAKETdup },
+ { C_ZZ13, C_DI16, C_AA8, C_X15 }},
+
+ /* mov_s b,u8 11011bbbuuuuuuuu. Wants UIMM8_8_S_PCREL. */
+ { "mov_s", 0x0000D800, 0x0000F800, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, MEMORY, NONE,
+ { RB_S, UIMM8_8_S }, { 0 }},
+
+ /* mov<.f> b,s12 00100bbb10001010FBBBssssssSSSSSS. Wants
+ SIMM12_20_PCREL. */
+ { "mov", 0x208A0000, 0xF8FF0000, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, MEMORY, NONE,
+ { RB, SIMM12_20 }, { C_F }},
+
+ /* mov<.f> b,limm 00100bbb00001010FBBB111110RRRRRR. */
+ { "mov", 0x200A0F80, 0xF8FF0FC0, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, MEMORY, NONE,
+ { RB, LIMM }, { C_F }},
+
+ /* sub_s c,b,u3 01101bbbccc01uuu. UIMM3_13_S_PCREL. */
+ { "sub_s", 0x00006808, 0x0000F818, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, ARITH, NONE,
+ { RC_S, RB_S, UIMM3_13_S }, { 0 }},
+
+ /* sub<.f> a,b,u6 00100bbb01000010FBBBuuuuuuAAAAAA.
+ UIMM6_20_PCREL. */
+ { "sub", 0x20420000, 0xF8FF0000, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, ARITH, NONE,
+ { RA, RB, UIMM6_20 }, { C_F }},
+
+ /* sub<.f> a,b,limm 00100bbb00000010FBBB111110AAAAAA. */
+ { "sub", 0x20020F80, 0xF8FF0FC0, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, ARITH, NONE,
+ { RA, RB, LIMM }, { C_F }},
+
+ /* mpy<.f> a,b,u6 00100bbb01011010FBBBuuuuuuAAAAAA.
+ UIMM6_20_PCREL. */
+ { "mpy", 0x205A0000, 0xF8FF0000, ARC_OPCODE_ARC700 | ARC_OPCODE_ARCv2EM
+ | ARC_OPCODE_ARCv2HS, ARITH, MPY6E, { RA, RB, UIMM6_20 }, { C_F }},
+
+ /* mpy<.f> a,b,limm 00100bbb00011010FBBB111110AAAAAA. */
+ { "mpy", 0x201A0F80, 0xF8FF0FC0, ARC_OPCODE_ARC700 | ARC_OPCODE_ARCv2EM
+ | ARC_OPCODE_ARCv2HS, ARITH, MPY6E, { RA, RB, LIMM }, { C_F }},
+
+ /* mov<.f><.cc> b,u6 00100bbb11001010FBBBuuuuuu1QQQQQ.
+ UIMM6_20_PCREL. */
+ { "mov", 0x20CA0020, 0xF8FF0020, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, MEMORY, NONE,
+ { RB, UIMM6_20 }, { C_F, C_CC }},
+
+ /* mov<.f><.cc> b,limm 00100bbb11001010FBBB1111100QQQQQ. */
+ { "mov", 0x20CA0F80, 0xF8FF0FE0, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, MEMORY, NONE,
+ { RB, LIMM }, { C_F, C_CC }},
+
+ /* add<.f><.cc> b,b,u6 00100bbb11000000FBBBuuuuuu1QQQQQ.
+ UIMM6_20_PCREL. */
+ { "add", 0x20C00020, 0xF8FF0020, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, ARITH, NONE,
+ { RB, RBdup, UIMM6_20 }, { C_F, C_CC }},
+
+ /* add<.f><.cc> b,b,limm 00100bbb11000000FBBB1111100QQQQQ. */
+ { "add", 0x20C00F80, 0xF8FF0FE0, ARC_OPCODE_ARC600 | ARC_OPCODE_ARC700
+ | ARC_OPCODE_ARCv2EM | ARC_OPCODE_ARCv2HS, ARITH, NONE,
+ { RB, RBdup, LIMM }, { C_F, C_CC }}
+ };
+
+const unsigned arc_num_relax_opcodes = ARRAY_SIZE (arc_relax_opcodes);
--
1.9.1