]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-arc.c
* config/sh/tm-sh.h (BELIEVE_PCC_PROMOTION): Define, so that
[thirdparty/binutils-gdb.git] / gas / config / tc-arc.c
index c8f216bb3f2229e74afb367943a5b41a92afc052..fd14c58cf1dbf29d2cf516aa11d08f914a520016 100644 (file)
@@ -16,7 +16,7 @@
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+   the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
 
 #include <stdio.h>
 #include <ctype.h>
@@ -49,9 +49,6 @@ const pseudo_typeS md_pseudo_table[] =
   { NULL, 0, 0 },
 };
 
-const int md_short_jump_size = 4;
-const int md_long_jump_size = 4;
-
 /* This array holds the chars that always start a comment.  If the
    pre-processor is disabled, these aren't very useful */
 const char comment_chars[] = "#;";
@@ -79,7 +76,7 @@ const char FLT_CHARS[] = "rRsSfFdD";
 /* Byte order.  */
 extern int target_big_endian;
 const char *arc_target_format = DEFAULT_TARGET_FORMAT;
-static int byte_order;
+static int byte_order = DEFAULT_BYTE_ORDER;
 
 /* One of bfd_mach_arc_xxx.  */
 static int arc_mach_type = bfd_mach_arc_base;
@@ -291,7 +288,9 @@ struct arc_fixup
   expressionS exp;
 };
 
-#define MAX_INSN_FIXUPS 5
+#define MAX_FIXUPS 5
+
+#define MAX_SUFFIXES 5
 
 /* This routine is called for each instruction to be assembled.  */
 
@@ -299,7 +298,7 @@ void
 md_assemble (str)
      char *str;
 {
-  const struct arc_opcode *opcode,*opcode_end;
+  const struct arc_opcode *opcode;
   char *start;
   arc_insn insn;
   static int init_tables_p = 0;
@@ -316,28 +315,23 @@ md_assemble (str)
   while (isspace (*str))
     str++;
 
-  /* The instructions are sorted by the first letter.  Scan the opcode table
-     until we find the right one.  */
-  opcode_end = arc_opcodes + arc_opcodes_count;
-  for (opcode = arc_opcodes; opcode < opcode_end; opcode++)
-    if (*opcode->syntax == *str)
-      break;
-  if (opcode == opcode_end)
-    {
-      as_bad ("bad instruction `%s'", str);
-      return;
-    }
+  /* The instructions are stored in lists hashed by the first letter (though
+     we needn't care how they're hashed).  Get the first in the list.  */
 
-  /* Keep looking until we find a match.  If we haven't found a match, and the
-     first character no longer matches, we needn't look any further.  */
+  opcode = arc_opcode_lookup_asm (str);
+
+  /* Keep looking until we find a match.  */
 
   start = str;
-  for ( ; opcode < opcode_end && *opcode->syntax == *start; ++opcode)
+  for ( ; opcode != NULL; opcode = ARC_OPCODE_NEXT_ASM (opcode))
     {
-      int past_opcode_p;
+      int past_opcode_p, fc, num_suffixes;
       char *syn;
-      struct arc_fixup fixups[MAX_INSN_FIXUPS];
-      int fc,limm_reloc_p;
+      struct arc_fixup fixups[MAX_FIXUPS];
+      /* Used as a sanity check.  If we need a limm reloc, make sure we ask
+        for an extra 4 bytes from frag_more.  */
+      int limm_reloc_p;
+      const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES];
 
       /* Is this opcode supported by the selected cpu?  */
       if (! arc_opcode_supported (opcode))
@@ -349,9 +343,7 @@ md_assemble (str)
       insn = opcode->value;
       fc = 0;
       past_opcode_p = 0;
-
-      /* Used as a sanity check.  If we need a limm reloc, make sure we ask
-        for an extra 4 bytes from frag_more.  */
+      num_suffixes = 0;
       limm_reloc_p = 0;
 
       /* We don't check for (*str != '\0') here because we want to parse
@@ -469,7 +461,7 @@ md_assemble (str)
              suffix_end = arc_suffixes + arc_suffixes_count;
              for (suffix = suf;
                   suffix < suffix_end && strcmp (suffix->name, suf->name) == 0;
-                  suffix++)
+                  ++suffix)
                {
                  if (arc_operands[suffix->type].fmt == *syn)
                    {
@@ -488,9 +480,14 @@ md_assemble (str)
                }
              ++syn;
              if (!found)
-               /* There's nothing to do except go on to try the next one.
-                  ??? This test can be deleted when we're done.  */
-               ;
+               ; /* Wrong type.  Just go on to try next insn entry.  */
+             else
+               {
+                 if (num_suffixes == MAX_SUFFIXES)
+                   as_bad ("too many suffixes");
+                 else
+                   insn_suffixes[num_suffixes++] = suffix;
+               }
            }
          else
            /* This is either a register or an expression of some kind.  */
@@ -546,7 +543,7 @@ md_assemble (str)
              else
                {
                  /* We need to generate a fixup for this expression.  */
-                 if (fc >= MAX_INSN_FIXUPS)
+                 if (fc >= MAX_FIXUPS)
                    as_fatal ("too many fixups");
                  fixups[fc].exp = exp;
 
@@ -603,15 +600,17 @@ md_assemble (str)
        }
 
       /* If we're at the end of the syntax string, we're done.  */
+      /* FIXME: try to move this to a separate function.  */
       if (*syn == '\0')
        {
          int i;
          char *f;
-         long limm;
+         long limm, limm_p;
 
-         /* ??? For the moment we assume a valid `str' can only contain blanks
+         /* For the moment we assume a valid `str' can only contain blanks
             now.  IE: We needn't try again with a longer version of the
-            insn.  */
+            insn and it is assumed that longer versions of insns appear
+            before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3).  */
 
          while (isspace (*str))
            ++str;
@@ -619,11 +618,63 @@ md_assemble (str)
          if (*str != '\0')
            as_bad ("junk at end of line: `%s'", str);
 
+         /* Is there a limm value?  */
+         limm_p = arc_opcode_limm_p (&limm);
+
+         /* Perform various error and warning tests.  */
+
+         {
+           static int in_delay_slot_p = 0;
+           static int prev_insn_needs_cc_nop_p = 0;
+           /* delay slot type seen */
+           int delay_slot_type = ARC_DELAY_NONE;
+           /* conditional execution flag seen */
+           int conditional = 0;
+           /* 1 if condition codes are being set */
+           int cc_set_p = 0;
+           /* 1 if conditional branch, including `b' "branch always" */
+           int cond_branch_p = opcode->flags & ARC_OPCODE_COND_BRANCH;
+           int need_cc_nop_p = 0;
+
+           for (i = 0; i < num_suffixes; ++i)
+             {
+               switch (arc_operands[insn_suffixes[i]->type].fmt)
+                 {
+                 case 'n' :
+                   delay_slot_type = insn_suffixes[i]->value;
+                   break;
+                 case 'q' :
+                   conditional = insn_suffixes[i]->value;
+                   break;
+                 case 'f' :
+                   cc_set_p = 1;
+                   break;
+                 }
+             }
+
+           /* Putting an insn with a limm value in a delay slot is supposed to
+              be legal, but let's warn the user anyway.  Ditto for 8 byte
+              jumps with delay slots.  */
+           if (in_delay_slot_p && limm_p)
+             as_warn ("8 byte instruction in delay slot");
+           if (delay_slot_type != ARC_DELAY_NONE && limm_p)
+             as_warn ("8 byte jump instruction with delay slot");
+           in_delay_slot_p = (delay_slot_type != ARC_DELAY_NONE) && !limm_p;
+
+           /* Warn when a conditional branch immediately follows a set of
+              the condition codes.  Note that this needn't be done if the
+              insn that sets the condition codes uses a limm.  */
+           if (cond_branch_p && conditional != 0 /* 0 = "always" */
+               && prev_insn_needs_cc_nop_p)
+             as_warn ("conditional branch follows set of flags");
+           prev_insn_needs_cc_nop_p = cc_set_p && !limm_p;
+         }
+
          /* Write out the instruction.
             It is important to fetch enough space in one call to `frag_more'.
             We use (f - frag_now->fr_literal) to compute where we are and we
             don't want frag_now to change between calls.  */
-         if (arc_opcode_limm_p (&limm))
+         if (limm_p)
            {
              f = frag_more (8);
              md_number_to_chars (f, insn, 4);
@@ -1009,11 +1060,6 @@ md_estimate_size_before_relax (fragp, seg)
   abort ();
 }
 
-const relax_typeS md_relax_table[] =
-{
-  { 0 }
-};
-
 /* Convert a machine dependent frag.  We never generate these.  */
 
 void
@@ -1044,8 +1090,6 @@ md_operand (expressionP)
 
   if (*p == '%' && strncmp (p, "%st(", 4) == 0)
     {
-      expressionS two;
-
       input_line_pointer += 4;
       expression (expressionP);
       if (*input_line_pointer != ')')
@@ -1054,17 +1098,39 @@ md_operand (expressionP)
          return;
        }
       ++input_line_pointer;
-      if (expressionP->X_op != O_symbol
-         || expressionP->X_add_number != 0)
+      if (expressionP->X_op == O_symbol
+         && expressionP->X_add_number == 0
+         /* I think this test is unnecessary but just as a sanity check... */
+         && expressionP->X_op_symbol == NULL)
        {
-         as_bad ("expression too complex for %st");
+         expressionS two;
+
+         expressionP->X_op = O_right_shift;
+         two.X_op = O_constant;
+         two.X_add_symbol = two.X_op_symbol = NULL;
+         two.X_add_number = 2;
+         expressionP->X_op_symbol = make_expr_symbol (&two);
+       }
+      /* allow %st(sym1-sym2) */
+      else if (expressionP->X_op == O_subtract
+              && expressionP->X_add_symbol != NULL
+              && expressionP->X_op_symbol != NULL
+              && expressionP->X_add_number == 0)
+       {
+         expressionS two;
+
+         expressionP->X_add_symbol = make_expr_symbol (expressionP);
+         expressionP->X_op = O_right_shift;
+         two.X_op = O_constant;
+         two.X_add_symbol = two.X_op_symbol = NULL;
+         two.X_add_number = 2;
+         expressionP->X_op_symbol = make_expr_symbol (&two);
+       }
+      else
+       {
+         as_bad ("expression too complex for %%st");
          return;
        }
-      expressionP->X_op = O_right_shift;
-      two.X_op = O_constant;
-      two.X_add_symbol = two.X_op_symbol = NULL;
-      two.X_add_number = 2;
-      expressionP->X_op_symbol = make_expr_symbol (&two);
     }
 }
 
@@ -1132,8 +1198,10 @@ md_pcrel_from (fixP)
 {
   if (fixP->fx_addsy != (symbolS *) NULL
       && ! S_IS_DEFINED (fixP->fx_addsy))
-    /* Return offset from PC to delay slot.  Offsets are from there.  */
-    return 4;
+    {
+      /* The symbol is undefined.  Let the linker figure it out.  */
+      return 0;
+    }
 
   /* Return the address of the delay slot.  */
   return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
@@ -1168,25 +1236,31 @@ get_arc_exp_reloc_type (data_p, default_type, exp, expnew)
      expressionS *expnew;
 {
   /* If the expression is "symbol >> 2" we must change it to just "symbol",
-     as fix_new_exp can't handle it.  That's ok though.  What's really going
-     on here is that we're using ">> 2" as a special syntax for specifying
-     BFD_RELOC_ARC_B26.  */
-
-  if (exp->X_op == O_right_shift)
+     as fix_new_exp can't handle it.  Similarily for (symbol - symbol) >> 2.
+     That's ok though.  What's really going on here is that we're using
+     ">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26.  */
+
+  if (exp->X_op == O_right_shift
+      && exp->X_op_symbol != NULL
+      && exp->X_op_symbol->sy_value.X_op == O_constant
+      && exp->X_op_symbol->sy_value.X_add_number == 2
+      && exp->X_add_number == 0)
     {
       if (exp->X_add_symbol != NULL
          && (exp->X_add_symbol->sy_value.X_op == O_constant
-             || exp->X_add_symbol->sy_value.X_op == O_symbol)
-         && exp->X_op_symbol != NULL
-         && exp->X_op_symbol->sy_value.X_op == O_constant
-         && exp->X_op_symbol->sy_value.X_add_number == 2
-         && exp->X_add_number == 0)
+             || exp->X_add_symbol->sy_value.X_op == O_symbol))
        {
          *expnew = *exp;
          expnew->X_op = O_symbol;
          expnew->X_op_symbol = NULL;
          return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
        }
+      else if (exp->X_add_symbol != NULL
+              && exp->X_add_symbol->sy_value.X_op == O_subtract)
+       {
+         *expnew = exp->X_add_symbol->sy_value;
+         return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
+       }
     }
 
   *expnew = *exp;
@@ -1202,9 +1276,10 @@ get_arc_exp_reloc_type (data_p, default_type, exp, expnew)
    that, we determine the correct reloc code and put it back in the fixup.  */
 
 int
-md_apply_fix (fixP, valueP)
+md_apply_fix3 (fixP, valueP, seg)
      fixS *fixP;
      valueT *valueP;
+     segT seg;
 {
   /*char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;*/
   valueT value;
@@ -1226,7 +1301,16 @@ md_apply_fix (fixP, valueP)
       fixP->fx_done = 1;
     }
   else if (fixP->fx_pcrel)
-    value = *valueP;
+    {
+      value = *valueP;
+      /* ELF relocations are against symbols.
+        If this symbol is in a different section then we need to leave it for
+        the linker to deal with.  Unfortunately, md_pcrel_from can't tell,
+        so we have to undo it's effects here.  */
+      if (S_IS_DEFINED (fixP->fx_addsy)
+         && S_GET_SEGMENT (fixP->fx_addsy) != seg)
+       value += md_pcrel_from (fixP);
+    }
   else
     {
       value = fixP->fx_offset;
@@ -1322,7 +1406,6 @@ md_apply_fix (fixP, valueP)
                              value, 2);
          break;
        case BFD_RELOC_32:
-       case BFD_RELOC_ARC_B26:
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
                              value, 4);
          break;
@@ -1332,6 +1415,15 @@ md_apply_fix (fixP, valueP)
                              value, 8);
          break;
 #endif
+       case BFD_RELOC_ARC_B26:
+         /* If !fixP->fx_done then `value' is an implicit addend.
+            We must shift it right by 2 in this case as well because the
+            linker performs the relocation and then adds this in (as opposed
+            to adding this in and then shifting right by 2).  */
+         value >>= 2;
+         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+                             value, 4);
+         break;
        default:
          abort ();
        }
@@ -1364,10 +1456,11 @@ tc_gen_reloc (section, fixP)
                    fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type));
       return NULL;
     }
-  reloc->addend = fixP->fx_addnumber;
 
   assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
 
+  reloc->addend = fixP->fx_addnumber;
+
   return reloc;
 }
 \f