Handle EV5 (21164/66/68) PALcode support.
[deliverable/binutils-gdb.git] / gas / config / tc-alpha.c
index f14e5e9d8280e54ccac0424de750fcbc01e313fc..c8da0cb4812481d154b7ddc3dfcf1760ff775a05 100644 (file)
  *  5-Oct-93  Alessandro Forin (af) at Carnegie-Mellon University
  *     First Checkin
  *
- * $Log$
- * Revision 1.1  1994/01/28 01:36:53  raeburn
- * New Alpha support files, based on files from CMU.
- * Still to do:
- *  - fix floating-point handling
- *  - figure out if we can adapt to using ../opcodes/alpha-opc.h
- *  - gcc bootstrap testing
- *  - 32-bit mode support?
- *  - test cross-assembly
- *
- *
  *    Author:  Alessandro Forin, Carnegie Mellon University
  *    Date:    Jan 1993
  */
 #include "alpha-opcode.h"
 #include "subsegs.h"
 
+/* @@ Will a simple 0x8000 work here?  If not, why not?  */
+#define GP_ADJUSTMENT  (0x8000 - 0x10)
+
+/* Which machine type is this?  Currently stores an integer for the
+   model, one of: 21064, 21066, 21164.  */
+static unsigned long machine;
+
 /* These are exported to relaxing code, even though we don't do any
    relaxing on this processor currently.  */
 const relax_typeS md_relax_table[1];
@@ -78,14 +74,15 @@ int md_long_jump_size = 4;
 /* handle of the OPCODE hash table */
 static struct hash_control *op_hash;
 
-/* sections we'll want to keep track of */
-static segT lita_sec, rdata, sdata;
+/* Sections and symbols we'll want to keep track of.  */
+static segT lita_sec, rdata, sdata, lit8_sec, lit4_sec;
+static symbolS *lit8_sym, *lit4_sym;
 
-/* setting for ".set [no]{at,macro}" */
+/* Setting for ".set [no]{at,macro}".  */
 static int at_ok = 1, macro_ok = 1;
 
 /* Keep track of global pointer.  */
-static valueT gp_value;
+valueT alpha_gp_value;
 static symbolS *gp;
 
 /* We'll probably be using this relocation frequently, and we
@@ -98,12 +95,19 @@ unsigned long alpha_gprmask, alpha_fprmask;
 /* Used for LITUSE relocations.  */
 static expressionS lituse_basereg, lituse_byteoff, lituse_jsr;
 
+/* Address size: In OSF/1 1.3, an undocumented "-32addr" option will
+   cause all addresses to be treated as 32-bit values in memory.  (The
+   in-register versions are all sign-extended to 64 bits, of course.)
+   Some other systems may want this option too.  */
+static int addr32;
+
 /* Imported functions -- they should be defined in header files somewhere.  */
 extern segT subseg_get ();
 extern PTR bfd_alloc_by_size_t ();
 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_gprel32 (), s_rdata (), s_sdata (), s_alpha_comm ();
 static int alpha_ip ();
@@ -148,7 +152,8 @@ const pseudo_typeS md_pseudo_table[] =
 #define        T9      23
 #define        T10     24
 #define        T11     25
-#define RA     26
+#define T12    26
+#define RA     26              /* note: same as T12 */
 #define        PV      27
 #define        AT      28
 #define        GP      29
@@ -179,7 +184,7 @@ const char comment_chars[] = "#";
    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 */
-const char line_comment_chars[] = "#";
+const char line_comment_chars[] = "#!";
 
 /* Chars that can be used to separate mant from exp in floating point nums */
 const char EXP_CHARS[] = "eE";
@@ -188,6 +193,7 @@ const char line_separator_chars[1];
 
 /* Chars that mean this number is a floating point constant, as in
    "0f12.456" or "0d1.2345e12".  */
+/* @@ Do all of these really get used on the alpha??  */
 char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 /* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
@@ -210,7 +216,7 @@ struct alpha_it {
   struct reloc_data reloc[MAX_RELOCS];
 };
 
-static int getExpression (char *str, struct alpha_it *insn);
+static void getExpression (char *str, struct alpha_it *insn);
 static char *expr_end;
 
 #define note_gpreg(R)          (alpha_gprmask |= (1 << (R)))
@@ -333,22 +339,12 @@ s_alpha_comm (ignore)
   demand_empty_rest_of_line ();
 }
 
-int
-alpha_local_label (name)
-     const char *name;
-{
-  if (name[0] == 'L' /* || name[0] == '$' */)
-    return 1;
-  return 0;
-}
-
 arelent *
 tc_gen_reloc (sec, fixp)
      asection *sec;
      fixS *fixp;
 {
   arelent *reloc;
-  bfd_reloc_code_real_type code;
 
   reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent));
   reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
@@ -374,13 +370,12 @@ tc_gen_reloc (sec, fixp)
     }
   assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
 
-  if (reloc->howto->pc_relative
-      && reloc->howto->pcrel_offset
-#if 1
-      && code != BFD_RELOC_ALPHA_GPDISP_HI16
-      && code != BFD_RELOC_ALPHA_GPDISP_LO16
-#endif
-    )
+  if (fixp->fx_r_type == BFD_RELOC_ALPHA_LITERAL)
+    {
+      /* fake out bfd_perform_relocation. sigh */
+      reloc->addend = -alpha_gp_value;
+    }
+  else if (reloc->howto->pc_relative && reloc->howto->pcrel_offset)
     {
       reloc->addend = fixp->fx_offset - reloc->address;
     }
@@ -443,88 +438,150 @@ s_gprel32 ()
 }
 
 static void
-create_lita_section ()
+create_literal_section (secp, name)
+     segT *secp;
+     const char *name;
 {
   segT current_section = now_seg;
   int current_subsec = now_subseg;
+  segT new_sec;
 
-  lita_sec = subseg_new (".lita", 0);
+  *secp = new_sec = subseg_new (name, 0);
   subseg_set (current_section, current_subsec);
-  bfd_set_section_flags (stdoutput, lita_sec,
+  bfd_set_section_alignment (stdoutput, new_sec, 3);
+  bfd_set_section_flags (stdoutput, new_sec,
                         SEC_RELOC | SEC_ALLOC | SEC_LOAD | SEC_READONLY
                         | SEC_DATA);
-  bfd_set_section_alignment (stdoutput, lita_sec, 3);
 }
 
-/* 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.  */
-void
-md_begin ()
+#define create_lita_section() create_literal_section (&lita_sec, ".lita")
+
+static valueT
+get_lit8_offset (val)
+     bfd_vma val;
 {
-  const char *retval;
-  int lose = 0;
-  unsigned int i = 0;
+  valueT retval;
+  if (lit8_sec == 0)
+    {
+      create_literal_section (&lit8_sec, ".lit8");
+      lit8_sym = section_symbol (lit8_sec);
+    }
+  retval = add_to_literal_pool ((symbolS *) 0, val, lit8_sec, 8);
+  if (retval >= 0xfff0)
+    as_fatal ("overflow in fp literal (.lit8) table");
+  return retval;
+}
 
-  op_hash = hash_new ();
+static valueT
+get_lit4_offset (val)
+     bfd_vma val;
+{
+  valueT retval;
+  if (lit4_sec == 0)
+    {
+      create_literal_section (&lit4_sec, ".lit4");
+      lit4_sym = section_symbol (lit4_sec);
+    }
+  retval = add_to_literal_pool ((symbolS *) 0, val, lit4_sec, 4);
+  if (retval >= 0xfff0)
+    as_fatal ("overflow in fp literal (.lit4) table");
+  return retval;
+}
+
+#define load_insn(NAME, OP)    (hash_insert (op_hash, (NAME), (PTR) (OP)))
+
+static void
+load_insn_table (ops, size)
+     struct alpha_opcode *ops;
+     int size;
+{
+  struct alpha_opcode *end = ops + size;
+  struct alpha_opcode *op;
+  const char *name;
 
-  for (i = 0; i < NUMOPCODES; )
+  for (op = ops; op < end; )
     {
-      const char *name = alpha_opcodes[i].name;
-      retval = hash_insert (op_hash, name, (PTR) & alpha_opcodes[i]);
+      const char *retval;
+
+      name = op->name;
+
+      retval = load_insn (op->name, op);
       if (retval)
-       {
-         as_bad ("internal error: can't hash opcode `%s': %s",
-                 alpha_opcodes[i].name, retval);
-         lose = 1;
-       }
+       as_fatal ("internal error: can't hash opcode `%s': %s",
+                 op->name, retval);
+
       do
-       ++i;
-      while (i < NUMOPCODES
-            && (alpha_opcodes[i].name == name
-                || !strcmp (alpha_opcodes[i].name, name)));
+       op++;
+      while (op < end
+            && (op->name == name
+                || !strcmp (op->name, name)));
     }
   /* Some opcodes include modifiers of various sorts with a "/mod"
      syntax, like the architecture documentation suggests.  However,
      for use with gcc at least, we also need to access those same
      opcodes without the "/".  */
-  for (i = 0; i < NUMOPCODES; )
+  for (op = ops; op < end; )
     {
-      const char *name = alpha_opcodes[i].name;
+      name = op->name;
+
       if (strchr (name, '/'))
        {
-         char *p = xmalloc (strlen (name));
-         const char *q = name;
-         char *q2 = p;
-
-         for (; *q; q++)
-           if (*q != '/')
-             *q2++ = *q;
-
-         *q2++ = 0;
-         retval = hash_insert (op_hash, p, (PTR) & alpha_opcodes[i]);
-         if (retval)
-           {
-             /* Ignore failures -- the opcode table does duplicate
-                some variants in different forms, like "hw_st/q" and
-                "hw_stq".  */
-#if 0
-             as_bad ("internal error: can't hash opcode variant `%s': %s",
-                     p, retval);
-             lose = 1;
-#endif
-           }
+         const char *name2, *p, *q;
+
+         name2 = xmalloc (strlen (name));
+         p = name2;
+         q = name;
+
+         while (*q)
+           if (*q == '/')
+             q++;
+           else
+             *p++ = *q++;
+         *p = 0;
+         /* Ignore failures -- the opcode table does duplicate some
+            variants in different forms, like "hw_stq" and "hw_st/q".
+            Maybe the variants can be eliminated, and this error checking
+            restored.  */
+         load_insn (name2, op);
        }
+
       do
-       ++i;
-      while (i < NUMOPCODES
-            && (alpha_opcodes[i].name == name
-                || !strcmp (alpha_opcodes[i].name, name)));
+       op++;
+      while (op < end
+            && (op->name == name
+                || !strcmp (op->name, name)));
     }
+}
 
+/* 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;
 
+  op_hash = hash_new ();
+  load_insn_table (alpha_opcodes, NUMOPCODES);
 
-  if (lose)
-    as_fatal ("Broken assembler.  No assembly attempted.");
+  /* Default to 21064 PAL instructions.  */
+  if (machine == 0)
+    machine = 21064;
+
+  switch (machine)
+    {
+    case 21064:
+    case 21066:
+      load_insn_table (alpha_pal21064_opcodes, NUM21064OPCODES);
+      break;
+    case 21164:
+      load_insn_table (alpha_pal21164_opcodes, NUM21164OPCODES);
+      break;
+    default:
+      as_fatal ("palcode set unknown (internal error)");
+    }
 
   lituse_basereg.X_op = O_constant;
   lituse_basereg.X_add_number = 1;
@@ -544,12 +601,48 @@ md_begin ()
 
 int optnum = 1;
 
+static void
+emit_insn (insn)
+     struct alpha_it *insn;
+{
+  char *toP;
+  int j;
+
+  toP = frag_more (4);
+
+  /* put out the opcode */
+  md_number_to_chars (toP, insn->opcode, 4);
+
+  /* put out the symbol-dependent stuff */
+  for (j = 0; j < MAX_RELOCS; j++)
+    {
+      struct reloc_data *r = &insn->reloc[j];
+      fixS *f;
+
+      if (r->code != BFD_RELOC_NONE)
+       {
+         if (r->exp.X_op == O_constant)
+           {
+             r->exp.X_add_symbol = section_symbol (absolute_section);
+             r->exp.X_op = O_symbol;
+           }
+         f = fix_new_exp (frag_now, (toP - frag_now->fr_literal), 4,
+                          &r->exp, r->pcrel, r->code);
+       }
+      if (r->code == BFD_RELOC_ALPHA_GPDISP_LO16)
+       {
+         static bit_fixS cookie;
+         /* @@ This'll make the range checking in write.c shut up.  */
+         f->fx_bit_fixP = &cookie;
+       }
+    }
+}
+
 void
 md_assemble (str)
      char *str;
 {
-  char *toP;
-  int i, j, count;
+  int i, count;
 #define        MAX_INSNS       5
   struct alpha_it insns[MAX_INSNS];
 
@@ -558,75 +651,45 @@ md_assemble (str)
     return;
 
   for (i = 0; i < count; i++)
-    {
-      toP = frag_more (4);
-
-      /* put out the opcode */
-      md_number_to_chars (toP, insns[i].opcode, 4);
-
-      /* put out the symbol-dependent stuff */
-      for (j = 0; j < MAX_RELOCS; j++)
-       {
-         struct reloc_data *r = &insns[i].reloc[j];
-         fixS *f;
-
-         if (r->code != BFD_RELOC_NONE)
-           {
-             if (r->exp.X_op == O_constant)
-               {
-                 r->exp.X_add_symbol = section_symbol (absolute_section);
-                 r->exp.X_op = O_symbol;
-               }
-             f = fix_new_exp (frag_now, (toP - frag_now->fr_literal), 4,
-                              &r->exp, r->pcrel, r->code);
-           }
-         if (r->code == BFD_RELOC_ALPHA_GPDISP_LO16)
-           {
-             static bit_fixS cookie;
-             /* This'll make the range checking in write.c shut up.  */
-             f->fx_bit_fixP = &cookie;
-           }
-       }
-    }
+    emit_insn (&insns[i]);
 }
 
-/* @@ Will a simple 0x8000 work here?  If not, why not?  */
-#define GP_ADJUSTMENT  (0x8000 - 0x10)
+static inline void
+maybe_set_gp (sec)
+     asection *sec;
+{
+  bfd_vma vma;
+  if (!sec)
+    return;
+  vma = bfd_get_section_vma (foo, sec);
+  if (vma && vma < alpha_gp_value)
+    alpha_gp_value = vma;
+}
 
 static void
 select_gp_value ()
 {
-  if (gp_value == 0)
-    /* Must be first time through -- pick a GP to use for this file.  */
-    {
-      bfd_vma lita_vma, sdata_vma;
-      if (lita_sec)
-       lita_vma = bfd_get_section_vma (abfd, lita_sec);
-      else
-       lita_vma = 0;
+  if (alpha_gp_value != 0)
+    abort ();
+
+  /* Get minus-one in whatever width...  */
+  alpha_gp_value = 0; alpha_gp_value--;
+
+  /* Select the smallest VMA of these existing sections.  */
+  maybe_set_gp (lita_sec);
+/* maybe_set_gp (sdata);   Was disabled before -- should we use it?  */
 #if 0
-      if (sdata)
-       sdata_vma = bfd_get_section_vma (abfd, sdata);
-      else
+  maybe_set_gp (lit8_sec);
+  maybe_set_gp (lit4_sec);
 #endif
-       sdata = 0;
-
-      if (lita_vma == 0
-      /* Who knows which order they'll get laid out in?  */
-         || (sdata_vma != 0 && sdata_vma < lita_vma))
-       gp_value = sdata_vma;
-      else
-       gp_value = lita_vma;
 
-      gp_value += GP_ADJUSTMENT;
+  alpha_gp_value += GP_ADJUSTMENT;
 
-      S_SET_VALUE (gp, gp_value);
+  S_SET_VALUE (gp, alpha_gp_value);
 
 #ifdef DEBUG1
-      printf ("Chose GP value of %lx\n", gp_value);
+  printf ("Chose GP value of %lx\n", alpha_gp_value);
 #endif
-      bfd_set_gp_value (stdoutput, gp_value);
-    }
 }
 
 int
@@ -648,6 +711,7 @@ alpha_force_relocation (f)
     case BFD_RELOC_8:
     case BFD_RELOC_23_PCREL_S2:
     case BFD_RELOC_14:
+    case BFD_RELOC_26:
       return 0;
     default:
       abort ();
@@ -672,29 +736,10 @@ alpha_fix_adjustable (f)
   return !alpha_force_relocation (f);
 }
 
-int 
-alpha_validate_fix (fixp, seg)
-     fixS *fixp;
-     segT seg;
-{
-  /* We must make sure we've got a good GP value if any relocations might
-     use it...  */
-  if (gp_value == 0)
-    select_gp_value ();
-  return 0;
-}
-
-int 
-alpha_frob_symbol (s)
-     symbolS *s;
-{
-  return 0;
-}
-
-unsigned long
+valueT
 md_section_align (seg, size)
      segT seg;
-     unsigned long size;
+     valueT size;
 {
 #ifdef OBJ_ECOFF
   /* This should probably be handled within BFD, or by pulling the
@@ -707,13 +752,11 @@ md_section_align (seg, size)
 }
 
 /* Add this thing to the .lita section and produce a LITERAL reloc referring
-   to it.
+   to it.  */
 
-   TODO:
-   Remove duplicates.
-   Set GP value properly, and have values in LITERAL references set
-   accordingly.
-   */
+/* Are we currently eligible to emit a LITUSE reloc for the literal
+   references just generated?  */
+static int lituse_pending;
 
 static void
 load_symbol_address (reg, insn)
@@ -739,23 +782,27 @@ load_symbol_address (reg, insn)
                                insn->reloc[0].exp.X_add_number,
                                lita_sec, 8);
 
-  /* @@ Get these numbers from GP setting.  */
-  retval -= GP_ADJUSTMENT;
-
   /* Now emit a LITERAL relocation for the original section.  */
   insn->reloc[0].exp.X_op = O_symbol;
   insn->reloc[0].exp.X_add_symbol = lita_sym;
   insn->reloc[0].exp.X_add_number = retval;
   insn->reloc[0].code = BFD_RELOC_ALPHA_LITERAL;
+  lituse_pending = 1;
 
   if (retval == 0x8000)
     /* Overflow? */
     as_fatal ("overflow in literal (.lita) table");
   x = retval;
-  insn->opcode = (0xa4000000   /* ldq */
-                 | (reg << SA)
-                 | (base_register << SB)
-                 | (x & 0xffff));
+  if (addr32)
+    insn->opcode = (0xa0000000 /* ldl */
+                   | (reg << SA)
+                   | (base_register << SB)
+                   | (x & 0xffff));
+  else
+    insn->opcode = (0xa4000000 /* ldq */
+                   | (reg << SA)
+                   | (base_register << SB)
+                   | (x & 0xffff));
   note_gpreg (base_register);
 }
 
@@ -779,15 +826,22 @@ load_expression (reg, insn)
   valueT addend;
   int num_insns = 1;
 
-  addend = insn->reloc[0].exp.X_add_number;
-  insn->reloc[0].exp.X_add_number = 0;
+  if (insn->reloc[0].exp.X_add_symbol->bsym->flags & BSF_SECTION_SYM)
+    {
+      addend = 0;
+    }
+  else
+    {
+      addend = insn->reloc[0].exp.X_add_number;
+      insn->reloc[0].exp.X_add_number = 0;
+    }
   load_symbol_address (reg, insn);
   if (addend)
     {
       num_insns++;
       {
        valueT x = addend;
-       if (x & ~0x7fff != 0
+       if ((x & ~0x7fff) != 0
            && (x & ~0x7fff) + 0x8000 != 0)
          {
            as_bad ("assembler not prepared to handle constants >16 bits yet");
@@ -800,11 +854,12 @@ load_expression (reg, insn)
                        | (addend & 0xffff));
       insn[1].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
       insn[1].reloc[0].exp = lituse_basereg;
+      lituse_pending = 0;
     }
   return num_insns;
 }
 
-static inline int
+static inline void
 getExpression (str, this_insn)
      char *str;
      struct alpha_it *this_insn;
@@ -861,14 +916,86 @@ getExpression (str, this_insn)
   /* XXX validate seg and exp, make sure they're reasonable */
   expr_end = input_line_pointer;
   input_line_pointer = save_in;
+}
 
-  return 0;
+/* 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;
+     int addr_reg, reg;
+     valueT addr_offset;
+{
+  char buf[90];
+  sprintf (buf, "%sq_u $%d,%ld($%d)", dir, reg, (long) addr_offset, addr_reg);
+  md_assemble (buf);
+}
+
+static void
+emit_load_unal (addr_reg, addr_offset, reg)
+     int addr_reg, reg;
+     valueT addr_offset;
+{
+  emit_unaligned_io ("ld", addr_reg, addr_offset, reg);
+}
+
+static void
+emit_store_unal (addr_reg, addr_offset, reg)
+     int addr_reg, reg;
+     valueT addr_offset;
+{
+  emit_unaligned_io ("st", addr_reg, addr_offset, reg);
+}
+
+static void
+emit_byte_manip_r (op, in, mask, out, mode, which)
+     char *op;
+{
+  char buf[90];
+  sprintf (buf, "%s%c%c $%d,$%d,$%d", op, mode, which, in, mask, out);
+  md_assemble (buf);
+}
+
+static void
+emit_extract_r (in, mask, out, mode, which)
+{
+  emit_byte_manip_r ("ext", in, mask, out, mode, which);
 }
 
-/* Note that for now, this function is called recursively.  Some of the
-   macros defined as part of the assembly language are currently
-   rewritten as sequences of strings to be assembled.  See, for example,
-   the handling of "divq".
+static void
+emit_insert_r (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)
+{
+  emit_byte_manip_r ("msk", in, mask, out, mode, which);
+}
+
+static void
+emit_sign_extend (reg, size)
+{
+  char buf[90];
+  sprintf (buf, "sll $%d,0x%x,$%d", reg, 64 - size, reg);
+  md_assemble (buf);
+  sprintf (buf, "sra $%d,0x%x,$%d", reg, 64 - size, reg);
+  md_assemble (buf);
+}
+
+static void
+emit_bis_r (in1, in2, out)
+{
+  char buf[90];
+  sprintf (buf, "bis $%d,$%d,$%d", in1, in2, out);
+  md_assemble (buf);
+}
+
+/* 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
+   strings to be assembled.  See, for example, the handling of "divq".
 
    For efficiency, this should be fixed someday.  */
 static int
@@ -1128,6 +1255,10 @@ alpha_ip (str, insns)
              insns[0].reloc[0].code = BFD_RELOC_8;
              goto immediate;
 
+           case 'I':           /* 26 bit immediate, for PALcode */
+             insns[0].reloc[0].code = BFD_RELOC_26;
+             goto immediate;
+
 #if 0
            case 't':           /* 12 bit 0...11 */
              insns[0].reloc = RELOC_0_12;
@@ -1142,7 +1273,6 @@ alpha_ip (str, insns)
 #else
            case 't':
            case '8':
-           case 'I':
              abort ();
 #endif
              /*FALLTHROUGH*/
@@ -1150,7 +1280,7 @@ alpha_ip (str, insns)
            immediate:
              if (*s == ' ')
                s++;
-             (void) getExpression (s, &insns[0]);
+             getExpression (s, &insns[0]);
              s = expr_end;
              /* Handle overflow in certain instructions by converting
                 to other instructions.  */
@@ -1199,6 +1329,64 @@ alpha_ip (str, insns)
                }
              continue;
 
+           case 'F':
+             {
+               int format, length, mode, i, size;
+               char temp[20 /*MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT*/];
+               char *err;
+               static const char formats[4] = "FGfd";
+               bfd_vma bits, offset;
+               char *old_input_line_pointer = input_line_pointer;
+
+               input_line_pointer = s;
+               SKIP_WHITESPACE ();
+               memset (temp, 0, sizeof (temp));
+               mode = (opcode >> 26) & 3;
+               format = formats[mode];
+               err = md_atof (format, temp, &length);
+               if (err)
+                 {
+                   as_bad ("Bad floating literal: %s", err);
+                   bits = 0;
+                 }
+               else
+                 {
+                   /* Generate little-endian number from byte sequence.  */
+                   bits = 0;
+                   for (i = length - 1; i >= 0; i--)
+                     bits += ((bfd_vma)(temp[i] & 0xff)) << (i * 8);
+                 }
+               switch (length)
+                 {
+                 case 8:
+                   offset = get_lit8_offset (bits) - 0x8000;
+                   insns[0].reloc[0].exp.X_add_symbol = lit8_sym;
+                   insns[0].reloc[0].exp.X_add_number = 0x8000;
+                   break;
+                 case 4:
+                   offset = get_lit4_offset (bits) - 0x8000;
+                   insns[0].reloc[0].exp.X_add_symbol = lit4_sym;
+                   insns[0].reloc[0].exp.X_add_number = 0x8000;
+                   break;
+                 default:
+                   abort ();
+                 }
+               insns[0].reloc[0].exp.X_op = O_symbol;
+               offset &= 0xffff;
+               num_gen = load_expression (AT, &insns[0]);
+               if (lituse_pending)
+                 {
+                   insns[num_gen].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+                   insns[num_gen].reloc[0].exp = lituse_basereg;
+                   lituse_pending = 0;
+                 }
+               insns[num_gen++].opcode = opcode | (AT << SB) | offset;
+               opcode = insns[0].opcode;
+               s = input_line_pointer;
+               input_line_pointer = old_input_line_pointer;
+             }
+             continue;
+
              /* The following two.. take advantage of the fact that
                 opcode already contains most of what we need to know.
                 We just prepend to the instr an "ldah
@@ -1220,7 +1408,7 @@ alpha_ip (str, insns)
 
              if (*s == ' ')
                s++;
-             (void) getExpression (s, &insns[0]);
+             getExpression (s, &insns[0]);
              s = expr_end;
 
              /* Must check for "lda ..,number" too */
@@ -1239,8 +1427,6 @@ alpha_ip (str, insns)
                  insns[1].reloc[0].code = BFD_RELOC_NONE;
 
                  sval = val;
-                 if (0)
-                   fprintf (stderr, "val %lx sval %lx\n", val, sval);
                  if ((sval != val) && (val & 0x8000))
                    {
                      val += 0x10000;
@@ -1277,13 +1463,23 @@ alpha_ip (str, insns)
                    tmp_reg = AT;
                  num_gen = load_expression (tmp_reg, insns);
                  opcode = insns[0].opcode;
-                 /* lda is opcode 8, 0x20000000 */
-                 if (OPCODE (old_opcode) != 0x08)
+                 /* lda is opcode 8, 0x20000000, and the macros that use
+                    this code have an opcode field of 0.  The latter
+                    require further processing, and we don't have the
+                    true opcode here.  */
+                 if (OPCODE (old_opcode) != 0
+                     && OPCODE (old_opcode) != 0x08)
                    {
                      struct alpha_it *i;
                      i = &insns[num_gen++];
-                     i->reloc[0].code = BFD_RELOC_NONE;
                      i->opcode = old_opcode | (tmp_reg << SB);
+
+                     if (lituse_pending)
+                       {
+                         i->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+                         i->reloc[0].exp = lituse_basereg;
+                         lituse_pending = 0;
+                       }
                    }
                }
              else
@@ -1307,7 +1503,7 @@ alpha_ip (str, insns)
              continue;
 
              /* Same failure modes as above, actually most of the
-                same code shared.  */
+                same code shared.  */
            case 'B':           /* Builtins */
              args++;
              switch (*args)
@@ -1355,7 +1551,8 @@ alpha_ip (str, insns)
                        insns[1].opcode |= addend & 0xffff;
                        insns[0].opcode |= ((addend >> 16)
                                            + (addend & 0x8000 ? 1 : 0));
-                       ecoff_set_gp_prolog_size (0);
+                       if (r2 == PV)
+                         ecoff_set_gp_prolog_size (0);
                      }
                      break;
                    default:
@@ -1390,7 +1587,7 @@ alpha_ip (str, insns)
                    /* We still have to parse the function name */
                    if (*s == ' ')
                      s++;
-                   (void) getExpression (s, &insns[0]);
+                   getExpression (s, &insns[0]);
                    etmp = insns[0].reloc[0].exp;
                    s = expr_end;
                    num_gen = load_expression (PV, &insns[0]);
@@ -1401,12 +1598,13 @@ alpha_ip (str, insns)
                                   | (mask << SA)
                                   | (PV << SB)
                                   | 0);
-                   if (num_gen == 2)
+                   if (lituse_pending)
                      {
                        /* LITUSE wasn't emitted yet */
                        jsr->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
                        jsr->reloc[0].exp = lituse_jsr;
                        r = &jsr->reloc[1];
+                       lituse_pending = 0;
                      }
                    else
                      r = &jsr->reloc[0];
@@ -1417,6 +1615,187 @@ alpha_ip (str, insns)
                  }
                  continue;
 
+               case 'd':
+                 /* Sub-word loads and stores.  We load the address into
+                    $at, which might involve using the `P' parameter
+                    processing too, then emit a sequence to get the job
+                    done, using unaligned memory accesses and byte
+                    manipulation, with t9 and t10 as temporaries.  */
+                 {
+                   /* Characteristics of access.  */
+                   int is_load, is_unsigned = 0, is_unaligned = 0;
+                   int mode_size, mode;
+                   /* Register operand.  */
+                   int reg;
+                   /* Addend for loads and stores.  */
+                   valueT addend;
+                   /* Which register do we use for the address?  */
+                   int addr;
+
+                   {
+                     /* Pick apart name and set flags.  */
+                     char *s = pattern->name;
+
+                     if (*s == 'u')
+                       {
+                         is_unaligned = 1;
+                         s++;
+                       }
+
+                     if (s[0] == 'l' && s[1] == 'd')
+                       is_load = 1;
+                     else if (s[0] == 's' && s[1] == 't')
+                       is_load = 0;
+                     else
+                       as_fatal ("unrecognized sub-word access insn `%s'",
+                                 str);
+                     s += 2;
+
+                     mode = *s++;
+                     if (mode == 'b') mode_size = 1;
+                     else if (mode == 'w') mode_size = 2;
+                     else if (mode == 'l') mode_size = 4;
+                     else if (mode == 'q') mode_size = 8;
+                     else abort ();
+
+                     if (*s == 'u')
+                       {
+                         is_unsigned = 1;
+                         s++;
+                       }
+
+                     assert (*s == 0);
+
+                     /* Longwords are always kept sign-extended.  */
+                     if (mode == 'l' && is_unsigned)
+                       abort ();
+                     /* There's no special unaligned byte handling.  */
+                     if (mode == 'b' && is_unaligned)
+                       abort ();
+                     /* Stores don't care about signedness.  */
+                     if (!is_load && is_unsigned)
+                       abort ();
+                   }
+
+                   if (args[-2] == 'P')
+                     {
+                       addr = AT;
+                       addend = 0;
+                     }
+                   else
+                     {
+                       /* foo r1,num(r2)
+                          r2 -> mask
+                          r1 -> (opcode >> SA) & 31
+                          num -> insns->reloc[0].*
+
+                          We want to emit "lda at,num(r2)", since these
+                          operations require the use of a single register
+                          with the starting address of the memory operand
+                          we want to access.
+
+                          We could probably get away without doing this
+                          (and use r2 below, with the addend for the
+                          actual reads and writes) in cases where the
+                          addend is known to be a multiple of 8.  */
+                       int r2 = mask;
+                       int r1 = (opcode >> SA) & 31;
+
+                       if (insns[0].reloc[0].code == BFD_RELOC_NONE)
+                         addend = 0;
+                       else if (insns[0].reloc[0].code == BFD_RELOC_16)
+                         {
+                           if (insns[0].reloc[0].exp.X_op != O_constant)
+                             abort ();
+                           addend = insns[0].reloc[0].exp.X_add_number;
+                         }
+                       else
+                         abort ();
+
+                       if (addend + mode_size - 1 < 0x7fff
+                           && (addend % 8) == 0
+                           && (r2 < T9 || r2 > T12))
+                         {
+                           addr = r2;
+                           num_gen = 0;
+                         }
+                       else
+                         {
+                           /* Let later relocation processing deal
+                              with the addend field.  */
+                           insns[num_gen-1].opcode = (0x20000000 /* lda */
+                                                      | (AT << SA)
+                                                      | (r2 << SB));
+                           addr = AT;
+                           addend = 0;
+                         }
+                       reg = r1;
+                     }
+
+                   /* Because the emit_* routines append directly to
+                      the current frag, we now need to flush any
+                      pending insns.  */
+                   {
+                     int i;
+                     for (i = 0; i < num_gen; i++)
+                       emit_insn (&insns[i]);
+                     num_gen = 0;
+                   }
+
+                   if (is_load)
+                     {
+                       int reg2, reg3;
+
+                       if (is_unaligned)
+                         reg2 = T9, reg3 = T10;
+                       else
+                         reg2 = reg;
+
+                       emit_load_unal (addr, addend, T9);
+                       if (is_unaligned)
+                         emit_load_unal (addr, addend + mode_size - 1, T10);
+                       emit_extract_r (T9, addr, reg2, mode, 'l');
+                       if (is_unaligned)
+                         {
+                           emit_extract_r (T10, addr, reg3, mode, 'h');
+                           emit_bis_r (T9, T10, reg);
+                         }
+                       if (!is_unsigned)
+                         emit_sign_extend (reg, mode_size * 8);
+                     }
+                   else
+                     {
+                       /* The second word gets processed first
+                          because if the address does turn out to be
+                          aligned, the processing for the second word
+                          will be pushing around all-zeros, and the
+                          entire value will be handled as the `first'
+                          word.  So we want to store the `first' word
+                          last.  */
+                       /* Pair these up so that the memory loads get
+                          separated from each other, as well as being
+                          well in advance of the uses of the values
+                          loaded.  */
+                       if (is_unaligned)
+                         {
+                           emit_load_unal (addr, addend + mode_size - 1, T11);
+                           emit_insert_r (reg, addr, T12, mode, 'h');
+                         }
+                       emit_load_unal (addr, addend, T9);
+                       emit_insert_r (reg, addr, T10, mode, 'l');
+                       if (is_unaligned)
+                         emit_mask_r (T12, addr, T12, mode, 'h');
+                       emit_mask_r (T10, addr, T10, mode, 'l');
+                       if (is_unaligned)
+                         emit_bis_r (T11, T12, T11);
+                       emit_bis_r (T9, T10, T9);
+                       if (is_unaligned)
+                         emit_store_unal (addr, addend + mode_size - 1, T11);
+                       emit_store_unal (addr, addend, T9);
+                     }
+                 }
+                 return 0;
+
                  /* DIVISION and MODULUS. Yech.
                       Convert  OP x,y,result
                       to       mov x,t10
@@ -1558,6 +1937,7 @@ md_atof (type, litP, sizeP)
     {
       /* VAX floats */
     case 'G':
+      /* VAX md_atof doesn't like "G" for some reason.  */
       type = 'g';
     case 'F':
     case 'D':
@@ -1654,9 +2034,43 @@ md_parse_option (argP, cntP, vecP)
       return 1;
     }
 #endif
+  if (!strcmp (*argP, "32addr"))
+    {
+      addr32 = 1;
+      *argP += 6;
+      return 1;
+    }
   if (!strcmp (*argP, "nocpp"))
     {
       *argP += 5;
+      return 1;
+    }
+  if (**argP == 'm')
+    {
+      unsigned long mach;
+
+      (*argP)++;
+      if (!strcmp (*argP, "21064"))
+       mach = 21064;
+      else if (!strcmp (*argP, "21066"))
+       mach = 21066;
+      else if (!strcmp (*argP, "21164"))
+       mach = 21164;
+      else
+       {
+         mach = 0;
+         (*argP)--;
+         return 0;
+       }
+      (*argP) += 5;
+
+      if (machine != 0 && machine != mach)
+       {
+         as_warn ("machine type %lu already chosen, overriding with %lu",
+                  machine, mach);
+       }
+      machine = mach;
+
       return 1;
     }
   return 0;
@@ -1753,7 +2167,7 @@ alpha_do_align (n, fill)
          || !strcmp (now_seg->name, ".init")
          || !strcmp (now_seg->name, ".fini")))
     {
-      static const char nop_pattern[] =        { 0x1f, 0x04, 0xff, 0x47 };
+      static const unsigned char nop_pattern[] = { 0x1f, 0x04, 0xff, 0x47 };
       frag_align_pattern (n, nop_pattern, sizeof (nop_pattern));
       return 1;
     }
@@ -1829,8 +2243,11 @@ md_apply_fix (fixP, valueP)
       return 3;
 
     case BFD_RELOC_32:
+      size = 4;
+      goto do_it;
     case BFD_RELOC_64:
-      return 42;
+      size = 8;
+      goto do_it;
     case BFD_RELOC_16:
       /* Don't want overflow checking.  */
       size = 2;
@@ -1844,6 +2261,28 @@ md_apply_fix (fixP, valueP)
        }
       break;
 
+    case BFD_RELOC_26:
+      if (fixP->fx_addsy != 0
+         && fixP->fx_addsy->bsym->section != absolute_section)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     "PALcode instructions require immediate constant function code");
+      else if (value >> 26 != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     "overflow in 26-bit PALcode function field");
+      *p++ = value & 0xff;
+      value >>= 8;
+      *p++ = value & 0xff;
+      value >>= 8;
+      *p++ = value & 0xff;
+      value >>= 8;
+      {
+       char x = *p;
+       x &= ~3;
+       x |= (value & 3);
+       *p++ = x;
+      }
+      goto done;
+
     case BFD_RELOC_14:
       if (fixP->fx_addsy != 0
          && fixP->fx_addsy->bsym->section != absolute_section)
@@ -1874,7 +2313,7 @@ md_apply_fix (fixP, valueP)
 
     case BFD_RELOC_GPREL32:
       assert (fixP->fx_subsy == gp);
-      value = - gp_value;      /* huh?  this works... */
+      value = - alpha_gp_value;        /* huh?  this works... */
       fixP->fx_subsy = 0;
       md_number_to_chars (p, value, 4);
       break;
@@ -1904,11 +2343,12 @@ md_apply_fix (fixP, valueP)
 }
 
 void
-alpha_end ()
+alpha_frob_ecoff_data ()
 {
+  select_gp_value ();
   /* $zero and $f31 are read-only */
-  alpha_gprmask &= ~(1L << 31);
-  alpha_fprmask &= ~(1L << 31);
+  alpha_gprmask &= ~1;
+  alpha_fprmask &= ~1;
 }
 
 /* The Alpha has support for some VAX floating point types, as well as for
This page took 0.038076 seconds and 4 git commands to generate.