From fc2ec73adf8447963f25e6910c888041020bd5f1 Mon Sep 17 00:00:00 2001 From: Claudiu Zissulescu Date: Tue, 26 Jan 2016 13:58:02 +0100 Subject: [PATCH] [ARC] GAS: add instruction relaxation. This patch adds instruction relaxation support for GAS. The assembly-time relaxation implementation has been made with an extendible design in mind because we want relaxation to be extended on without having to need a deep understanding of the source code. 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 some of the functions. I also added some tests for relaxation. gas/ 2016-01-26 Claudiu Zissulescu Janek van Oirschot * 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. * NEWS: Mention the new relaxation option. * doc/c-arc.texi (ARC Options): Document new mrelax option. gas/testsuite 2016-01-26 Claudiu Zissulescu * 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 Janek van Oirschot * arc.h (arc_opcode arc_relax_opcodes, arc_num_relax_opcodes): Declare. opcodes/ 2016-01-26 Claudiu Zissulescu Janek van Oirschot * arc-opc.c (arc_relax_opcodes, arc_num_relax_opcodes): New variable. --- gas/NEWS | 2 + gas/config/tc-arc.c | 630 +++++++++++++++++++++++++++++++---- gas/config/tc-arc.h | 56 ++++ gas/doc/c-arc.texi | 6 + 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 +++++++ 14 files changed, 852 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/NEWS b/gas/NEWS index d66c2f5..a4ddeca 100644 --- a/gas/NEWS +++ b/gas/NEWS @@ -1,5 +1,7 @@ -*- text -*- +* Add assembly-time relaxation option for ARC cpus. + Changes in 2.26: * Add a configure option --enable-compressed-debug-sections={all,gas} to diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c index 40ba0ad..8708fc7 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<.x><.aa><.di> a, [b, s9] -> + LD<.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= 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/doc/c-arc.texi b/gas/doc/c-arc.texi index 96abe83..4824027 100644 --- a/gas/doc/c-arc.texi +++ b/gas/doc/c-arc.texi @@ -85,6 +85,12 @@ default. This option turns on Code Density instructions. Only valid for ARC EM processors. +@cindex @code{-mrelax} command line option, ARC +@item -mrelax +Enable support for assembly-time relaxation. The assembler will +replace a longer version of an instruction with a shorter one, +whenever it is possible. + @end table @node ARC Syntax 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 : + 0: 2000 0000 add r0,r0,r0 + +00000004
: + 4: 0802 0000 bl 0 + 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 : + 0: 2000 0000 add r0,r0,r0 + +00000004
: + 4: 0001 0000 b 0 + 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 : + 0: 78e0 nop_s + 2: 78e0 nop_s + +00000004 : + 4: 2000 0000 add r0,r0,r0 + +00000008 : + 8: ffff bl_s 4 + a: 2100 0041 add r1,r1,r1 + e: f1fc b_s 4 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..84cff68 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> 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> 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