summaryrefslogtreecommitdiffstats
path: root/binutils-2.25/gas/config/tc-frv.c
diff options
context:
space:
mode:
Diffstat (limited to 'binutils-2.25/gas/config/tc-frv.c')
-rw-r--r--binutils-2.25/gas/config/tc-frv.c1834
1 files changed, 1834 insertions, 0 deletions
diff --git a/binutils-2.25/gas/config/tc-frv.c b/binutils-2.25/gas/config/tc-frv.c
new file mode 100644
index 00000000..9cdbe265
--- /dev/null
+++ b/binutils-2.25/gas/config/tc-frv.c
@@ -0,0 +1,1834 @@
+/* tc-frv.c -- Assembler for the Fujitsu FRV.
+ Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ 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 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/frv-desc.h"
+#include "opcodes/frv-opc.h"
+#include "cgen.h"
+#include "libbfd.h"
+#include "elf/common.h"
+#include "elf/frv.h"
+#include "dwarf2dbg.h"
+
+/* Structure to hold all of the different components describing
+ an individual instruction. */
+typedef struct
+{
+ const CGEN_INSN * insn;
+ const CGEN_INSN * orig_insn;
+ CGEN_FIELDS fields;
+#if CGEN_INT_INSN_P
+ CGEN_INSN_INT buffer [1];
+#define INSN_VALUE(buf) (*(buf))
+#else
+ unsigned char buffer [CGEN_MAX_INSN_SIZE];
+#define INSN_VALUE(buf) (buf)
+#endif
+ char * addr;
+ fragS * frag;
+ int num_fixups;
+ fixS * fixups [GAS_CGEN_MAX_FIXUPS];
+ int indices [MAX_OPERAND_INSTANCES];
+}
+frv_insn;
+
+enum vliw_insn_type
+{
+ VLIW_GENERIC_TYPE, /* Don't care about this insn. */
+ VLIW_BRANCH_TYPE, /* A Branch. */
+ VLIW_LABEL_TYPE, /* A Label. */
+ VLIW_NOP_TYPE, /* A NOP. */
+ VLIW_BRANCH_HAS_NOPS /* A Branch that requires NOPS. */
+};
+
+/* We're going to use these in the fr_subtype field to mark
+ whether to keep inserted nops. */
+
+#define NOP_KEEP 1 /* Keep these NOPS. */
+#define NOP_DELETE 2 /* Delete these NOPS. */
+
+#define DO_COUNT TRUE
+#define DONT_COUNT FALSE
+
+/* A list of insns within a VLIW insn. */
+struct vliw_insn_list
+{
+ /* The type of this insn. */
+ enum vliw_insn_type type;
+
+ /* The corresponding gas insn information. */
+ const CGEN_INSN *insn;
+
+ /* For branches and labels, the symbol that is referenced. */
+ symbolS *sym;
+
+ /* For branches, the frag containing the single nop that was generated. */
+ fragS *snop_frag;
+
+ /* For branches, the frag containing the double nop that was generated. */
+ fragS *dnop_frag;
+
+ /* Pointer to raw data for this insn. */
+ char *address;
+
+ /* Next insn in list. */
+ struct vliw_insn_list *next;
+};
+
+static struct vliw_insn_list single_nop_insn = {
+ VLIW_NOP_TYPE, NULL, NULL, NULL, NULL, NULL, NULL };
+
+static struct vliw_insn_list double_nop_insn = {
+ VLIW_NOP_TYPE, NULL, NULL, NULL, NULL, NULL, NULL };
+
+struct vliw_chain
+{
+ int num;
+ int insn_count;
+ struct vliw_insn_list *insn_list;
+ struct vliw_chain *next;
+};
+
+static struct vliw_chain *vliw_chain_top;
+static struct vliw_chain *current_vliw_chain;
+static struct vliw_chain *previous_vliw_chain;
+static struct vliw_insn_list *current_vliw_insn;
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "!";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+static FRV_VLIW vliw;
+
+/* Default machine */
+
+#ifdef DEFAULT_CPU_FRV
+#define DEFAULT_MACHINE bfd_mach_frv
+#define DEFAULT_FLAGS EF_FRV_CPU_GENERIC
+
+#else
+#ifdef DEFAULT_CPU_FR300
+#define DEFAULT_MACHINE bfd_mach_fr300
+#define DEFAULT_FLAGS EF_FRV_CPU_FR300
+
+#else
+#ifdef DEFAULT_CPU_SIMPLE
+#define DEFAULT_MACHINE bfd_mach_frvsimple
+#define DEFAULT_FLAGS EF_FRV_CPU_SIMPLE
+
+#else
+#ifdef DEFAULT_CPU_TOMCAT
+#define DEFAULT_MACHINE bfd_mach_frvtomcat
+#define DEFAULT_FLAGS EF_FRV_CPU_TOMCAT
+
+#else
+#ifdef DEFAULT_CPU_FR400
+#define DEFAULT_MACHINE bfd_mach_fr400
+#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
+#endif
+#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 | DEFAULT_FDPIC;
+
+static int frv_user_set_flags_p = 0;
+static int frv_pic_p = 0;
+static const char *frv_pic_flag = DEFAULT_FDPIC ? "-mfdpic" : (const char *)0;
+
+/* Print tomcat-specific debugging info. */
+static int tomcat_debug = 0;
+
+/* Tomcat-specific NOP statistics. */
+static int tomcat_stats = 0;
+static int tomcat_doubles = 0;
+static int tomcat_singles = 0;
+
+/* Forward reference to static functions */
+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[] =
+{
+ { "eflags", frv_set_flags, 0 },
+ { "word", cons, 4 },
+ { "picptr", frv_pic_ptr, 4 },
+ { NULL, NULL, 0 }
+};
+
+
+#define FRV_SHORTOPTS "G:"
+const char * md_shortopts = FRV_SHORTOPTS;
+
+#define OPTION_GPR_32 (OPTION_MD_BASE)
+#define OPTION_GPR_64 (OPTION_MD_BASE + 1)
+#define OPTION_FPR_32 (OPTION_MD_BASE + 2)
+#define OPTION_FPR_64 (OPTION_MD_BASE + 3)
+#define OPTION_SOFT_FLOAT (OPTION_MD_BASE + 4)
+#define OPTION_DWORD_YES (OPTION_MD_BASE + 5)
+#define OPTION_DWORD_NO (OPTION_MD_BASE + 6)
+#define OPTION_DOUBLE (OPTION_MD_BASE + 7)
+#define OPTION_NO_DOUBLE (OPTION_MD_BASE + 8)
+#define OPTION_MEDIA (OPTION_MD_BASE + 9)
+#define OPTION_NO_MEDIA (OPTION_MD_BASE + 10)
+#define OPTION_CPU (OPTION_MD_BASE + 11)
+#define OPTION_PIC (OPTION_MD_BASE + 12)
+#define OPTION_BIGPIC (OPTION_MD_BASE + 13)
+#define OPTION_LIBPIC (OPTION_MD_BASE + 14)
+#define OPTION_MULADD (OPTION_MD_BASE + 15)
+#define OPTION_NO_MULADD (OPTION_MD_BASE + 16)
+#define OPTION_TOMCAT_DEBUG (OPTION_MD_BASE + 17)
+#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[] =
+{
+ { "mgpr-32", no_argument, NULL, OPTION_GPR_32 },
+ { "mgpr-64", no_argument, NULL, OPTION_GPR_64 },
+ { "mfpr-32", no_argument, NULL, OPTION_FPR_32 },
+ { "mfpr-64", no_argument, NULL, OPTION_FPR_64 },
+ { "mhard-float", no_argument, NULL, OPTION_FPR_64 },
+ { "msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT },
+ { "mdword", no_argument, NULL, OPTION_DWORD_YES },
+ { "mno-dword", no_argument, NULL, OPTION_DWORD_NO },
+ { "mdouble", no_argument, NULL, OPTION_DOUBLE },
+ { "mno-double", no_argument, NULL, OPTION_NO_DOUBLE },
+ { "mmedia", no_argument, NULL, OPTION_MEDIA },
+ { "mno-media", no_argument, NULL, OPTION_NO_MEDIA },
+ { "mcpu", required_argument, NULL, OPTION_CPU },
+ { "mpic", no_argument, NULL, OPTION_PIC },
+ { "mPIC", no_argument, NULL, OPTION_BIGPIC },
+ { "mlibrary-pic", no_argument, NULL, OPTION_LIBPIC },
+ { "mmuladd", no_argument, NULL, OPTION_MULADD },
+ { "mno-muladd", no_argument, NULL, OPTION_NO_MULADD },
+ { "mtomcat-debug", no_argument, NULL, OPTION_TOMCAT_DEBUG },
+ { "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 },
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* What value to give to bfd_set_gp_size. */
+static int g_switch_value = 8;
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ default:
+ return 0;
+
+ case 'G':
+ g_switch_value = atoi (arg);
+ if (! g_switch_value)
+ frv_flags |= EF_FRV_G0;
+ break;
+
+ case OPTION_GPR_32:
+ frv_flags = (frv_flags & ~EF_FRV_GPR_MASK) | EF_FRV_GPR_32;
+ break;
+
+ case OPTION_GPR_64:
+ frv_flags = (frv_flags & ~EF_FRV_GPR_MASK) | EF_FRV_GPR_64;
+ break;
+
+ case OPTION_FPR_32:
+ frv_flags = (frv_flags & ~EF_FRV_FPR_MASK) | EF_FRV_FPR_32;
+ break;
+
+ case OPTION_FPR_64:
+ frv_flags = (frv_flags & ~EF_FRV_FPR_MASK) | EF_FRV_FPR_64;
+ break;
+
+ case OPTION_SOFT_FLOAT:
+ frv_flags = (frv_flags & ~EF_FRV_FPR_MASK) | EF_FRV_FPR_NONE;
+ break;
+
+ case OPTION_DWORD_YES:
+ frv_flags = (frv_flags & ~EF_FRV_DWORD_MASK) | EF_FRV_DWORD_YES;
+ break;
+
+ case OPTION_DWORD_NO:
+ frv_flags = (frv_flags & ~EF_FRV_DWORD_MASK) | EF_FRV_DWORD_NO;
+ break;
+
+ case OPTION_DOUBLE:
+ frv_flags |= EF_FRV_DOUBLE;
+ break;
+
+ case OPTION_NO_DOUBLE:
+ frv_flags &= ~EF_FRV_DOUBLE;
+ break;
+
+ case OPTION_MEDIA:
+ frv_flags |= EF_FRV_MEDIA;
+ break;
+
+ case OPTION_NO_MEDIA:
+ frv_flags &= ~EF_FRV_MEDIA;
+ break;
+
+ case OPTION_MULADD:
+ frv_flags |= EF_FRV_MULADD;
+ break;
+
+ case OPTION_NO_MULADD:
+ frv_flags &= ~EF_FRV_MULADD;
+ break;
+
+ case OPTION_PACK:
+ frv_flags &= ~EF_FRV_NOPACK;
+ break;
+
+ case OPTION_NO_PACK:
+ frv_flags |= EF_FRV_NOPACK;
+ break;
+
+ case OPTION_CPU:
+ {
+ char *p;
+ int cpu_flags = EF_FRV_CPU_GENERIC;
+
+ /* Identify the processor type */
+ p = arg;
+ if (strcmp (p, "frv") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_GENERIC;
+ frv_mach = bfd_mach_frv;
+ }
+
+ else if (strcmp (p, "fr500") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_FR500;
+ 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)
+ {
+ cpu_flags = EF_FRV_CPU_FR300;
+ frv_mach = bfd_mach_fr300;
+ }
+
+ else if (strcmp (p, "simple") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_SIMPLE;
+ frv_mach = bfd_mach_frvsimple;
+ frv_flags |= EF_FRV_NOPACK;
+ }
+
+ else if (strcmp (p, "tomcat") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_TOMCAT;
+ frv_mach = bfd_mach_frvtomcat;
+ }
+
+ else
+ {
+ as_fatal (_("Unknown cpu -mcpu=%s"), arg);
+ return 0;
+ }
+
+ frv_flags = (frv_flags & ~EF_FRV_CPU_MASK) | cpu_flags;
+ }
+ break;
+
+ case OPTION_PIC:
+ frv_flags |= EF_FRV_PIC;
+ frv_pic_p = 1;
+ frv_pic_flag = "-fpic";
+ break;
+
+ case OPTION_BIGPIC:
+ frv_flags |= EF_FRV_BIGPIC;
+ frv_pic_p = 1;
+ frv_pic_flag = "-fPIC";
+ break;
+
+ case OPTION_LIBPIC:
+ frv_flags |= (EF_FRV_LIBPIC | EF_FRV_G0);
+ frv_pic_p = 1;
+ frv_pic_flag = "-mlibrary-pic";
+ 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;
+
+ case OPTION_TOMCAT_STATS:
+ tomcat_stats = 1;
+ break;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _("FRV specific command line options:\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"));
+}
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = frv_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ frv_cgen_init_asm (gas_cgen_cpu_desc);
+
+ /* This is a callback from cgen to gas to parse operands. */
+ cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
+
+ /* Set the ELF flags if desired. */
+ if (frv_flags)
+ bfd_set_private_flags (stdoutput, frv_flags);
+
+ /* Set the machine type */
+ bfd_default_set_arch_mach (stdoutput, bfd_arch_frv, frv_mach);
+
+ /* Set up gp size so we can put local common items in .sbss */
+ bfd_set_gp_size (stdoutput, g_switch_value);
+
+ frv_vliw_reset (& vliw, frv_mach, frv_flags);
+}
+
+bfd_boolean
+frv_md_fdpic_enabled (void)
+{
+ return (frv_flags & EF_FRV_FDPIC) != 0;
+}
+
+int chain_num = 0;
+
+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;
+
+ if (current_vliw_chain == NULL)
+ {
+ vliw_chain_entry = (struct vliw_chain *) xmalloc (sizeof (struct vliw_chain));
+ vliw_chain_entry->insn_count = 0;
+ vliw_chain_entry->insn_list = NULL;
+ vliw_chain_entry->next = NULL;
+ vliw_chain_entry->num = chain_num++;
+
+ if (!vliw_chain_top)
+ vliw_chain_top = vliw_chain_entry;
+ current_vliw_chain = vliw_chain_entry;
+ if (previous_vliw_chain)
+ previous_vliw_chain->next = vliw_chain_entry;
+ }
+
+ vliw_insn_list_entry = (struct vliw_insn_list *) xmalloc (sizeof (struct vliw_insn_list));
+ vliw_insn_list_entry->type = VLIW_GENERIC_TYPE;
+ vliw_insn_list_entry->insn = NULL;
+ vliw_insn_list_entry->sym = NULL;
+ vliw_insn_list_entry->snop_frag = NULL;
+ vliw_insn_list_entry->dnop_frag = NULL;
+ vliw_insn_list_entry->next = NULL;
+
+ if (count)
+ current_vliw_chain->insn_count++;
+
+ if (current_vliw_insn)
+ current_vliw_insn->next = vliw_insn_list_entry;
+ current_vliw_insn = vliw_insn_list_entry;
+
+ if (!current_vliw_chain->insn_list)
+ current_vliw_chain->insn_list = current_vliw_insn;
+
+ return vliw_insn_list_entry;
+}
+
+ /* Identify the following cases:
+
+ 1) A VLIW insn that contains both a branch and the branch destination.
+ This requires the insertion of two vliw instructions before the
+ branch. The first consists of two nops. The second consists of
+ a single nop.
+
+ 2) A single instruction VLIW insn which is the destination of a branch
+ that is in the next VLIW insn. This requires the insertion of a vliw
+ insn containing two nops before the branch.
+
+ 3) A double instruction VLIW insn which contains the destination of a
+ branch that is in the next VLIW insn. This requires the insertion of
+ a VLIW insn containing a single nop before the branch.
+
+ 4) A single instruction VLIW insn which contains branch destination (x),
+ followed by a single instruction VLIW insn which does not contain
+ the branch to (x), followed by a VLIW insn which does contain the branch
+ to (x). This requires the insertion of a VLIW insn containing a single
+ nop before the VLIW instruction containing the branch.
+
+ */
+#define FRV_IS_NOP(insn) (insn.buffer[0] == FRV_NOP_PACK || insn.buffer[0] == FRV_NOP_NOPACK)
+#define FRV_NOP_PACK 0x00880000 /* ori.p gr0,0,gr0 */
+#define FRV_NOP_NOPACK 0x80880000 /* ori gr0,0,gr0 */
+
+/* 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 (enum vliw_insn_type vliw_insn_type,
+ struct vliw_chain *this_chain,
+ symbolS *label_sym)
+{
+
+ struct vliw_insn_list *the_insn;
+
+ if (!this_chain)
+ return NULL;
+
+ for (the_insn = this_chain->insn_list; the_insn; the_insn = the_insn->next)
+ {
+ if (the_insn->type == vliw_insn_type
+ && the_insn->sym == label_sym)
+ return the_insn;
+ }
+
+ return NULL;
+}
+
+enum vliw_nop_type
+{
+ /* A Vliw insn containing a single nop insn. */
+ VLIW_SINGLE_NOP,
+
+ /* A Vliw insn containing two nop insns. */
+ VLIW_DOUBLE_NOP,
+
+ /* Two vliw insns. The first containing two nop insns.
+ The second contain a single nop insn. */
+ VLIW_DOUBLE_THEN_SINGLE_NOP
+};
+
+static void
+frv_debug_tomcat (struct vliw_chain *start_chain)
+{
+ struct vliw_chain *this_chain;
+ struct vliw_insn_list *this_insn;
+ int i = 1;
+
+ for (this_chain = start_chain; this_chain; this_chain = this_chain->next, i++)
+ {
+ fprintf (stderr, "\nVliw Insn #%d, #insns: %d\n", i, this_chain->insn_count);
+
+ 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: %p\n", this_insn->sym);
+ else if (this_insn->type == VLIW_BRANCH_TYPE)
+ 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 %p\n", this_insn->insn->base->name, this_insn->sym);
+ else if (this_insn->type == VLIW_NOP_TYPE)
+ fprintf (stderr, "Nop\n");
+ else
+ fprintf (stderr, " %s\n", this_insn->insn->base->name);
+ }
+ }
+}
+
+static void
+frv_adjust_vliw_count (struct vliw_chain *this_chain)
+{
+ struct vliw_insn_list *this_insn;
+
+ this_chain->insn_count = 0;
+
+ for (this_insn = this_chain->insn_list;
+ this_insn;
+ this_insn = this_insn->next)
+ {
+ if (this_insn->type != VLIW_LABEL_TYPE)
+ this_chain->insn_count++;
+ }
+
+}
+
+/* Insert the desired nop combination in the vliw chain before insert_before_insn.
+ Rechain the vliw insn. */
+
+static struct vliw_chain *
+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;
+ struct vliw_chain *return_me = NULL;
+ struct vliw_insn_list *prev_insn = NULL;
+ struct vliw_insn_list *curr_insn = vliw_to_split->insn_list;
+
+ struct vliw_chain *double_nop = (struct vliw_chain *) xmalloc (sizeof (struct vliw_chain));
+ struct vliw_chain *single_nop = (struct vliw_chain *) xmalloc (sizeof (struct vliw_chain));
+ struct vliw_chain *second_part = (struct vliw_chain *) xmalloc (sizeof (struct vliw_chain));
+ struct vliw_chain *curr_vliw = vliw_chain_top;
+ struct vliw_chain *prev_vliw = NULL;
+
+ while (curr_insn && curr_insn != insert_before_insn)
+ {
+ /* We can't set the packing bit on a label. If we have the case
+ label 1:
+ label 2:
+ label 3:
+ branch that needs nops
+ Then don't set pack bit later. */
+
+ if (curr_insn->type != VLIW_LABEL_TYPE)
+ pack_prev = TRUE;
+ prev_insn = curr_insn;
+ curr_insn = curr_insn->next;
+ }
+
+ while (curr_vliw && curr_vliw != vliw_to_split)
+ {
+ prev_vliw = curr_vliw;
+ curr_vliw = curr_vliw->next;
+ }
+
+ switch (this_nop_type)
+ {
+ case VLIW_SINGLE_NOP:
+ if (!prev_insn)
+ {
+ /* Branch is first, Insert the NOP prior to this vliw insn. */
+ if (prev_vliw)
+ prev_vliw->next = single_nop;
+ else
+ vliw_chain_top = single_nop;
+ single_nop->next = vliw_to_split;
+ vliw_to_split->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ return_me = vliw_to_split;
+ }
+ else
+ {
+ /* Set the packing bit on the previous insn. */
+ if (pack_prev)
+ {
+ char *buffer = prev_insn->address;
+ buffer[0] |= 0x80;
+ }
+ /* The branch is in the middle. Split this vliw insn into first
+ and second parts. Insert the NOP inbetween. */
+
+ second_part->insn_list = insert_before_insn;
+ second_part->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ second_part->next = vliw_to_split->next;
+ frv_adjust_vliw_count (second_part);
+
+ single_nop->next = second_part;
+
+ vliw_to_split->next = single_nop;
+ prev_insn->next = NULL;
+
+ return_me = second_part;
+ frv_adjust_vliw_count (vliw_to_split);
+ }
+ break;
+
+ case VLIW_DOUBLE_NOP:
+ if (!prev_insn)
+ {
+ /* Branch is first, Insert the NOP prior to this vliw insn. */
+ if (prev_vliw)
+ prev_vliw->next = double_nop;
+ else
+ vliw_chain_top = double_nop;
+
+ double_nop->next = vliw_to_split;
+ return_me = vliw_to_split;
+ vliw_to_split->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ }
+ else
+ {
+ /* Set the packing bit on the previous insn. */
+ if (pack_prev)
+ {
+ char *buffer = prev_insn->address;
+ buffer[0] |= 0x80;
+ }
+
+ /* The branch is in the middle. Split this vliw insn into first
+ and second parts. Insert the NOP inbetween. */
+ second_part->insn_list = insert_before_insn;
+ second_part->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ second_part->next = vliw_to_split->next;
+ frv_adjust_vliw_count (second_part);
+
+ double_nop->next = second_part;
+
+ vliw_to_split->next = single_nop;
+ prev_insn->next = NULL;
+ frv_adjust_vliw_count (vliw_to_split);
+
+ return_me = second_part;
+ }
+ break;
+
+ case VLIW_DOUBLE_THEN_SINGLE_NOP:
+ double_nop->next = single_nop;
+ double_nop->insn_count = 2;
+ double_nop->insn_list = &double_nop_insn;
+ single_nop->insn_count = 1;
+ single_nop->insn_list = &single_nop_insn;
+
+ if (!prev_insn)
+ {
+ /* The branch is the first insn in this vliw. Don't split the vliw. Insert
+ the nops prior to this vliw. */
+ if (prev_vliw)
+ prev_vliw->next = double_nop;
+ else
+ vliw_chain_top = double_nop;
+
+ single_nop->next = vliw_to_split;
+ return_me = vliw_to_split;
+ vliw_to_split->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ }
+ else
+ {
+ /* Set the packing bit on the previous insn. */
+ if (pack_prev)
+ {
+ char *buffer = prev_insn->address;
+ buffer[0] |= 0x80;
+ }
+
+ /* The branch is in the middle of this vliw insn. Split into first and
+ second parts. Insert the nop vliws in between. */
+ second_part->insn_list = insert_before_insn;
+ second_part->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ second_part->next = vliw_to_split->next;
+ frv_adjust_vliw_count (second_part);
+
+ single_nop->next = second_part;
+
+ vliw_to_split->next = double_nop;
+ prev_insn->next = NULL;
+ frv_adjust_vliw_count (vliw_to_split);
+
+ return_me = second_part;
+ }
+ break;
+ }
+
+ return return_me;
+}
+
+static void
+frv_tomcat_analyze_vliw_chains (void)
+{
+ struct vliw_chain *vliw1 = NULL;
+ struct vliw_chain *vliw2 = NULL;
+ struct vliw_chain *vliw3 = NULL;
+
+ struct vliw_insn_list *this_insn = NULL;
+ struct vliw_insn_list *temp_insn = NULL;
+
+ /* We potentially need to look at three VLIW insns to determine if the
+ workaround is required. Set them up. Ignore existing nops during analysis. */
+
+#define FRV_SET_VLIW_WINDOW(VLIW1, VLIW2, VLIW3) \
+ if (VLIW1 && VLIW1->next) \
+ VLIW2 = VLIW1->next; \
+ else \
+ VLIW2 = NULL; \
+ if (VLIW2 && VLIW2->next) \
+ VLIW3 = VLIW2->next; \
+ else \
+ VLIW3 = NULL
+
+ vliw1 = vliw_chain_top;
+
+workaround_top:
+
+ FRV_SET_VLIW_WINDOW (vliw1, vliw2, vliw3);
+
+ if (!vliw1)
+ return;
+
+ if (vliw1->insn_count == 1)
+ {
+ /* check vliw1 for a label. */
+ if (vliw1->insn_list->type == VLIW_LABEL_TYPE)
+ {
+ temp_insn = frv_find_in_vliw (VLIW_BRANCH_TYPE, vliw2, vliw1->insn_list->sym);
+ if (temp_insn)
+ {
+ vliw1 = frv_tomcat_shuffle (VLIW_DOUBLE_NOP, vliw2, vliw1->insn_list);
+ temp_insn->dnop_frag->fr_subtype = NOP_KEEP;
+ vliw1 = vliw1->next;
+ if (tomcat_stats)
+ tomcat_doubles++;
+ goto workaround_top;
+ }
+ else if (vliw2
+ && vliw2->insn_count == 1
+ && (temp_insn = frv_find_in_vliw (VLIW_BRANCH_TYPE, vliw3, vliw1->insn_list->sym)) != NULL)
+ {
+ temp_insn->snop_frag->fr_subtype = NOP_KEEP;
+ vliw1 = frv_tomcat_shuffle (VLIW_SINGLE_NOP, vliw3, vliw3->insn_list);
+ if (tomcat_stats)
+ tomcat_singles++;
+ goto workaround_top;
+ }
+ }
+ }
+
+ if (vliw1->insn_count == 2)
+ {
+ /* Check vliw1 for a label. */
+ for (this_insn = vliw1->insn_list; this_insn; this_insn = this_insn->next)
+ {
+ if (this_insn->type == VLIW_LABEL_TYPE)
+ {
+ if ((temp_insn = frv_find_in_vliw (VLIW_BRANCH_TYPE, vliw2, this_insn->sym)) != NULL)
+ {
+ temp_insn->snop_frag->fr_subtype = NOP_KEEP;
+ vliw1 = frv_tomcat_shuffle (VLIW_SINGLE_NOP, vliw2, this_insn);
+ if (tomcat_stats)
+ tomcat_singles++;
+ }
+ else
+ vliw1 = vliw1->next;
+ goto workaround_top;
+ }
+ }
+ }
+ /* Examine each insn in this VLIW. Look for the workaround criteria. */
+ for (this_insn = vliw1->insn_list; this_insn; this_insn = this_insn->next)
+ {
+ /* Don't look at labels or nops. */
+ while (this_insn
+ && (this_insn->type == VLIW_LABEL_TYPE
+ || this_insn->type == VLIW_NOP_TYPE
+ || this_insn->type == VLIW_BRANCH_HAS_NOPS))
+ this_insn = this_insn->next;
+
+ if (!this_insn)
+ {
+ vliw1 = vliw2;
+ goto workaround_top;
+ }
+
+ if (frv_is_branch_insn (this_insn->insn))
+ {
+ if ((temp_insn = frv_find_in_vliw (VLIW_LABEL_TYPE, vliw1, this_insn->sym)) != NULL)
+ {
+ /* Insert [nop/nop] [nop] before branch. */
+ this_insn->snop_frag->fr_subtype = NOP_KEEP;
+ this_insn->dnop_frag->fr_subtype = NOP_KEEP;
+ vliw1 = frv_tomcat_shuffle (VLIW_DOUBLE_THEN_SINGLE_NOP, vliw1, this_insn);
+ goto workaround_top;
+ }
+ }
+
+
+ }
+ /* This vliw insn checks out okay. Take a look at the next one. */
+ vliw1 = vliw1->next;
+ goto workaround_top;
+}
+
+void
+frv_tomcat_workaround (void)
+{
+ if (frv_mach != bfd_mach_frvtomcat)
+ return;
+
+ if (tomcat_debug)
+ frv_debug_tomcat (vliw_chain_top);
+
+ frv_tomcat_analyze_vliw_chains ();
+
+ if (tomcat_stats)
+ {
+ fprintf (stderr, "Inserted %d Single Nops\n", tomcat_singles);
+ fprintf (stderr, "Inserted %d Double Nops\n", tomcat_doubles);
+ }
+}
+
+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 *vlw, frv_insn *insn)
+{
+ switch ((*vlw->current_vliw)[vlw->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 (char *str)
+{
+ frv_insn insn;
+ char *errmsg;
+ int packing_constraint;
+ finished_insnS finished_insn;
+ fragS *double_nop_frag = NULL;
+ fragS *single_nop_frag = NULL;
+ struct vliw_insn_list *vliw_insn_list_entry = NULL;
+
+ /* 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);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* If the cpu is tomcat, then we need to insert nops to workaround
+ hardware limitations. We need to keep track of each vliw unit
+ and examine the length of the unit and the individual insns
+ within the unit to determine the number and location of the
+ required nops. */
+ if (frv_mach == bfd_mach_frvtomcat)
+ {
+ /* If we've just finished a VLIW insn OR this is a branch,
+ then start up a new frag. Fill it with nops. We will get rid
+ of those that are not required after we've seen all of the
+ instructions but before we start resolving fixups. */
+ if ( !FRV_IS_NOP (insn)
+ && (frv_is_branch_insn (insn.insn) || insn.fields.f_pack))
+ {
+ char *buffer;
+
+ frag_wane (frag_now);
+ frag_new (0);
+ double_nop_frag = frag_now;
+ buffer = frag_var (rs_machine_dependent, 8, 8, NOP_DELETE, NULL, 0, 0);
+ md_number_to_chars (buffer, FRV_NOP_PACK, 4);
+ md_number_to_chars (buffer+4, FRV_NOP_NOPACK, 4);
+
+ frag_wane (frag_now);
+ frag_new (0);
+ single_nop_frag = frag_now;
+ buffer = frag_var (rs_machine_dependent, 4, 4, NOP_DELETE, NULL, 0, 0);
+ md_number_to_chars (buffer, FRV_NOP_NOPACK, 4);
+ }
+
+ vliw_insn_list_entry = frv_insert_vliw_insn (DO_COUNT);
+ vliw_insn_list_entry->insn = insn.insn;
+ if (frv_is_branch_insn (insn.insn))
+ vliw_insn_list_entry->type = VLIW_BRANCH_TYPE;
+
+ if ( !FRV_IS_NOP (insn)
+ && (frv_is_branch_insn (insn.insn) || insn.fields.f_pack))
+ {
+ vliw_insn_list_entry->snop_frag = single_nop_frag;
+ vliw_insn_list_entry->dnop_frag = double_nop_frag;
+ }
+ }
+
+ /* Make sure that this insn does not violate the VLIW packing constraints. */
+ /* -mno-pack disallows any packing whatsoever. */
+ if (frv_flags & EF_FRV_NOPACK)
+ {
+ if (! insn.fields.f_pack)
+ {
+ as_bad (_("VLIW packing used for -mno-pack"));
+ return;
+ }
+ }
+ /* -mcpu=FRV is an idealized FR-V implementation that supports all of the
+ 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)
+ {
+ as_bad (_("VLIW packing constraint violation"));
+ return;
+ }
+ }
+
+ /* Doesn't really matter what we pass for RELAX_P here. */
+ gas_cgen_finish_insn (insn.insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (& insn.fields), 1, &finished_insn);
+
+
+ /* If the cpu is tomcat, then we need to insert nops to workaround
+ hardware limitations. We need to keep track of each vliw unit
+ and examine the length of the unit and the individual insns
+ within the unit to determine the number and location of the
+ required nops. */
+ if (frv_mach == bfd_mach_frvtomcat)
+ {
+ if (vliw_insn_list_entry)
+ vliw_insn_list_entry->address = finished_insn.addr;
+ else
+ abort();
+
+ if (insn.fields.f_pack)
+ {
+ /* We've completed a VLIW insn. */
+ previous_vliw_chain = current_vliw_chain;
+ current_vliw_chain = NULL;
+ current_vliw_insn = NULL;
+ }
+ }
+}
+
+/* The syntax in the manual says constants begin with '#'.
+ We just ignore it. */
+
+void
+md_operand (expressionS *expressionP)
+{
+ if (* input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+valueT
+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 (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Interface to relax_segment. */
+
+/* FIXME: Build table by hand, get it working, then machine generate. */
+const relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0},
+ {511 - 2 - 2, -512 - 2 + 2, 0, 2 },
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 2, 0 },
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 4, 0 }
+};
+
+long
+frv_relax_frag (fragS *fragP ATTRIBUTE_UNUSED, long stretch ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Return an initial guess of the length by which a fragment must grow to
+ hold a branch to reach its destination.
+ Also updates fr_type/fr_subtype as necessary.
+
+ Called just before doing relaxation.
+ Any symbol that is now undefined will not become defined.
+ The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ Although it may not be explicit in the frag, pretend fr_var starts with a
+ 0 value. */
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT segment ATTRIBUTE_UNUSED)
+{
+ switch (fragP->fr_subtype)
+ {
+ case NOP_KEEP:
+ return fragP->fr_var;
+
+ default:
+ case NOP_DELETE:
+ return 0;
+ }
+}
+
+/* *fragP has been relaxed to its final size, and now needs to have
+ the bytes inside it modified to conform to the new size.
+
+ Called after relaxation is finished.
+ fragP->fr_type == rs_machine_dependent.
+ fragP->fr_subtype is the subtype of what the address relaxed to. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ switch (fragP->fr_subtype)
+ {
+ default:
+ case NOP_DELETE:
+ return;
+
+ case NOP_KEEP:
+ fragP->fr_fix = fragP->fr_var;
+ fragP->fr_var = 0;
+ return;
+ }
+}
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from_section (fixS *fixP, segT sec)
+{
+ if (TC_FORCE_RELOCATION (fixP)
+ || (fixP->fx_addsy != (symbolS *) NULL
+ && S_GET_SEGMENT (fixP->fx_addsy) != sec))
+ {
+ /* 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;
+ }
+
+ return (fixP->fx_frag->fr_address + fixP->fx_where) & ~1;
+}
+
+/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
+ Returns BFD_RELOC_NONE if no reloc type can be found.
+ *FIXP may be modified if desired. */
+
+bfd_reloc_code_real_type
+md_cgen_lookup_reloc (const CGEN_INSN *insn ATTRIBUTE_UNUSED,
+ const CGEN_OPERAND *operand,
+ fixS *fixP)
+{
+ switch (operand->type)
+ {
+ case FRV_OPERAND_LABEL16:
+ fixP->fx_pcrel = TRUE;
+ return BFD_RELOC_FRV_LABEL16;
+
+ 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;
+ break;
+
+ 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:
+ return BFD_RELOC_FRV_GPRELU12;
+
+ default:
+ break;
+ }
+ return BFD_RELOC_NONE;
+}
+
+
+/* See whether we need to force a relocation into the output file.
+ This is used to force out switch and PC relative relocations when
+ relaxing. */
+
+int
+frv_force_relocation (fixS *fix)
+{
+ 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;
+}
+
+
+/* Write a value out to the object file, using the appropriate endianness. */
+
+void
+frv_md_number_to_chars (char *buf, valueT val, int n)
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+bfd_boolean
+frv_fix_adjustable (fixS *fixP)
+{
+ bfd_reloc_code_real_type reloc_type;
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const CGEN_INSN *insn = NULL;
+ int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ const CGEN_OPERAND *operand = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ }
+ else
+ reloc_type = fixP->fx_r_type;
+
+ /* We need the symbol name for the VTABLE entries */
+ if ( reloc_type == BFD_RELOC_VTABLE_INHERIT
+ || reloc_type == BFD_RELOC_VTABLE_ENTRY
+ || reloc_type == BFD_RELOC_FRV_GPREL12
+ || reloc_type == BFD_RELOC_FRV_GPRELU12)
+ return 0;
+
+ return 1;
+}
+
+/* Allow user to set flags bits. */
+void
+frv_set_flags (int arg ATTRIBUTE_UNUSED)
+{
+ flagword new_flags = get_absolute_expression ();
+ flagword new_mask = ~ (flagword)0;
+
+ frv_user_set_flags_p = 1;
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ new_mask = get_absolute_expression ();
+ }
+
+ frv_flags = (frv_flags & ~new_mask) | (new_flags & new_mask);
+ bfd_set_private_flags (stdoutput, frv_flags);
+}
+
+/* Frv specific function to handle 4 byte initializations for pointers that are
+ considered 'safe' for use with pic support. Until frv_frob_file{,_section}
+ is run, we encode it a BFD_RELOC_CTOR, and it is turned back into a normal
+ BFD_RELOC_32 at that time. */
+
+void
+frv_pic_ptr (int nbytes)
+{
+ expressionS exp;
+ char *p;
+
+ if (nbytes != 4)
+ abort ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
+ do
+ {
+ 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,
+ reloc_type);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+
+
+#ifdef DEBUG
+#define DPRINTF1(A) fprintf (stderr, A)
+#define DPRINTF2(A,B) fprintf (stderr, A, B)
+#define DPRINTF3(A,B,C) fprintf (stderr, A, B, C)
+
+#else
+#define DPRINTF1(A)
+#define DPRINTF2(A,B)
+#define DPRINTF3(A,B,C)
+#endif
+
+/* Go through a the sections looking for relocations that are problematical for
+ pic. If not pic, just note that this object can't be linked with pic. If
+ it is pic, see if it needs to be marked so that it will be fixed up, or if
+ not possible, issue an error. */
+
+static void
+frv_frob_file_section (bfd *abfd, asection *sec, void *ptr ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fixS *fixp;
+ CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
+ flagword flags = bfd_get_section_flags (abfd, sec);
+
+ /* Skip relocations in known sections (.ctors, .dtors, and .gcc_except_table)
+ since we can fix those up by hand. */
+ int known_section_p = (sec->name
+ && sec->name[0] == '.'
+ && ((sec->name[1] == 'c'
+ && strcmp (sec->name, ".ctor") == 0)
+ || (sec->name[1] == 'd'
+ && strcmp (sec->name, ".dtor") == 0)
+ || (sec->name[1] == 'g'
+ && strcmp (sec->name, ".gcc_except_table") == 0)));
+
+ DPRINTF3 ("\nFrv section %s%s\n", sec->name, (known_section_p) ? ", known section" : "");
+ if ((flags & SEC_ALLOC) == 0)
+ {
+ DPRINTF1 ("\tSkipping non-loaded section\n");
+ return;
+ }
+
+ for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
+ {
+ symbolS *s = fixp->fx_addsy;
+ bfd_reloc_code_real_type reloc;
+ int non_pic_p;
+ int opindex;
+ const CGEN_OPERAND *operand;
+ const CGEN_INSN *insn = fixp->fx_cgen.insn;
+
+ if (fixp->fx_done)
+ {
+ DPRINTF1 ("\tSkipping reloc that has already been done\n");
+ continue;
+ }
+
+ if (fixp->fx_pcrel)
+ {
+ DPRINTF1 ("\tSkipping reloc that is PC relative\n");
+ continue;
+ }
+
+ if (! s)
+ {
+ DPRINTF1 ("\tSkipping reloc without symbol\n");
+ continue;
+ }
+
+ if (fixp->fx_r_type < BFD_RELOC_UNUSED)
+ {
+ opindex = -1;
+ reloc = fixp->fx_r_type;
+ }
+ else
+ {
+ opindex = (int) fixp->fx_r_type - (int) BFD_RELOC_UNUSED;
+ operand = cgen_operand_lookup_by_num (cd, opindex);
+ reloc = md_cgen_lookup_reloc (insn, operand, fixp);
+ }
+
+ DPRINTF3 ("\treloc %s\t%s", bfd_get_reloc_code_name (reloc), S_GET_NAME (s));
+
+ non_pic_p = 0;
+ switch (reloc)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_32:
+ /* Skip relocations in known sections (.ctors, .dtors, and
+ .gcc_except_table) since we can fix those up by hand. Also
+ skip forward references to constants. Also skip a difference
+ of two symbols, which still uses the BFD_RELOC_32 at this
+ point. */
+ if (! known_section_p
+ && S_GET_SEGMENT (s) != absolute_section
+ && !fixp->fx_subsy
+ && (flags & (SEC_READONLY | SEC_CODE)) == 0)
+ {
+ non_pic_p = 1;
+ }
+ break;
+
+ /* FIXME -- should determine if any of the GP relocation really uses
+ gr16 (which is not pic safe) or not. Right now, assume if we
+ aren't being compiled with -mpic, the usage is non pic safe, but
+ is safe with -mpic. */
+ 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:
+ non_pic_p = ! frv_pic_p;
+ break;
+
+ case BFD_RELOC_FRV_LO16:
+ case BFD_RELOC_FRV_HI16:
+ if (S_GET_SEGMENT (s) != absolute_section)
+ non_pic_p = 1;
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ non_pic_p = 1;
+ break;
+
+ /* If this is a blessed BFD_RELOC_32, convert it back to the normal
+ relocation. */
+ case BFD_RELOC_CTOR:
+ fixp->fx_r_type = BFD_RELOC_32;
+ break;
+ }
+
+ if (non_pic_p)
+ {
+ DPRINTF1 (" (Non-pic relocation)\n");
+ if (frv_pic_p)
+ as_warn_where (fixp->fx_file, fixp->fx_line,
+ _("Relocation %s is not safe for %s"),
+ bfd_get_reloc_code_name (reloc), frv_pic_flag);
+
+ else if ((frv_flags & EF_FRV_NON_PIC_RELOCS) == 0)
+ {
+ frv_flags |= EF_FRV_NON_PIC_RELOCS;
+ bfd_set_private_flags (abfd, frv_flags);
+ }
+ }
+#ifdef DEBUG
+ else
+ DPRINTF1 ("\n");
+#endif
+ }
+}
+
+/* After all of the symbols have been adjusted, go over the file looking
+ for any relocations that pic won't support. */
+
+void
+frv_frob_file (void)
+{
+ bfd_map_over_sections (stdoutput, frv_frob_file_section, (void *) 0);
+}
+
+void
+frv_frob_label (symbolS *this_label)
+{
+ struct vliw_insn_list *vliw_insn_list_entry;
+
+ dwarf2_emit_label (this_label);
+ if (frv_mach != bfd_mach_frvtomcat)
+ return;
+
+ if (now_seg != text_section)
+ return;
+
+ vliw_insn_list_entry = frv_insert_vliw_insn(DONT_COUNT);
+ vliw_insn_list_entry->type = VLIW_LABEL_TYPE;
+ vliw_insn_list_entry->sym = this_label;
+}
+
+fixS *
+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);
+
+ if (frv_mach == bfd_mach_frvtomcat
+ && current_vliw_insn
+ && current_vliw_insn->type == VLIW_BRANCH_TYPE
+ && exp != NULL)
+ current_vliw_insn->sym = exp->X_add_symbol;
+
+ return fixP;
+}