* config/tc-alpha.c (s_alpha_usepv): New.
[binutils-gdb.git] / gas / config / tc-alpha.c
index abbc96b194c0ef9f6c0ef0aa53717aa186cb28e7..83dbd34444835510eb1850503918e1cc904edd75 100644 (file)
@@ -1,6 +1,6 @@
 /* tc-alpha.c - Processor-specific code for the DEC Alpha AXP CPU.
    Copyright 1989, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002 Free Software Foundation, Inc.
+   2001, 2002, 2003 Free Software Foundation, Inc.
    Contributed by Carnegie Mellon University, 1993.
    Written by Alessandro Forin, based on earlier gas-1.38 target CPU files.
    Modified by Ken Raeburn for gas-2.x and ECOFF support.
@@ -60,6 +60,7 @@
 #ifdef OBJ_ELF
 #include "elf/alpha.h"
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 #endif
 
 #include "safe-ctype.h"
@@ -266,6 +267,7 @@ static void s_alpha_file PARAMS ((int));
 static void s_alpha_loc PARAMS ((int));
 static void s_alpha_stab PARAMS ((int));
 static void s_alpha_coff_wrapper PARAMS ((int));
+static void s_alpha_usepv PARAMS ((int));
 #endif
 #ifdef OBJ_EVAX
 static void s_alpha_section PARAMS ((int));
@@ -407,11 +409,6 @@ static symbolS *alpha_lit8_symbol;
 static offsetT alpha_lit8_literal;
 #endif
 
-#ifdef OBJ_ELF
-/* The active .ent symbol.  */
-static symbolS *alpha_cur_ent_sym;
-#endif
-
 /* Is the assembler not allowed to use $at?  */
 static int alpha_noat_on = 0;
 
@@ -1448,8 +1445,6 @@ alpha_force_relocation (f)
     case BFD_RELOC_ALPHA_LINKAGE:
     case BFD_RELOC_ALPHA_CODEADDR:
     case BFD_RELOC_ALPHA_BRSGP:
-    case BFD_RELOC_VTABLE_INHERIT:
-    case BFD_RELOC_VTABLE_ENTRY:
     case BFD_RELOC_ALPHA_TLSGD:
     case BFD_RELOC_ALPHA_TLSLDM:
     case BFD_RELOC_ALPHA_GOTDTPREL16:
@@ -1466,7 +1461,7 @@ alpha_force_relocation (f)
       break;
     }
 
-  return S_FORCE_RELOC (f->fx_addsy);
+  return generic_force_reloc (f);
 }
 
 /* Return true if we can partially resolve a relocation now.  */
@@ -1528,7 +1523,7 @@ alpha_fix_adjustable (f)
        const char *name;
        int offset = 0;
 
-       if (S_FORCE_RELOC (sym))
+       if (generic_force_reloc (f))
          return 0;
 
        switch (S_GET_OTHER (sym) & STO_ALPHA_STD_GPLOAD)
@@ -4384,6 +4379,25 @@ s_alpha_sdata (ignore)
 #endif
 
 #ifdef OBJ_ELF
+struct alpha_elf_frame_data
+{
+  symbolS *func_sym;
+  symbolS *func_end_sym;
+  symbolS *prologue_sym;
+  unsigned int mask;
+  unsigned int fmask;
+  int fp_regno;
+  int ra_regno;
+  offsetT frame_size;
+  offsetT mask_offset;
+  offsetT fmask_offset;
+
+  struct alpha_elf_frame_data *next;
+};
+
+static struct alpha_elf_frame_data *all_frame_data;
+static struct alpha_elf_frame_data **plast_frame_data = &all_frame_data;
+static struct alpha_elf_frame_data *cur_frame_data;
 
 /* Handle the .section pseudo-op.  This is like the usual one, but it
    clears alpha_insn_label and restores auto alignment.  */
@@ -4420,12 +4434,21 @@ s_alpha_ent (dummy)
        {
          symbolS *sym;
 
-         if (alpha_cur_ent_sym)
+         if (cur_frame_data)
            as_warn (_("nested .ent directives"));
 
          sym = symbol_find_or_make (name);
          symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
-         alpha_cur_ent_sym = sym;
+
+         cur_frame_data = calloc (1, sizeof (*cur_frame_data));
+         cur_frame_data->func_sym = sym;
+
+         /* Provide sensible defaults.  */
+         cur_frame_data->fp_regno = 30;        /* sp */
+         cur_frame_data->ra_regno = 26;        /* ra */
+
+         *plast_frame_data = cur_frame_data;
+         plast_frame_data = &cur_frame_data->next;
 
          /* The .ent directive is sometimes followed by a number.  Not sure
             what it really means, but ignore it.  */
@@ -4465,22 +4488,27 @@ s_alpha_end (dummy)
          symbolS *sym;
 
          sym = symbol_find (name);
-         if (sym != alpha_cur_ent_sym)
+         if (!cur_frame_data)
+           as_warn (_(".end directive without matching .ent"));
+         else if (sym != cur_frame_data->func_sym)
            as_warn (_(".end directive names different symbol than .ent"));
 
          /* Create an expression to calculate the size of the function.  */
          if (sym)
            {
-             symbol_get_obj (sym)->size =
-               (expressionS *) xmalloc (sizeof (expressionS));
-             symbol_get_obj (sym)->size->X_op = O_subtract;
-             symbol_get_obj (sym)->size->X_add_symbol
-               = symbol_new ("L0\001", now_seg, frag_now_fix (), frag_now);
-             symbol_get_obj (sym)->size->X_op_symbol = sym;
-             symbol_get_obj (sym)->size->X_add_number = 0;
+             OBJ_SYMFIELD_TYPE *obj = symbol_get_obj (sym);
+             expressionS *exp = xmalloc (sizeof (expressionS));
+
+             obj->size = exp;
+             exp->X_op = O_subtract;
+             exp->X_add_symbol = symbol_temp_new_now ();
+             exp->X_op_symbol = sym;
+             exp->X_add_number = 0;
+
+             cur_frame_data->func_end_sym = exp->X_add_symbol;
            }
 
-         alpha_cur_ent_sym = NULL;
+         cur_frame_data = NULL;
 
          *input_line_pointer = name_end;
        }
@@ -4500,7 +4528,45 @@ s_alpha_mask (fp)
        ecoff_directive_mask (0);
     }
   else
-    discard_rest_of_line ();
+    {
+      long val;
+      offsetT offset;
+
+      if (!cur_frame_data)
+       {
+         if (fp)
+           as_warn (_(".fmask outside of .ent"));
+         else
+           as_warn (_(".mask outside of .ent"));
+         discard_rest_of_line ();
+         return;
+       }
+
+      if (get_absolute_expression_and_terminator (&val) != ',')
+       {
+         if (fp)
+           as_warn (_("bad .fmask directive"));
+         else
+           as_warn (_("bad .mask directive"));
+         --input_line_pointer;
+         discard_rest_of_line ();
+         return;
+       }
+
+      offset = get_absolute_expression ();
+      demand_empty_rest_of_line ();
+
+      if (fp)
+       {
+         cur_frame_data->fmask = val;
+          cur_frame_data->fmask_offset = offset;
+       }
+      else
+       {
+         cur_frame_data->mask = val;
+         cur_frame_data->mask_offset = offset;
+       }
+    }
 }
 
 static void
@@ -4510,7 +4576,36 @@ s_alpha_frame (dummy)
   if (ECOFF_DEBUGGING)
     ecoff_directive_frame (0);
   else
-    discard_rest_of_line ();
+    {
+      long val;
+
+      if (!cur_frame_data)
+       {
+         as_warn (_(".frame outside of .ent"));
+         discard_rest_of_line ();
+         return;
+       }
+
+      cur_frame_data->fp_regno = tc_get_register (1);
+
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer++ != ','
+         || get_absolute_expression_and_terminator (&val) != ',')
+       {
+         as_warn (_("bad .frame directive"));
+         --input_line_pointer;
+         discard_rest_of_line ();
+         return;
+       }
+      cur_frame_data->frame_size = val;
+
+      cur_frame_data->ra_regno = tc_get_register (0);
+
+      /* Next comes the "offset of saved $a0 from $sp".  In gcc terms
+        this is current_function_pretend_args_size.  There's no place
+        to put this value, so ignore it.  */
+      s_ignore (42);
+    }
 }
 
 static void
@@ -4526,7 +4621,7 @@ s_alpha_prologue (ignore)
   if (ECOFF_DEBUGGING)
     sym = ecoff_get_cur_proc_sym ();
   else
-    sym = alpha_cur_ent_sym;
+    sym = cur_frame_data ? cur_frame_data->func_sym : NULL;
 
   if (sym == NULL)
     {
@@ -4551,6 +4646,9 @@ s_alpha_prologue (ignore)
       as_bad (_("Invalid argument %d to .prologue."), arg);
       break;
     }
+
+  if (cur_frame_data)
+    cur_frame_data->prologue_sym = symbol_temp_new_now ();
 }
 
 static char *first_file_directive;
@@ -4644,8 +4742,146 @@ s_alpha_coff_wrapper (which)
       ignore_rest_of_line ();
     }
 }
+
+/* Called at the end of assembly.  Here we emit unwind info for frames
+   unless the compiler has done it for us.  */
+
+void
+alpha_elf_md_end (void)
+{
+  struct alpha_elf_frame_data *p;
+
+  if (cur_frame_data)
+    as_warn (_(".ent directive without matching .end"));
+
+  /* If someone has generated the unwind info themselves, great.  */
+  if (bfd_get_section_by_name (stdoutput, ".eh_frame") != NULL)
+    return;
+
+  /* Generate .eh_frame data for the unwind directives specified.  */
+  for (p = all_frame_data; p ; p = p->next)
+    if (p->prologue_sym)
+      {
+       /* Create a temporary symbol at the same location as our
+          function symbol.  This prevents problems with globals.  */
+       cfi_new_fde (symbol_temp_new (S_GET_SEGMENT (p->func_sym),
+                                     S_GET_VALUE (p->func_sym),
+                                     symbol_get_frag (p->func_sym)));
+
+       cfi_set_return_column (p->ra_regno);
+       cfi_add_CFA_def_cfa_register (30);
+       if (p->fp_regno != 30 || p->mask || p->fmask || p->frame_size)
+         {
+           unsigned int mask;
+           offsetT offset;
+
+           cfi_add_advance_loc (p->prologue_sym);
+
+           if (p->fp_regno != 30)
+             if (p->frame_size != 0)
+               cfi_add_CFA_def_cfa (p->fp_regno, p->frame_size);
+             else
+               cfi_add_CFA_def_cfa_register (p->fp_regno);
+           else if (p->frame_size != 0)
+             cfi_add_CFA_def_cfa_offset (p->frame_size);
+
+           mask = p->mask;
+           offset = p->mask_offset;
+
+           /* Recall that $26 is special-cased and stored first.  */
+           if ((mask >> 26) & 1)
+             {
+               cfi_add_CFA_offset (26, offset);
+               offset += 8;
+               mask &= ~(1 << 26);
+             }
+           while (mask)
+             {
+               unsigned int i;
+               i = mask & -mask;
+               mask ^= i;
+               i = ffs (i) - 1;
+
+               cfi_add_CFA_offset (i, offset);
+               offset += 8;
+             }
+
+           mask = p->fmask;
+           offset = p->fmask_offset;
+           while (mask)
+             {
+               unsigned int i;
+               i = mask & -mask;
+               mask ^= i;
+               i = ffs (i) - 1;
+
+               cfi_add_CFA_offset (i + 32, offset);
+               offset += 8;
+             }
+         }
+
+       cfi_end_fde (p->func_end_sym);
+      }
+}
+
+static void
+s_alpha_usepv (int unused ATTRIBUTE_UNUSED)
+{
+  char *name, name_end;
+  char *which, which_end;
+  symbolS *sym;
+  int other;
+
+  name = input_line_pointer;
+  name_end = get_symbol_end ();
+
+  if (! is_name_beginner (*name))
+    {
+      as_bad (_(".usepv directive has no name"));
+      *input_line_pointer = name_end;
+      ignore_rest_of_line ();
+      return;
+    }
+
+  sym = symbol_find_or_make (name);
+  *input_line_pointer++ = name_end;
+
+  if (name_end != ',')
+    {
+      as_bad (_(".usepv directive has no type"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  SKIP_WHITESPACE ();
+  which = input_line_pointer;
+  which_end = get_symbol_end ();
+
+  if (strcmp (which, "no") == 0)
+    other = STO_ALPHA_NOPV;
+  else if (strcmp (which, "std") == 0)
+    other = STO_ALPHA_STD_GPLOAD;
+  else
+    {
+      as_bad (_("unknown argument for .usepv"));
+      other = 0;
+    }
+  
+  *input_line_pointer = which_end;
+  demand_empty_rest_of_line ();
+
+  S_SET_OTHER (sym, other | (S_GET_OTHER (sym) & ~STO_ALPHA_STD_GPLOAD));
+}
 #endif /* OBJ_ELF */
 
+/* Standard calling conventions leaves the CFA at $30 on entry.  */
+
+void
+alpha_cfi_frame_initial_instructions ()
+{
+  cfi_add_CFA_def_cfa_register (30);
+}
+
 #ifdef OBJ_EVAX
 
 /* Handle the section specific pseudo-op.  */
@@ -5068,8 +5304,6 @@ s_alpha_file (ignore)
   int length;
   static char case_hack[32];
 
-  extern char *demand_copy_string PARAMS ((int *lenP));
-
   sprintf (case_hack, "<CASE:%01d%01d>",
           alpha_flag_hash_long_names, alpha_flag_show_after_trunc);
 
@@ -5493,6 +5727,7 @@ const pseudo_typeS md_pseudo_table[] = {
   {"loc", s_alpha_loc, 9},
   {"stabs", s_alpha_stab, 's'},
   {"stabn", s_alpha_stab, 'n'},
+  {"usepv", s_alpha_usepv, 0},
   /* COFF debugging related pseudos.  */
   {"begin", s_alpha_coff_wrapper, 0},
   {"bend", s_alpha_coff_wrapper, 1},
@@ -5639,7 +5874,7 @@ alpha_elf_section_letter (letter, ptr_msg)
     return SHF_ALPHA_GPREL;
 
   *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S,G,T in string");
-  return 0;
+  return -1;
 }
 
 /* Map SHF_ALPHA_GPREL to SEC_SMALL_DATA.  */