(tc_gen_reloc): Use bfd_get_reloc_code_name in error message.
[binutils-gdb.git] / gas / config / tc-alpha.c
index 61764ebdef3cb62b5c05512a9741b859ef1d6c34..2b82dd7e3d151e0e19bd21150814c986e5839e79 100644 (file)
@@ -54,6 +54,8 @@
  *    Date:    Jan 1993
  */
 
+#include <ctype.h>
+
 #include "as.h"
 #include "alpha-opcode.h"
 #include "subsegs.h"
@@ -87,7 +89,7 @@ static symbolS *gp;
 
 /* We'll probably be using this relocation frequently, and we
    will want to compare for it.  */
-static reloc_howto_type *gpdisp_hi16_howto;
+static const reloc_howto_type *gpdisp_hi16_howto;
 
 /* These are exported to ECOFF code.  */
 unsigned long alpha_gprmask, alpha_fprmask;
@@ -108,10 +110,28 @@ extern void s_globl (), s_long (), s_short (), s_space (), cons (), s_text (),
   s_data (), float_cons ();
 
 /* Static functions, needing forward declarations.  */
-static void s_mask (), s_base (), s_proc (), s_alpha_set ();
+static void s_base (), s_proc (), s_alpha_set ();
 static void s_gprel32 (), s_rdata (), s_sdata (), s_alpha_comm ();
 static int alpha_ip ();
 
+static void emit_unaligned_io PARAMS ((char *, int, valueT, int));
+static void emit_load_unal PARAMS ((int, valueT, int));
+static void emit_store_unal PARAMS ((int, valueT, int));
+static void emit_byte_manip_r PARAMS ((char *, int, int, int, int, int));
+static void emit_extract_r PARAMS ((int, int, int, int, int));
+static void emit_insert_r PARAMS ((int, int, int, int, int));
+static void emit_mask_r PARAMS ((int, int, int, int, int));
+static void emit_sign_extend PARAMS ((int, int));
+static void emit_bis_r PARAMS ((int, int, int));
+static int build_mem PARAMS ((int, int, int, bfd_signed_vma));
+static int build_operate_n PARAMS ((int, int, int, int, int));
+static void emit_sll_n PARAMS ((int, int, int));
+static void emit_ldah_num PARAMS ((int, bfd_vma, int));
+static void emit_addq_r PARAMS ((int, int, int));
+static void emit_lda_n PARAMS ((int, bfd_vma, int));
+static void emit_add64 PARAMS ((int, int, bfd_vma));
+static int in_range PARAMS ((bfd_vma, int, int));
+
 const pseudo_typeS md_pseudo_table[] =
 {
   {"common", s_comm, 0},       /* is this used? */
@@ -183,7 +203,7 @@ const char comment_chars[] = "#";
 /* Note that input_file.c hand checks for '#' at the beginning of the
    first line of the input file.  This is because the compiler outputs
    #NO_APP at the beginning of its output. */
-/* Also note that '/*' will always start a comment */
+/* Also note that C style comments are always recognized.  */
 const char line_comment_chars[] = "#!";
 
 /* Chars that can be used to separate mant from exp in floating point nums */
@@ -226,7 +246,6 @@ int
 tc_get_register (frame)
      int frame;
 {
-  int reg;
   int framereg = SP;
 
   SKIP_WHITESPACE ();
@@ -350,7 +369,7 @@ tc_gen_reloc (sec, fixp)
   reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
-  if (fixp->fx_r_type > BFD_RELOC_UNUSED || fixp->fx_r_type < 0)
+  if (fixp->fx_r_type > BFD_RELOC_UNUSED)
     abort ();
 
   if (fixp->fx_r_type == BFD_RELOC_ALPHA_GPDISP_HI16)
@@ -365,8 +384,8 @@ tc_gen_reloc (sec, fixp)
   assert (reloc->howto != 0);
   if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
     {
-      as_fatal ("bug in handling type-%d relocs", fixp->fx_r_type);
-      abort ();
+      as_fatal ("internal error? cannot generate `%s' relocation",
+               bfd_get_reloc_code_name (fixp->fx_r_type));
     }
   assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
 
@@ -406,11 +425,47 @@ s_base ()
   if (base_register < 0 || base_register > 31)
     {
       base_register = GP;
-      as_warn ("Bad base register, using $r.", base_register);
+      as_warn ("Bad base register, using $%d.", base_register);
     }
   demand_empty_rest_of_line ();
 }
 
+static int in_range (val, nbits, unsignedness)
+     bfd_vma val;
+     int nbits, unsignedness;
+{
+  /* Look at top bit of value that would be stored, figure out how it
+     would be extended by the hardware, and see if that matches the
+     original supplied value.  */
+  bfd_vma mask;
+  bfd_vma one = 1;
+  bfd_vma top_bit, stored_value, missing_bits;
+
+  mask = (one << nbits) - 1;
+  stored_value = val & mask;
+  top_bit = stored_value & (one << nbits - 1);
+  missing_bits = val & ~mask;
+  if (unsignedness)
+    {
+      return missing_bits == 0;
+    }
+  else
+    {
+      /* will sign-extend */
+      if (top_bit)
+       {
+         /* all remaining bits beyond mask should be one */
+         missing_bits |= mask;
+         return missing_bits + 1 == 0;
+       }
+      else
+       {
+         /* all other bits should be zero */
+         return missing_bits == 0;
+       }
+    }
+}
+
 static void
 s_gprel32 ()
 {
@@ -526,7 +581,8 @@ load_insn_table (ops, size)
 
       if (strchr (name, '/'))
        {
-         const char *name2, *p, *q;
+         char *name2, *p;
+         const char *q;
 
          name2 = xmalloc (strlen (name));
          p = name2;
@@ -553,15 +609,15 @@ load_insn_table (ops, size)
     }
 }
 
+static struct alpha_it clear_insn;
+
 /* This function is called once, at assembler startup time.  It should
    set up all the tables, etc. that the MD part of the assembler will
    need, that can be determined before arguments are parsed.  */
 void
 md_begin ()
 {
-  const char *retval;
-  int lose = 0;
-  unsigned int i = 0;
+  int i;
 
   op_hash = hash_new ();
   load_insn_table (alpha_opcodes, NUMOPCODES);
@@ -595,8 +651,11 @@ md_begin ()
   create_lita_section ();
   /* For handling the GP, create a symbol that won't be output in the
      symbol table.  We'll edit it out of relocs later.  */
-  gp = symbol_new ("<GP value>", lita_sec, 0x8000, &zero_address_frag);
-  symbol_remove (gp, &symbol_rootP, &symbol_lastP);
+  gp = symbol_create ("<GP value>", lita_sec, 0x8000, &zero_address_frag);
+
+  memset (&clear_insn, 0, sizeof (clear_insn));
+  for (i = 0; i < MAX_RELOCS; i++)
+    clear_insn.reloc[i].code = BFD_RELOC_NONE;
 }
 
 int optnum = 1;
@@ -732,8 +791,10 @@ alpha_fix_adjustable (f)
       return 0;
     case BFD_RELOC_GPREL32:
       return 1;
+    default:
+      return !alpha_force_relocation (f);
     }
-  return !alpha_force_relocation (f);
+  /*NOTREACHED*/
 }
 
 valueT
@@ -766,11 +827,7 @@ load_symbol_address (reg, insn)
   static symbolS *lita_sym;
 
   int x;
-  addressT reloc_addr;
   valueT retval;
-  char *p;
-  symbolS *sym;
-  valueT addend;
 
   if (!lita_sym)
     {
@@ -823,7 +880,7 @@ load_expression (reg, insn)
      int reg;
      struct alpha_it *insn;
 {
-  valueT addend;
+  valueT addend, addendhi, addendlo;
   int num_insns = 1;
 
   if (insn->reloc[0].exp.X_add_symbol->bsym->flags & BSF_SECTION_SYM)
@@ -838,22 +895,39 @@ load_expression (reg, insn)
   load_symbol_address (reg, insn);
   if (addend)
     {
-      num_insns++;
-      {
-       valueT x = addend;
-       if ((x & ~0x7fff) != 0
-           && (x & ~0x7fff) + 0x8000 != 0)
-         {
-           as_bad ("assembler not prepared to handle constants >16 bits yet");
-           addend = 0;
-         }
-      }
-      insn[1].opcode = (0x20000000     /* lda */
-                       | (reg << SA)
-                       | (reg << SB)
-                       | (addend & 0xffff));
-      insn[1].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
-      insn[1].reloc[0].exp = lituse_basereg;
+      if ((addend & ~0x7fffffff) != 0
+         && (addend & ~0x7fffffff) + 0x80000000 != 0)
+       {
+         as_bad ("assembler not prepared to handle constants >32 bits yet");
+         addend = 0;
+       }
+      addendlo = addend & 0xffff;
+      addend -= addendlo;
+      addendhi = addend >> 16;
+      if (addendlo & 0x8000)
+       addendhi++;
+      /* It appears that the BASEREG LITUSE reloc should not be used on
+        an LDAH instruction.  */
+      if (addendlo)
+       {
+         insn[1].opcode = (0x20000000  /* lda */
+                           | (reg << SA)
+                           | (reg << SB)
+                           | (addendlo & 0xffff));
+         insn[1].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+         insn[1].reloc[0].exp = lituse_basereg;
+         num_insns++;
+       }
+      if (addendhi)
+       {
+         insn[num_insns].opcode = (0x24000000
+                                   | (reg << SA)
+                                   | (reg << SB)
+                                   | (addendhi & 0xffff));
+         num_insns++;
+       }
+      if (num_insns == 1)
+       abort ();
       lituse_pending = 0;
     }
   return num_insns;
@@ -918,8 +992,6 @@ getExpression (str, this_insn)
   input_line_pointer = save_in;
 }
 
-/* All of these should soon be changed to just emit words to the
-   output frag...  */
 static void
 emit_unaligned_io (dir, addr_reg, addr_offset, reg)
      char *dir;
@@ -950,6 +1022,7 @@ emit_store_unal (addr_reg, addr_offset, reg)
 static void
 emit_byte_manip_r (op, in, mask, out, mode, which)
      char *op;
+     int in, mask, out, mode, which;
 {
   char buf[90];
   sprintf (buf, "%s%c%c $%d,$%d,$%d", op, mode, which, in, mask, out);
@@ -958,24 +1031,28 @@ emit_byte_manip_r (op, in, mask, out, mode, which)
 
 static void
 emit_extract_r (in, mask, out, mode, which)
+     int in, mask, out, mode, which;
 {
   emit_byte_manip_r ("ext", in, mask, out, mode, which);
 }
 
 static void
 emit_insert_r (in, mask, out, mode, which)
+     int in, mask, out, mode, which;
 {
   emit_byte_manip_r ("ins", in, mask, out, mode, which);
 }
 
 static void
 emit_mask_r (in, mask, out, mode, which)
+     int in, mask, out, mode, which;
 {
   emit_byte_manip_r ("msk", in, mask, out, mode, which);
 }
 
 static void
 emit_sign_extend (reg, size)
+     int reg, size;
 {
   char buf[90];
   sprintf (buf, "sll $%d,0x%x,$%d", reg, 64 - size, reg);
@@ -986,12 +1063,145 @@ emit_sign_extend (reg, size)
 
 static void
 emit_bis_r (in1, in2, out)
+     int in1, in2, out;
 {
   char buf[90];
   sprintf (buf, "bis $%d,$%d,$%d", in1, in2, out);
   md_assemble (buf);
 }
 
+static int
+build_mem (opc, ra, rb, disp)
+     int opc, ra, rb;
+     bfd_signed_vma disp;
+{
+  if ((disp >> 15) != 0
+      && (disp >> 15) + 1 != 0)
+    abort ();
+  return ((opc << 26) | (ra << SA) | (rb << SB) | (disp & 0xffff));
+}
+
+static int
+build_operate_n (opc, fn, ra, lit, rc)
+     int opc, fn, ra, rc;
+     int lit;
+{
+  if (lit & ~0xff)
+    abort ();
+  return ((opc << 26) | (fn << 5) | (ra << SA) | (lit << SN) | (1 << 12) | (rc << SC));
+}
+
+static void
+emit_sll_n (dest, disp, src)
+     int dest, disp, src;
+{
+  struct alpha_it insn = clear_insn;
+  insn.opcode = build_operate_n (0x12, 0x39, src, disp, dest);
+  emit_insn (&insn);
+}
+
+static void
+emit_ldah_num (dest, addend, src)
+     int dest, src;
+     bfd_vma addend;
+{
+  struct alpha_it insn = clear_insn;
+  insn.opcode = build_mem (0x09, dest, src, addend);
+  emit_insn (&insn);
+}
+
+static void
+emit_addq_r (in1, in2, out)
+     int in1, in2, out;
+{
+  struct alpha_it insn = clear_insn;
+  insn.opcode = 0x40000400 | (in1 << SA) | (in2 << SB) | (out << SC);
+  emit_insn (&insn);
+}
+
+static void
+emit_lda_n (dest, addend, src)
+     int dest, src;
+     bfd_vma addend;
+{
+  struct alpha_it insn = clear_insn;
+  insn.opcode = build_mem (0x08, dest, src, addend);
+  emit_insn (&insn);
+}
+
+static void
+emit_add64 (in, out, num)
+     int in, out;
+     bfd_vma num;
+{
+  bfd_signed_vma snum = num;
+
+  if (in_range (num, 16, 0))
+    {
+      emit_lda_n (out, num, in);
+      return;
+    }
+  if ((num & 0xffff) == 0
+      && in == ZERO
+      && in_range (snum >> 16, 16, 0))
+    {
+      emit_ldah_num (out, snum >> 16, in);
+      return;
+    }
+  /* I'm not sure this one is getting invoked when it could.  */
+  if ((num & 1) == 0 && in == ZERO)
+    {
+      if (in_range (snum >> 1, 16, 0))
+       {
+         emit_lda_n (out, snum >> 1, in);
+         emit_addq_r (out, out, out);
+         return;
+       }
+      else if (num & 0x1fffe == 0
+              && in_range (snum >> 17, 16, 0))
+       {
+         emit_ldah_num (out, snum >> 17, in);
+         emit_addq_r (out, out, out);
+         return;
+       }
+    }
+  if (in_range (num, 32, 0))
+    {
+      bfd_vma lo = num & 0xffff;
+      if (lo & 0x8000)
+       lo -= 0x10000;
+      num -= lo;
+      emit_ldah_num (out, snum >> 16, in);
+      if (lo)
+       emit_lda_n (out, lo, out);
+      return;
+    }
+
+  if (in != ZERO && in != AT && out != AT && at_ok)
+    {
+      emit_add64 (ZERO, AT, num);
+      emit_addq_r (AT, in, out);
+      return;
+    }
+
+  if (in != ZERO)
+    as_bad ("load expression too complex to expand");
+
+  /* Could check also for loading 16- or 32-bit value and shifting by
+     arbitrary displacement.  */
+
+  {
+    bfd_vma lo = snum & 0xffffffff;
+    if (lo & 0x80000000)
+      lo -= ((bfd_vma)0x10000000 << 4);
+    snum -= lo;
+    emit_add64 (ZERO, out, snum >> 32);
+    emit_sll_n (out, 32, out);
+    if (lo != 0)
+      emit_add64 (out, out, lo);
+  }
+}
+
 /* Note that for now, this function is called recursively (by way of
    calling md_assemble again).  Some of the macros defined as part of
    the assembly language are currently rewritten as sequences of
@@ -1050,11 +1260,8 @@ alpha_ip (str, insns)
     {
       opcode = pattern->match;
       num_gen = 1;
-      memset (insns, 0, sizeof (*insns));
-      for (i = 0; i < MAX_RELOCS; i++)
-       insns[0].reloc[i].code = BFD_RELOC_NONE;
-      for (i = 1; i < MAX_INSNS; i++)
-       insns[i] = insns[0];
+      for (i = 0; i < MAX_INSNS; i++)
+       insns[i] = clear_insn;
 
       /* Build the opcode, checking as we go to make sure that the
         operands match.  */
@@ -1317,21 +1524,52 @@ alpha_ip (str, insns)
                  else if (at_ok && macro_ok)
                    {
                      /* Constant value supplied, but it's too large.  */
-                     char expansion[64];
-                     sprintf (expansion, "lda $%d,%d($%d)", AT,
-                              insns[0].reloc[0].exp.X_add_number, ZERO);
-                     md_assemble (expansion);
-                     opcode |= 0x1000 /* use reg */  | (AT << SB);
+                     emit_add64 (ZERO, AT,
+                                 insns[0].reloc[0].exp.X_add_number);
+                     opcode &= ~ 0x1000;
+                     opcode |= (AT << SB);
                      insns[0].reloc[0].code = BFD_RELOC_NONE;
                    }
                  else
                    as_bad ("overflow in 8-bit literal field in `operate' format insn");
                }
+             else if (insns[0].reloc[0].code == BFD_RELOC_16
+                      && insns[0].reloc[0].exp.X_op == O_constant
+                      && !in_range (insns[0].reloc[0].exp.X_add_number,
+                                    16, 0))
+               {
+                 bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+                 if (OPCODE (opcode) == 0x08)
+                   {
+                     emit_add64 (ZERO, AT, val);
+                     opcode &= ~0x1000;
+                     opcode |= (AT << SB);
+                     insns[0].reloc[0].code = BFD_RELOC_NONE;
+                   }
+                 else if (OPCODE (opcode) == 0x09
+                          && in_range (val >> 16, 16, 0))
+                   {
+                     /* ldah with high operand - convert to low */
+                     insns[0].reloc[0].exp.X_add_number >>= 16;
+                   }
+                 else
+                   as_bad ("I don't know how to handle 32+ bit constants here yet, sorry.");
+               }
+             else if (insns[0].reloc[0].code == BFD_RELOC_32
+                      && insns[0].reloc[0].exp.X_op == O_constant)
+               {
+                 bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+                 bfd_signed_vma sval = val;
+                 if (val >> 32 != 0
+                     && sval >> 32 != 0
+                     && sval >> 32 != -1)
+                   as_bad ("I don't know how to handle 64 bit constants here yet, sorry.");
+               }
              continue;
 
            case 'F':
              {
-               int format, length, mode, i, size;
+               int format, length, mode, i;
                char temp[20 /*MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT*/];
                char *err;
                static const char formats[4] = "FGfd";
@@ -1402,7 +1640,6 @@ alpha_ip (str, insns)
              /* fall through */
 
            case 'G':           /* Addressing macros: GET */
-           get_macro:
              /* All it is missing is the expression, which is what we
                 will get now */
 
@@ -1419,34 +1656,25 @@ alpha_ip (str, insns)
                }
              if (insns[0].reloc[0].exp.X_op == O_constant)
                {
-                 /* This only handles 32bit numbers */
-                 register int val = insns[0].reloc[0].exp.X_add_number;
-                 register short sval;
+                 bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+                 bfd_vma top, low;
 
                  insns[0].reloc[0].code = BFD_RELOC_NONE;
                  insns[1].reloc[0].code = BFD_RELOC_NONE;
 
-                 sval = val;
-                 if ((sval != val) && (val & 0x8000))
-                   {
-                     val += 0x10000;
-                     sval = val;
-                   }
-
-                 if (optnum && (sval == val))
+                 low = val & 0xffff;
+                 if (low & 0x8000)
+                   low -= 0x10000;
+                 top = val - low;
+                 if (top)
                    {
-                     /* optimize away the ldah */
-                     num_gen = 1;
-                     opcode |= (ZERO << SB) | (val & 0xffff);
+                     emit_add64 (ZERO, AT, top);
+                     opcode |= AT << SB;
                    }
                  else
-                   {
-                     num_gen = 2;
-                     insns[1].opcode = opcode | (mask << SB) | (val & 0xffff);
-                     opcode = 0x24000000 /*ldah*/  |
-                       mask << SA | (ZERO << SB) |
-                       ((val >> 16) & 0xffff);
-                   }
+                   opcode |= ZERO << SB;
+                 opcode &= ~0x1000;
+                 opcode |= low & 0xffff;
                }
              else if (insns[0].reloc[0].exp.X_op == O_symbol)
                {
@@ -1634,7 +1862,7 @@ alpha_ip (str, insns)
 
                    {
                      /* Pick apart name and set flags.  */
-                     char *s = pattern->name;
+                     const char *s = pattern->name;
 
                      if (*s == 'u')
                        {
@@ -2066,12 +2294,12 @@ Alpha options:\n\
 -32addr                        treat addresses as 32-bit values\n\
 -F                     lack floating point instructions support\n\
 -m21064 | -m21066 | -m21164\n\
-                       specify variant of Alpha architecture\n\
--nocpp                 ignored\n");
+                       specify variant of Alpha architecture\n");
 }
 \f
 static void
 s_proc (is_static)
+     int is_static;
 {
   /* XXXX Align to cache linesize XXXXX */
   char *name;
@@ -2154,7 +2382,7 @@ md_pcrel_from (fixP)
 int
 alpha_do_align (n, fill)
      int n;
-     char *fill;
+     const char *fill;
 {
   if (!fill
       && (now_seg == text_section
@@ -2230,7 +2458,6 @@ md_apply_fix (fixP, valueP)
       *p |= value;
       value >>= 5;
       fixP->fx_done = 1;
-    check_zov:
       if (value != 0)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      "overflow in type-%d reloc", (int) fixP->fx_r_type);