daily update
[external/binutils.git] / gas / config / tc-frv.c
index 2b8d3ea..4551d75 100644 (file)
@@ -1,11 +1,12 @@
 /* tc-frv.c -- Assembler for the Fujitsu FRV.
-   Copyright 2002, 2003 Free Software Foundation.
+   Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   Free Software Foundation. Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
 
    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, 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
 
-#include <stdio.h>
 #include "as.h"
 #include "subsegs.h"     
 #include "symcat.h"
@@ -115,7 +115,7 @@ static struct vliw_insn_list        *current_vliw_insn;
 
 const char comment_chars[]        = ";";
 const char line_comment_chars[]   = "#";
-const char line_separator_chars[] = ""; 
+const char line_separator_chars[] = "!"; 
 const char EXP_CHARS[]            = "eE";
 const char FLT_CHARS[]            = "dD";
 
@@ -148,6 +148,11 @@ static FRV_VLIW vliw;
 #define DEFAULT_FLAGS  EF_FRV_CPU_FR400
 
 #else
+#ifdef  DEFAULT_CPU_FR550
+#define DEFAULT_MACHINE        bfd_mach_fr550
+#define DEFAULT_FLAGS  EF_FRV_CPU_FR550
+
+#else
 #define DEFAULT_MACHINE        bfd_mach_fr500
 #define DEFAULT_FLAGS  EF_FRV_CPU_FR500
 #endif
@@ -155,15 +160,23 @@ static FRV_VLIW vliw;
 #endif
 #endif
 #endif
+#endif
+
+#ifdef TE_LINUX
+# define DEFAULT_FDPIC EF_FRV_FDPIC
+#else
+# define DEFAULT_FDPIC 0
+#endif
 
 static unsigned long frv_mach = bfd_mach_frv;
+static bfd_boolean fr400_audio;
 
 /* Flags to set in the elf header */
-static flagword frv_flags = DEFAULT_FLAGS;
+static flagword frv_flags = DEFAULT_FLAGS | DEFAULT_FDPIC;
 
 static int frv_user_set_flags_p = 0;
 static int frv_pic_p = 0;
-static const char *frv_pic_flag = (const char *)0;
+static const char *frv_pic_flag = DEFAULT_FDPIC ? "-mfdpic" : (const char *)0;
 
 /* Print tomcat-specific debugging info.  */
 static int tomcat_debug = 0;
@@ -174,9 +187,8 @@ static int tomcat_doubles = 0;
 static int tomcat_singles = 0;
 
 /* Forward reference to static functions */
-static void frv_set_flags              PARAMS ((int));
-static void frv_pic_ptr                        PARAMS ((int));
-static void frv_frob_file_section      PARAMS ((bfd *, asection *, PTR));
+static void frv_set_flags (int);
+static void frv_pic_ptr (int);
 
 /* The target specific pseudo-ops which we support.  */
 const pseudo_typeS md_pseudo_table[] =
@@ -212,6 +224,8 @@ const char * md_shortopts = FRV_SHORTOPTS;
 #define OPTION_TOMCAT_STATS    (OPTION_MD_BASE + 18)
 #define OPTION_PACK            (OPTION_MD_BASE + 19)
 #define OPTION_NO_PACK         (OPTION_MD_BASE + 20)
+#define OPTION_FDPIC           (OPTION_MD_BASE + 21)
+#define OPTION_NOPIC           (OPTION_MD_BASE + 22)
 
 struct option md_longopts[] =
 {
@@ -237,6 +251,8 @@ struct option md_longopts[] =
   { "mtomcat-stats",   no_argument,            NULL, OPTION_TOMCAT_STATS  },
   { "mpack",           no_argument,            NULL, OPTION_PACK          },
   { "mno-pack",                no_argument,            NULL, OPTION_NO_PACK       },
+  { "mfdpic",          no_argument,            NULL, OPTION_FDPIC         },
+  { "mnopic",          no_argument,            NULL, OPTION_NOPIC         },
   { NULL,              no_argument,            NULL, 0                 },
 };
 
@@ -246,9 +262,7 @@ size_t md_longopts_size = sizeof (md_longopts);
 static int g_switch_value = 8;
 
 int
-md_parse_option (c, arg)
-     int    c;
-     char * arg;
+md_parse_option (int c, char *arg)
 {
   switch (c)
     {
@@ -340,10 +354,30 @@ md_parse_option (c, arg)
            frv_mach = bfd_mach_fr500;
          }
 
+       else if (strcmp (p, "fr550") == 0)
+         {
+           cpu_flags = EF_FRV_CPU_FR550;
+           frv_mach = bfd_mach_fr550;
+         }
+
+       else if (strcmp (p, "fr450") == 0)
+         {
+           cpu_flags = EF_FRV_CPU_FR450;
+           frv_mach = bfd_mach_fr450;
+         }
+
+       else if (strcmp (p, "fr405") == 0)
+         {
+           cpu_flags = EF_FRV_CPU_FR405;
+           frv_mach = bfd_mach_fr400;
+           fr400_audio = TRUE;
+         }
+
        else if (strcmp (p, "fr400") == 0)
          {
            cpu_flags = EF_FRV_CPU_FR400;
            frv_mach = bfd_mach_fr400;
+           fr400_audio = FALSE;
          }
 
        else if (strcmp (p, "fr300") == 0)
@@ -367,7 +401,7 @@ md_parse_option (c, arg)
 
        else
          {
-           as_fatal ("Unknown cpu -mcpu=%s", arg);
+           as_fatal (_("Unknown cpu -mcpu=%s"), arg);
            return 0;
          }
 
@@ -394,6 +428,17 @@ md_parse_option (c, arg)
       g_switch_value = 0;
       break;
 
+    case OPTION_FDPIC:
+      frv_flags |= EF_FRV_FDPIC;
+      frv_pic_flag = "-mfdpic";
+      break;
+
+    case OPTION_NOPIC:
+      frv_flags &= ~(EF_FRV_FDPIC | EF_FRV_PIC
+                    | EF_FRV_BIGPIC | EF_FRV_LIBPIC);
+      frv_pic_flag = 0;
+      break;
+
     case OPTION_TOMCAT_DEBUG:
       tomcat_debug = 1;
       break;
@@ -407,35 +452,36 @@ md_parse_option (c, arg)
 }
 
 void
-md_show_usage (stream)
-  FILE * stream;
+md_show_usage (FILE * stream)
 {
   fprintf (stream, _("FRV specific command line options:\n"));
-  fprintf (stream, _("-G n         Data >= n bytes is in small data area\n"));
-  fprintf (stream, _("-mgpr-32     Note 32 gprs are used\n"));
-  fprintf (stream, _("-mgpr-64     Note 64 gprs are used\n"));
-  fprintf (stream, _("-mfpr-32     Note 32 fprs are used\n"));
-  fprintf (stream, _("-mfpr-64     Note 64 fprs are used\n"));
-  fprintf (stream, _("-msoft-float Note software fp is used\n"));
-  fprintf (stream, _("-mdword      Note stack is aligned to a 8 byte boundary\n"));
-  fprintf (stream, _("-mno-dword   Note stack is aligned to a 4 byte boundary\n"));
-  fprintf (stream, _("-mdouble     Note fp double insns are used\n"));
-  fprintf (stream, _("-mmedia      Note media insns are used\n"));
-  fprintf (stream, _("-mmuladd     Note multiply add/subtract insns are used\n"));
-  fprintf (stream, _("-mpack       Note instructions are packed\n"));
-  fprintf (stream, _("-mno-pack    Do not allow instructions to be packed\n"));
-  fprintf (stream, _("-mpic        Note small position independent code\n"));
-  fprintf (stream, _("-mPIC        Note large position independent code\n"));
-  fprintf (stream, _("-mlibrary-pic Compile library for large position indepedent code\n"));
-  fprintf (stream, _("-mcpu={fr500|fr400|fr300|frv|simple|tomcat}\n"));
-  fprintf (stream, _("             Record the cpu type\n"));
-  fprintf (stream, _("-mtomcat-stats Print out stats for tomcat workarounds\n"));
-  fprintf (stream, _("-mtomcat-debug Debug tomcat workarounds\n"));
+  fprintf (stream, _("-G n            Put data <= n bytes in the small data area\n"));
+  fprintf (stream, _("-mgpr-32        Mark generated file as only using 32 GPRs\n"));
+  fprintf (stream, _("-mgpr-64        Mark generated file as using all 64 GPRs\n"));
+  fprintf (stream, _("-mfpr-32        Mark generated file as only using 32 FPRs\n"));
+  fprintf (stream, _("-mfpr-64        Mark generated file as using all 64 FPRs\n"));
+  fprintf (stream, _("-msoft-float    Mark generated file as using software FP\n"));
+  fprintf (stream, _("-mdword         Mark generated file as using a 8-byte stack alignment\n"));
+  fprintf (stream, _("-mno-dword      Mark generated file as using a 4-byte stack alignment\n"));
+  fprintf (stream, _("-mdouble        Mark generated file as using double precision FP insns\n"));
+  fprintf (stream, _("-mmedia         Mark generated file as using media insns\n"));
+  fprintf (stream, _("-mmuladd        Mark generated file as using multiply add/subtract insns\n"));
+  fprintf (stream, _("-mpack          Allow instructions to be packed\n"));
+  fprintf (stream, _("-mno-pack       Do not allow instructions to be packed\n"));
+  fprintf (stream, _("-mpic           Mark generated file as using small position independent code\n"));
+  fprintf (stream, _("-mPIC           Mark generated file as using large position independent code\n"));
+  fprintf (stream, _("-mlibrary-pic   Mark generated file as using position indepedent code for libraries\n"));
+  fprintf (stream, _("-mfdpic         Assemble for the FDPIC ABI\n"));
+  fprintf (stream, _("-mnopic         Disable -mpic, -mPIC, -mlibrary-pic and -mfdpic\n"));
+  fprintf (stream, _("-mcpu={fr500|fr550|fr400|fr405|fr450|fr300|frv|simple|tomcat}\n"));
+  fprintf (stream, _("                Record the cpu type\n"));
+  fprintf (stream, _("-mtomcat-stats  Print out stats for tomcat workarounds\n"));
+  fprintf (stream, _("-mtomcat-debug  Debug tomcat workarounds\n"));
 } 
 
 \f
 void
-md_begin ()
+md_begin (void)
 {
   /* Initialize the `cgen' interface.  */
   
@@ -462,13 +508,16 @@ md_begin ()
   frv_vliw_reset (& vliw, frv_mach, frv_flags);
 }
 
-int chain_num = 0;
+bfd_boolean
+frv_md_fdpic_enabled (void)
+{
+  return (frv_flags & EF_FRV_FDPIC) != 0;
+}
 
-struct vliw_insn_list *frv_insert_vliw_insn PARAMS ((bfd_boolean));
+int chain_num = 0;
 
-struct vliw_insn_list *
-frv_insert_vliw_insn (count)
-      bfd_boolean count;
+static struct vliw_insn_list *
+frv_insert_vliw_insn (bfd_boolean count)
 {
   struct vliw_insn_list *vliw_insn_list_entry;
   struct vliw_chain     *vliw_chain_entry;
@@ -537,14 +586,10 @@ frv_insert_vliw_insn (count)
 
 /* Check a vliw insn for an insn of type containing the sym passed in label_sym.  */
 
-static struct vliw_insn_list *frv_find_in_vliw
-  PARAMS ((enum vliw_insn_type, struct vliw_chain *, symbolS *));
-
 static struct vliw_insn_list *
-frv_find_in_vliw (vliw_insn_type, this_chain, label_sym)
-    enum vliw_insn_type vliw_insn_type;
-    struct vliw_chain *this_chain;
-    symbolS *label_sym;
+frv_find_in_vliw (enum vliw_insn_type vliw_insn_type,
+                 struct vliw_chain *this_chain,
+                 symbolS *label_sym)
 {
 
   struct vliw_insn_list *the_insn;
@@ -575,11 +620,8 @@ enum vliw_nop_type
   VLIW_DOUBLE_THEN_SINGLE_NOP
 };
 
-static void frv_debug_tomcat PARAMS ((struct vliw_chain *));
-
 static void
-frv_debug_tomcat (start_chain)
-   struct vliw_chain *start_chain;
+frv_debug_tomcat (struct vliw_chain *start_chain)
 {
    struct vliw_chain *this_chain;
    struct vliw_insn_list *this_insn;
@@ -592,11 +634,11 @@ frv_debug_tomcat (start_chain)
       for (this_insn = this_chain->insn_list; this_insn; this_insn = this_insn->next)
        {
          if (this_insn->type == VLIW_LABEL_TYPE)
-           fprintf (stderr, "Label Value: %d\n", (int) this_insn->sym);
+           fprintf (stderr, "Label Value: %p\n", this_insn->sym);
          else if (this_insn->type == VLIW_BRANCH_TYPE)
-           fprintf (stderr, "%s to %d\n", this_insn->insn->base->name, (int) this_insn->sym);
+           fprintf (stderr, "%s to %p\n", this_insn->insn->base->name, this_insn->sym);
          else if (this_insn->type == VLIW_BRANCH_HAS_NOPS)
-           fprintf (stderr, "nop'd %s to %d\n", this_insn->insn->base->name, (int) this_insn->sym);
+           fprintf (stderr, "nop'd %s to %p\n", this_insn->insn->base->name, this_insn->sym);
          else if (this_insn->type == VLIW_NOP_TYPE)
            fprintf (stderr, "Nop\n");
          else
@@ -605,11 +647,8 @@ frv_debug_tomcat (start_chain)
    }
 }
 
-static void frv_adjust_vliw_count PARAMS ((struct vliw_chain *));
-
 static void
-frv_adjust_vliw_count (this_chain)
-    struct vliw_chain *this_chain;
+frv_adjust_vliw_count (struct vliw_chain *this_chain)
 {
   struct vliw_insn_list *this_insn;
 
@@ -628,14 +667,10 @@ frv_adjust_vliw_count (this_chain)
 /* Insert the desired nop combination in the vliw chain before insert_before_insn.
    Rechain the vliw insn.  */
 
-static struct vliw_chain *frv_tomcat_shuffle
-  PARAMS ((enum vliw_nop_type, struct vliw_chain *, struct vliw_insn_list *));
-
 static struct vliw_chain *
-frv_tomcat_shuffle (this_nop_type, vliw_to_split, insert_before_insn)
-   enum vliw_nop_type    this_nop_type;
-   struct vliw_chain     *vliw_to_split;
-   struct vliw_insn_list *insert_before_insn;
+frv_tomcat_shuffle (enum vliw_nop_type this_nop_type,
+                   struct vliw_chain *vliw_to_split,
+                   struct vliw_insn_list *insert_before_insn)
 {
 
   bfd_boolean pack_prev = FALSE;
@@ -689,7 +724,7 @@ frv_tomcat_shuffle (this_nop_type, vliw_to_split, insert_before_insn)
          /* Set the packing bit on the previous insn.  */
          if (pack_prev)
            {
-             unsigned char *buffer = prev_insn->address;
+             char *buffer = prev_insn->address;
              buffer[0] |= 0x80;
            }
          /* The branch is in the middle.  Split this vliw insn into first
@@ -728,7 +763,7 @@ frv_tomcat_shuffle (this_nop_type, vliw_to_split, insert_before_insn)
          /* Set the packing bit on the previous insn.  */
          if (pack_prev)
            {
-             unsigned char *buffer = prev_insn->address;
+             char *buffer = prev_insn->address;
              buffer[0] |= 0x80;
            }
 
@@ -774,7 +809,7 @@ frv_tomcat_shuffle (this_nop_type, vliw_to_split, insert_before_insn)
          /* Set the packing bit on the previous insn.  */
          if (pack_prev)
            {
-             unsigned char *buffer = prev_insn->address;
+             char *buffer = prev_insn->address;
              buffer[0] |= 0x80;
            }
 
@@ -799,10 +834,8 @@ frv_tomcat_shuffle (this_nop_type, vliw_to_split, insert_before_insn)
   return return_me;
 }
 
-static void frv_tomcat_analyze_vliw_chains PARAMS ((void));
-
 static void
-frv_tomcat_analyze_vliw_chains ()
+frv_tomcat_analyze_vliw_chains (void)
 {
   struct vliw_chain *vliw1 = NULL;
   struct vliw_chain *vliw2 = NULL;
@@ -919,7 +952,7 @@ workaround_top:
 }
 
 void
-frv_tomcat_workaround ()
+frv_tomcat_workaround (void)
 {
   if (frv_mach != bfd_mach_frvtomcat)
     return;
@@ -936,9 +969,125 @@ frv_tomcat_workaround ()
     }
 }
 
+static int
+fr550_check_insn_acc_range (frv_insn *insn, int low, int hi)
+{
+  int acc;
+  switch (CGEN_INSN_NUM (insn->insn))
+    {
+    case FRV_INSN_MADDACCS:
+    case FRV_INSN_MSUBACCS:
+    case FRV_INSN_MDADDACCS:
+    case FRV_INSN_MDSUBACCS:
+    case FRV_INSN_MASACCS:
+    case FRV_INSN_MDASACCS:
+      acc = insn->fields.f_ACC40Si;
+      if (acc < low || acc > hi)
+       return 1; /* out of range */
+      acc = insn->fields.f_ACC40Sk;
+      if (acc < low || acc > hi)
+       return 1; /* out of range */
+      break;
+    case FRV_INSN_MMULHS:
+    case FRV_INSN_MMULHU:
+    case FRV_INSN_MMULXHS:
+    case FRV_INSN_MMULXHU:
+    case FRV_INSN_CMMULHS:
+    case FRV_INSN_CMMULHU:
+    case FRV_INSN_MQMULHS:
+    case FRV_INSN_MQMULHU:
+    case FRV_INSN_MQMULXHS:
+    case FRV_INSN_MQMULXHU:
+    case FRV_INSN_CMQMULHS:
+    case FRV_INSN_CMQMULHU:
+    case FRV_INSN_MMACHS:
+    case FRV_INSN_MMRDHS:
+    case FRV_INSN_CMMACHS: 
+    case FRV_INSN_MQMACHS:
+    case FRV_INSN_CMQMACHS:
+    case FRV_INSN_MQXMACHS:
+    case FRV_INSN_MQXMACXHS:
+    case FRV_INSN_MQMACXHS:
+    case FRV_INSN_MCPXRS:
+    case FRV_INSN_MCPXIS:
+    case FRV_INSN_CMCPXRS:
+    case FRV_INSN_CMCPXIS:
+    case FRV_INSN_MQCPXRS:
+    case FRV_INSN_MQCPXIS:
+     acc = insn->fields.f_ACC40Sk;
+      if (acc < low || acc > hi)
+       return 1; /* out of range */
+      break;
+    case FRV_INSN_MMACHU:
+    case FRV_INSN_MMRDHU:
+    case FRV_INSN_CMMACHU:
+    case FRV_INSN_MQMACHU:
+    case FRV_INSN_CMQMACHU:
+    case FRV_INSN_MCPXRU:
+    case FRV_INSN_MCPXIU:
+    case FRV_INSN_CMCPXRU:
+    case FRV_INSN_CMCPXIU:
+    case FRV_INSN_MQCPXRU:
+    case FRV_INSN_MQCPXIU:
+      acc = insn->fields.f_ACC40Uk;
+      if (acc < low || acc > hi)
+       return 1; /* out of range */
+      break;
+    default:
+      break;
+    }
+  return 0; /* all is ok */
+}
+
+static int
+fr550_check_acc_range (FRV_VLIW *vliw, frv_insn *insn)
+{
+  switch ((*vliw->current_vliw)[vliw->next_slot - 1])
+    {
+    case UNIT_FM0:
+    case UNIT_FM2:
+      return fr550_check_insn_acc_range (insn, 0, 3);
+    case UNIT_FM1:
+    case UNIT_FM3:
+      return fr550_check_insn_acc_range (insn, 4, 7);
+    default:
+      break;
+    }
+  return 0; /* all is ok */
+}
+
+/* Return true if the target implements instruction INSN.  */
+
+static bfd_boolean
+target_implements_insn_p (const CGEN_INSN *insn)
+{
+  switch (frv_mach)
+    {
+    default:
+      /* bfd_mach_frv or generic.  */
+      return TRUE;
+
+    case bfd_mach_fr300:
+    case bfd_mach_frvsimple:
+      return CGEN_INSN_MACH_HAS_P (insn, MACH_SIMPLE);
+
+    case bfd_mach_fr400:
+      return ((fr400_audio || !CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_AUDIO))
+             && CGEN_INSN_MACH_HAS_P (insn, MACH_FR400));
+
+    case bfd_mach_fr450:
+      return CGEN_INSN_MACH_HAS_P (insn, MACH_FR450);
+
+    case bfd_mach_fr500:
+      return CGEN_INSN_MACH_HAS_P (insn, MACH_FR500);
+
+    case bfd_mach_fr550:
+      return CGEN_INSN_MACH_HAS_P (insn, MACH_FR550);
+    }
+}
+
 void
-md_assemble (str)
-     char * str;
+md_assemble (char *str)
 {
   frv_insn insn;
   char *errmsg;
@@ -951,6 +1100,8 @@ md_assemble (str)
   /* Initialize GAS's cgen interface for a new instruction.  */
   gas_cgen_init_parse ();
 
+  memset (&insn, 0, sizeof (insn));
+
   insn.insn = frv_cgen_assemble_insn
     (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, &errmsg);
   
@@ -1017,7 +1168,14 @@ md_assemble (str)
      instructions, don't do vliw checking.  */
   else if (frv_mach != bfd_mach_frv)
     {
+      if (!target_implements_insn_p (insn.insn))
+       {
+         as_bad (_("Instruction not supported by this architecture"));
+         return;
+       }
       packing_constraint = frv_vliw_add_insn (& vliw, insn.insn);
+      if (frv_mach == bfd_mach_fr550 && ! packing_constraint)
+       packing_constraint = fr550_check_acc_range (& vliw, & insn);
       if (insn.fields.f_pack)
        frv_vliw_reset (& vliw, frv_mach, frv_flags);
       if (packing_constraint)
@@ -1058,8 +1216,7 @@ md_assemble (str)
    We just ignore it.  */
 
 void 
-md_operand (expressionP)
-     expressionS * expressionP;
+md_operand (expressionS *expressionP)
 {
   if (* input_line_pointer == '#')
     {
@@ -1069,17 +1226,14 @@ md_operand (expressionP)
 }
 
 valueT
-md_section_align (segment, size)
-     segT   segment;
-     valueT size;
+md_section_align (segT segment, valueT size)
 {
   int align = bfd_get_section_alignment (stdoutput, segment);
   return ((size + (1 << align) - 1) & (-1 << align));
 }
 
 symbolS *
-md_undefined_symbol (name)
-  char * name ATTRIBUTE_UNUSED;
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
 {
   return 0;
 }
@@ -1096,9 +1250,7 @@ const relax_typeS md_relax_table[] =
 };
 
 long
-frv_relax_frag (fragP, stretch)
-     fragS   *fragP ATTRIBUTE_UNUSED;
-     long    stretch ATTRIBUTE_UNUSED;
+frv_relax_frag (fragS *fragP ATTRIBUTE_UNUSED, long stretch ATTRIBUTE_UNUSED)
 {
   return 0;
 }
@@ -1115,9 +1267,7 @@ frv_relax_frag (fragP, stretch)
    0 value.  */
 
 int
-md_estimate_size_before_relax (fragP, segment)
-     fragS * fragP;
-     segT    segment ATTRIBUTE_UNUSED;
+md_estimate_size_before_relax (fragS *fragP, segT segment ATTRIBUTE_UNUSED)
 {
   switch (fragP->fr_subtype)
     {
@@ -1138,10 +1288,9 @@ md_estimate_size_before_relax (fragP, segment)
    fragP->fr_subtype is the subtype of what the address relaxed to.  */
 
 void
-md_convert_frag (abfd, sec, fragP)
-  bfd *   abfd ATTRIBUTE_UNUSED;
-  segT    sec ATTRIBUTE_UNUSED;
-  fragS * fragP;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+                segT sec ATTRIBUTE_UNUSED,
+                fragS *fragP)
 {
   switch (fragP->fr_subtype)
     {
@@ -1162,16 +1311,16 @@ md_convert_frag (abfd, sec, fragP)
    given a PC relative reloc.  */
 
 long
-md_pcrel_from_section (fixP, sec)
-     fixS * fixP;
-     segT   sec;
+md_pcrel_from_section (fixS *fixP, segT sec)
 {
-  if (fixP->fx_addsy != (symbolS *) NULL
-      && (! S_IS_DEFINED (fixP->fx_addsy)
-         || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+  if (TC_FORCE_RELOCATION (fixP)
+      || (fixP->fx_addsy != (symbolS *) NULL
+         && S_GET_SEGMENT (fixP->fx_addsy) != sec))
     {
-      /* The symbol is undefined (or is defined but not in this section).
-        Let the linker figure it out.  */
+      /* If we can't adjust this relocation, or if it references a
+        local symbol in a different section (which
+        TC_FORCE_RELOCATION can't check), let the linker figure it
+        out.  */
       return 0;
     }
 
@@ -1183,10 +1332,9 @@ md_pcrel_from_section (fixP, sec)
    *FIXP may be modified if desired.  */
 
 bfd_reloc_code_real_type
-md_cgen_lookup_reloc (insn, operand, fixP)
-     const CGEN_INSN *    insn ATTRIBUTE_UNUSED;
-     const CGEN_OPERAND * operand;
-     fixS *               fixP;
+md_cgen_lookup_reloc (const CGEN_INSN *insn ATTRIBUTE_UNUSED,
+                     const CGEN_OPERAND *operand,
+                     fixS *fixP)
 {
   switch (operand->type)
     {
@@ -1196,12 +1344,18 @@ md_cgen_lookup_reloc (insn, operand, fixP)
 
     case FRV_OPERAND_LABEL24:
       fixP->fx_pcrel = TRUE;
+
+      if (fixP->fx_cgen.opinfo != 0)
+       return fixP->fx_cgen.opinfo;
+
       return BFD_RELOC_FRV_LABEL24;
 
     case FRV_OPERAND_UHI16:
     case FRV_OPERAND_ULO16:
     case FRV_OPERAND_SLO16:
-
+    case FRV_OPERAND_CALLANN:
+    case FRV_OPERAND_LDANN:
+    case FRV_OPERAND_LDDANN:
       /* The relocation type should be recorded in opinfo */
       if (fixP->fx_cgen.opinfo != 0)
         return fixP->fx_cgen.opinfo;
@@ -1209,6 +1363,9 @@ md_cgen_lookup_reloc (insn, operand, fixP)
 
     case FRV_OPERAND_D12:
     case FRV_OPERAND_S12:
+      if (fixP->fx_cgen.opinfo != 0)
+       return fixP->fx_cgen.opinfo;
+
       return BFD_RELOC_FRV_GPREL12;
 
     case FRV_OPERAND_U12:
@@ -1226,87 +1383,146 @@ md_cgen_lookup_reloc (insn, operand, fixP)
    relaxing.  */
 
 int
-frv_force_relocation (fix)
-     fixS * fix;
+frv_force_relocation (fixS *fix)
 {
-  if (fix->fx_r_type == BFD_RELOC_FRV_GPREL12
-      || fix->fx_r_type == BFD_RELOC_FRV_GPRELU12)
-    return 1;
+  switch (fix->fx_r_type < BFD_RELOC_UNUSED
+         ? (int) fix->fx_r_type
+         : fix->fx_cgen.opinfo)
+    {
+    case BFD_RELOC_FRV_GPREL12:
+    case BFD_RELOC_FRV_GPRELU12:
+    case BFD_RELOC_FRV_GPREL32:
+    case BFD_RELOC_FRV_GPRELHI:
+    case BFD_RELOC_FRV_GPRELLO:
+    case BFD_RELOC_FRV_GOT12:
+    case BFD_RELOC_FRV_GOTHI:
+    case BFD_RELOC_FRV_GOTLO:
+    case BFD_RELOC_FRV_FUNCDESC_VALUE:
+    case BFD_RELOC_FRV_FUNCDESC_GOTOFF12:
+    case BFD_RELOC_FRV_FUNCDESC_GOTOFFHI:
+    case BFD_RELOC_FRV_FUNCDESC_GOTOFFLO:
+    case BFD_RELOC_FRV_GOTOFF12:
+    case BFD_RELOC_FRV_GOTOFFHI:
+    case BFD_RELOC_FRV_GOTOFFLO:
+    case BFD_RELOC_FRV_GETTLSOFF:
+    case BFD_RELOC_FRV_TLSDESC_VALUE:
+    case BFD_RELOC_FRV_GOTTLSDESC12:
+    case BFD_RELOC_FRV_GOTTLSDESCHI:
+    case BFD_RELOC_FRV_GOTTLSDESCLO:
+    case BFD_RELOC_FRV_TLSMOFF12:
+    case BFD_RELOC_FRV_TLSMOFFHI:
+    case BFD_RELOC_FRV_TLSMOFFLO:
+    case BFD_RELOC_FRV_GOTTLSOFF12:
+    case BFD_RELOC_FRV_GOTTLSOFFHI:
+    case BFD_RELOC_FRV_GOTTLSOFFLO:
+    case BFD_RELOC_FRV_TLSOFF:
+    case BFD_RELOC_FRV_TLSDESC_RELAX:
+    case BFD_RELOC_FRV_GETTLSOFF_RELAX:
+    case BFD_RELOC_FRV_TLSOFF_RELAX:
+      return 1;
+
+    default:
+      break;
+    }
 
   return generic_force_reloc (fix);
 }
+
+/* Apply a fixup that could be resolved within the assembler.  */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+  if (fixP->fx_addsy == 0)
+    switch (fixP->fx_cgen.opinfo)
+      {
+      case BFD_RELOC_FRV_HI16:
+       *valP >>= 16;
+       /* Fall through.  */
+      case BFD_RELOC_FRV_LO16:
+       *valP &= 0xffff;
+       break;
+
+       /* We need relocations for these, even if their symbols reduce
+          to constants.  */
+      case BFD_RELOC_FRV_GPREL12:
+      case BFD_RELOC_FRV_GPRELU12:
+      case BFD_RELOC_FRV_GPREL32:
+      case BFD_RELOC_FRV_GPRELHI:
+      case BFD_RELOC_FRV_GPRELLO:
+      case BFD_RELOC_FRV_GOT12:
+      case BFD_RELOC_FRV_GOTHI:
+      case BFD_RELOC_FRV_GOTLO:
+      case BFD_RELOC_FRV_FUNCDESC_VALUE:
+      case BFD_RELOC_FRV_FUNCDESC_GOTOFF12:
+      case BFD_RELOC_FRV_FUNCDESC_GOTOFFHI:
+      case BFD_RELOC_FRV_FUNCDESC_GOTOFFLO:
+      case BFD_RELOC_FRV_GOTOFF12:
+      case BFD_RELOC_FRV_GOTOFFHI:
+      case BFD_RELOC_FRV_GOTOFFLO:
+      case BFD_RELOC_FRV_GETTLSOFF:
+      case BFD_RELOC_FRV_TLSDESC_VALUE:
+      case BFD_RELOC_FRV_GOTTLSDESC12:
+      case BFD_RELOC_FRV_GOTTLSDESCHI:
+      case BFD_RELOC_FRV_GOTTLSDESCLO:
+      case BFD_RELOC_FRV_TLSMOFF12:
+      case BFD_RELOC_FRV_TLSMOFFHI:
+      case BFD_RELOC_FRV_TLSMOFFLO:
+      case BFD_RELOC_FRV_GOTTLSOFF12:
+      case BFD_RELOC_FRV_GOTTLSOFFHI:
+      case BFD_RELOC_FRV_GOTTLSOFFLO:
+      case BFD_RELOC_FRV_TLSOFF:
+      case BFD_RELOC_FRV_TLSDESC_RELAX:
+      case BFD_RELOC_FRV_GETTLSOFF_RELAX:
+      case BFD_RELOC_FRV_TLSOFF_RELAX:
+       fixP->fx_addsy = abs_section_sym;
+       break;
+      }
+  else
+    switch (fixP->fx_cgen.opinfo)
+      {
+      case BFD_RELOC_FRV_GETTLSOFF:
+      case BFD_RELOC_FRV_TLSDESC_VALUE:
+      case BFD_RELOC_FRV_GOTTLSDESC12:
+      case BFD_RELOC_FRV_GOTTLSDESCHI:
+      case BFD_RELOC_FRV_GOTTLSDESCLO:
+      case BFD_RELOC_FRV_TLSMOFF12:
+      case BFD_RELOC_FRV_TLSMOFFHI:
+      case BFD_RELOC_FRV_TLSMOFFLO:
+      case BFD_RELOC_FRV_GOTTLSOFF12:
+      case BFD_RELOC_FRV_GOTTLSOFFHI:
+      case BFD_RELOC_FRV_GOTTLSOFFLO:
+      case BFD_RELOC_FRV_TLSOFF:
+      case BFD_RELOC_FRV_TLSDESC_RELAX:
+      case BFD_RELOC_FRV_GETTLSOFF_RELAX:
+      case BFD_RELOC_FRV_TLSOFF_RELAX:
+       /* Mark TLS symbols as such.  */
+       if (S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
+         S_SET_THREAD_LOCAL (fixP->fx_addsy);
+       break;
+      }
+
+  gas_cgen_md_apply_fix (fixP, valP, seg);
+  return;
+}
+
 \f
 /* Write a value out to the object file, using the appropriate endianness.  */
 
 void
-frv_md_number_to_chars (buf, val, n)
-     char * buf;
-     valueT val;
-     int    n;
+frv_md_number_to_chars (char *buf, valueT val, int n)
 {
   number_to_chars_bigendian (buf, val, n);
 }
 
-/* Turn a string in input_line_pointer into a floating point constant of type
-   type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
-   emitted is stored in *sizeP .  An error message is returned, or NULL on OK.
-*/
-
-/* Equal to MAX_PRECISION in atof-ieee.c */
-#define MAX_LITTLENUMS 6
-
 char *
-md_atof (type, litP, sizeP)
-     char   type;
-     char * litP;
-     int *  sizeP;
+md_atof (int type, char *litP, int *sizeP)
 {
-  int              i;
-  int              prec;
-  LITTLENUM_TYPE   words [MAX_LITTLENUMS];
-  char *           t;
-
-  switch (type)
-    {
-    case 'f':
-    case 'F':
-    case 's':
-    case 'S':
-      prec = 2;
-      break;
-
-    case 'd':
-    case 'D':
-    case 'r':
-    case 'R':
-      prec = 4;
-      break;
-
-   /* FIXME: Some targets allow other format chars for bigger sizes here.  */
-
-    default:
-      * sizeP = 0;
-      return _("Bad call to md_atof()");
-    }
-
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-  * sizeP = prec * sizeof (LITTLENUM_TYPE);
-
-  for (i = 0; i < prec; i++)
-    {
-      md_number_to_chars (litP, (valueT) words[i],
-                         sizeof (LITTLENUM_TYPE));
-      litP += sizeof (LITTLENUM_TYPE);
-    }
-     
-  return 0;
+  return ieee_md_atof (type, litP, sizeP, TRUE);
 }
 
 bfd_boolean
-frv_fix_adjustable (fixP)
-   fixS * fixP;
+frv_fix_adjustable (fixS *fixP)
 {
   bfd_reloc_code_real_type reloc_type;
 
@@ -1332,8 +1548,7 @@ frv_fix_adjustable (fixP)
 
 /* Allow user to set flags bits.  */
 void
-frv_set_flags (arg)
-     int arg ATTRIBUTE_UNUSED;
+frv_set_flags (int arg ATTRIBUTE_UNUSED)
 {
   flagword new_flags = get_absolute_expression ();
   flagword new_mask = ~ (flagword)0;
@@ -1355,8 +1570,7 @@ frv_set_flags (arg)
    BFD_RELOC_32 at that time.  */
 
 void
-frv_pic_ptr (nbytes)
-     int nbytes;
+frv_pic_ptr (int nbytes)
 {
   expressionS exp;
   char *p;
@@ -1380,12 +1594,35 @@ frv_pic_ptr (nbytes)
 
   do
     {
-      expression (&exp);
+      bfd_reloc_code_real_type reloc_type = BFD_RELOC_CTOR;
+      
+      if (strncasecmp (input_line_pointer, "funcdesc(", 9) == 0)
+       {
+         input_line_pointer += 9;
+         expression (&exp);
+         if (*input_line_pointer == ')')
+           input_line_pointer++;
+         else
+           as_bad (_("missing ')'"));
+         reloc_type = BFD_RELOC_FRV_FUNCDESC;
+       }
+      else if (strncasecmp (input_line_pointer, "tlsmoff(", 8) == 0)
+       {
+         input_line_pointer += 8;
+         expression (&exp);
+         if (*input_line_pointer == ')')
+           input_line_pointer++;
+         else
+           as_bad (_("missing ')'"));
+         reloc_type = BFD_RELOC_FRV_TLSMOFF;
+       }
+      else
+       expression (&exp);
 
       p = frag_more (4);
       memset (p, 0, 4);
       fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &exp, 0,
-                  BFD_RELOC_CTOR);
+                  reloc_type);
     }
   while (*input_line_pointer++ == ',');
 
@@ -1412,10 +1649,7 @@ frv_pic_ptr (nbytes)
    not possible, issue an error.  */
 
 static void
-frv_frob_file_section (abfd, sec, ptr)
-     bfd *abfd;
-     asection *sec;
-     PTR ptr ATTRIBUTE_UNUSED;
+frv_frob_file_section (bfd *abfd, asection *sec, void *ptr ATTRIBUTE_UNUSED)
 {
   segment_info_type *seginfo = seg_info (sec);
   fixS *fixp;
@@ -1557,14 +1791,13 @@ frv_frob_file_section (abfd, sec, ptr)
    for any relocations that pic won't support.  */
 
 void
-frv_frob_file ()
+frv_frob_file (void)
 {
-  bfd_map_over_sections (stdoutput, frv_frob_file_section, (PTR)0);
+  bfd_map_over_sections (stdoutput, frv_frob_file_section, (void *) 0);
 }
 
 void
-frv_frob_label (this_label)
-    symbolS *this_label;
+frv_frob_label (symbolS *this_label)
 {
   struct vliw_insn_list *vliw_insn_list_entry;
 
@@ -1580,14 +1813,13 @@ frv_frob_label (this_label)
 }
 
 fixS *
-frv_cgen_record_fixup_exp (frag, where, insn, length, operand, opinfo, exp)
-     fragS *              frag;
-     int                  where;
-     const CGEN_INSN *    insn;
-     int                  length;
-     const CGEN_OPERAND * operand;
-     int                  opinfo;
-     expressionS *        exp;
+frv_cgen_record_fixup_exp (fragS *frag,
+                          int where,
+                          const CGEN_INSN *insn,
+                          int length,
+                          const CGEN_OPERAND *operand,
+                          int opinfo,
+                          expressionS *exp)
 {
   fixS * fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
                                            operand, opinfo, exp);