[binutils][arm] BFloat16 enablement [4/X]
[deliverable/binutils-gdb.git] / gas / config / tc-rl78.c
index 04c9b2c845263efdccb7501fba1fb252f7a64bb7..9160cc027fb077118e666d344f57d3e8fa150928 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-rl78.c -- Assembler for the Renesas RL78
-   Copyright (C) 2011-2014 Free Software Foundation, Inc.
+   Copyright (C) 2011-2019 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    02110-1301, USA.  */
 
 #include "as.h"
-#include "struc-symbol.h"
-#include "obstack.h"
 #include "safe-ctype.h"
 #include "dwarf2dbg.h"
-#include "libbfd.h"
 #include "elf/common.h"
 #include "elf/rl78.h"
 #include "rl78-defs.h"
@@ -85,6 +82,15 @@ typedef struct rl78_bytesT
 
 static rl78_bytesT rl78_bytes;
 
+void
+rl78_relax (int type, int pos)
+{
+  rl78_bytes.relax[rl78_bytes.n_relax].type = type;
+  rl78_bytes.relax[rl78_bytes.n_relax].field_pos = pos;
+  rl78_bytes.relax[rl78_bytes.n_relax].val_ofs = rl78_bytes.n_base + rl78_bytes.n_ops;
+  rl78_bytes.n_relax ++;
+}
+
 void
 rl78_linkrelax_addr16 (void)
 {
@@ -94,6 +100,7 @@ rl78_linkrelax_addr16 (void)
 void
 rl78_linkrelax_branch (void)
 {
+  rl78_relax (RL78_RELAX_BRANCH, 0);
   rl78_bytes.link_relax |= RL78_RELAXA_BRA;
 }
 
@@ -122,7 +129,7 @@ rl78_prefix (int p)
 }
 
 int
-rl78_has_prefix ()
+rl78_has_prefix (void)
 {
   return rl78_bytes.n_prefix;
 }
@@ -199,6 +206,16 @@ rl78_op (expressionS exp, int nbytes, int type)
       if (nbytes > 2
          && exp.X_md == BFD_RELOC_RL78_CODE)
        exp.X_md = 0;
+
+      if (nbytes == 1
+         && (exp.X_md == BFD_RELOC_RL78_LO16
+             || exp.X_md == BFD_RELOC_RL78_HI16))
+       as_bad (_("16-bit relocation used in 8-bit operand"));
+
+      if (nbytes == 2
+         && exp.X_md == BFD_RELOC_RL78_HI8)
+       as_bad (_("8-bit relocation used in 16-bit operand"));
+
       rl78_op_fixup (exp, rl78_bytes.n_ops * 8, nbytes * 8, type);
       memset (rl78_bytes.ops + rl78_bytes.n_ops, 0, nbytes);
       rl78_bytes.n_ops += nbytes;
@@ -262,7 +279,12 @@ rl78_field (int val, int pos, int sz)
 enum options
 {
   OPTION_RELAX = OPTION_MD_BASE,
+  OPTION_NORELAX,
   OPTION_G10,
+  OPTION_G13,
+  OPTION_G14,
+  OPTION_32BIT_DOUBLES,
+  OPTION_64BIT_DOUBLES,
 };
 
 #define RL78_SHORTOPTS ""
@@ -272,32 +294,85 @@ const char * md_shortopts = RL78_SHORTOPTS;
 struct option md_longopts[] =
 {
   {"relax", no_argument, NULL, OPTION_RELAX},
+  {"norelax", no_argument, NULL, OPTION_NORELAX},
   {"mg10", no_argument, NULL, OPTION_G10},
+  {"mg13", no_argument, NULL, OPTION_G13},
+  {"mg14", no_argument, NULL, OPTION_G14},
+  {"mrl78", no_argument, NULL, OPTION_G14},
+  {"m32bit-doubles", no_argument, NULL, OPTION_32BIT_DOUBLES},
+  {"m64bit-doubles", no_argument, NULL, OPTION_64BIT_DOUBLES},
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
 
 int
-md_parse_option (int c, char * arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char * arg ATTRIBUTE_UNUSED)
 {
   switch (c)
     {
     case OPTION_RELAX:
       linkrelax = 1;
       return 1;
+    case OPTION_NORELAX:
+      linkrelax = 0;
+      return 1;
 
     case OPTION_G10:
+      elf_flags &= ~ E_FLAG_RL78_CPU_MASK;
       elf_flags |= E_FLAG_RL78_G10;
       return 1;
+
+    case OPTION_G13:
+      elf_flags &= ~ E_FLAG_RL78_CPU_MASK;
+      elf_flags |= E_FLAG_RL78_G13;
+      return 1;
+
+    case OPTION_G14:
+      elf_flags &= ~ E_FLAG_RL78_CPU_MASK;
+      elf_flags |= E_FLAG_RL78_G14;
+      return 1;
+
+    case OPTION_32BIT_DOUBLES:
+      elf_flags &= ~ E_FLAG_RL78_64BIT_DOUBLES;
+      return 1;
+
+    case OPTION_64BIT_DOUBLES:
+      elf_flags |= E_FLAG_RL78_64BIT_DOUBLES;
+      return 1;
     }
   return 0;
 }
 
-void
-md_show_usage (FILE * stream ATTRIBUTE_UNUSED)
+int
+rl78_isa_g10 (void)
+{
+  return (elf_flags & E_FLAG_RL78_CPU_MASK) == E_FLAG_RL78_G10;
+}
+
+int
+rl78_isa_g13 (void)
 {
+  return (elf_flags & E_FLAG_RL78_CPU_MASK) == E_FLAG_RL78_G13;
 }
 
+int
+rl78_isa_g14 (void)
+{
+  return (elf_flags & E_FLAG_RL78_CPU_MASK) == E_FLAG_RL78_G14;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+  fprintf (stream, _(" RL78 specific command line options:\n"));
+  fprintf (stream, _("  --mrelax          Enable link time relaxation\n"));
+  fprintf (stream, _("  --mg10            Enable support for G10 variant\n"));
+  fprintf (stream, _("  --mg13            Selects the G13 core.\n"));
+  fprintf (stream, _("  --mg14            Selects the G14 core [default]\n"));
+  fprintf (stream, _("  --mrl78           Alias for --mg14\n"));
+  fprintf (stream, _("  --m32bit-doubles  [default]\n"));
+  fprintf (stream, _("  --m64bit-doubles  Source code uses 64-bit doubles\n"));
+}
 
 static void
 s_bss (int ignore ATTRIBUTE_UNUSED)
@@ -309,23 +384,34 @@ s_bss (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+static void
+rl78_float_cons (int ignore ATTRIBUTE_UNUSED)
+{
+  if (elf_flags & E_FLAG_RL78_64BIT_DOUBLES)
+    return float_cons ('d');
+  return float_cons ('f');
+}
+
 /* The target specific pseudo-ops which we support.  */
 const pseudo_typeS md_pseudo_table[] =
 {
-  /* Our "standard" pseudos. */
-  { "double",   float_cons,    'd' },
-  { "bss",     s_bss,          0 },
-  { "3byte",   cons,           3 },
-  { "int",     cons,           4 },
-  { "word",    cons,           4 },
+  /* Our "standard" pseudos.  */
+  { "double", rl78_float_cons, 'd' },
+  { "bss",    s_bss,           0 },
+  { "3byte",  cons,            3 },
+  { "int",    cons,            4 },
+  { "word",   cons,            4 },
 
   /* End of list marker.  */
   { NULL,      NULL,           0 }
 };
 
+static symbolS * rl78_abs_sym = NULL;
+
 void
 md_begin (void)
 {
+  rl78_abs_sym = symbol_make ("__rl78_abs__");
 }
 
 void
@@ -348,7 +434,7 @@ md_number_to_chars (char * buf, valueT val, int n)
 }
 
 static void
-require_end_of_expr (char *fname)
+require_end_of_expr (const char *fname)
 {
   while (* input_line_pointer == ' '
         || * input_line_pointer == '\t')
@@ -366,7 +452,7 @@ require_end_of_expr (char *fname)
 
 static struct
 {
-  char * fname;
+  const char * fname;
   int    reloc;
 }
 reloc_functions[] =
@@ -414,7 +500,7 @@ rl78_frag_init (fragS * fragP)
 {
   if (rl78_bytes.n_relax || rl78_bytes.link_relax)
     {
-      fragP->tc_frag_data = malloc (sizeof (rl78_bytesT));
+      fragP->tc_frag_data = XNEW (rl78_bytesT);
       memcpy (fragP->tc_frag_data, & rl78_bytes, sizeof (rl78_bytesT));
     }
   else
@@ -445,7 +531,7 @@ rl78_handle_align (fragS * frag)
     }
 }
 
-char *
+const char *
 md_atof (int type, char * litP, int * sizeP)
 {
   return ieee_md_atof (type, litP, sizeP, target_big_endian);
@@ -486,12 +572,13 @@ md_assemble (char * str)
   rl78_parse ();
 
   /* This simplifies the relaxation code.  */
-  if (rl78_bytes.link_relax)
+  if (rl78_bytes.n_relax || rl78_bytes.link_relax)
     {
       int olen = rl78_bytes.n_prefix + rl78_bytes.n_base + rl78_bytes.n_ops;
       /* We do it this way because we want the frag to have the
-        rl78_bytes in it, which we initialize above.  */
-      bytes = frag_more (olen);
+        rl78_bytes in it, which we initialize above.  The extra bytes
+        are for relaxing.  */
+      bytes = frag_more (olen + 3);
       frag_then = frag_now;
       frag_variant (rs_machine_dependent,
                    olen /* max_chars */,
@@ -601,13 +688,23 @@ rl78_cons_fix_new (fragS *        frag,
     case BFD_RELOC_RL78_LO16:
     case BFD_RELOC_RL78_HI16:
       if (size != 2)
-       as_bad (_("%%hi16/%%lo16 only applies to .short or .hword"));
-      type = exp->X_md;
+       {
+         /* Fixups to assembler generated expressions do not use %hi or %lo.  */
+         if (frag->fr_file)
+           as_bad (_("%%hi16/%%lo16 only applies to .short or .hword"));
+       }
+      else
+       type = exp->X_md;
       break;
     case BFD_RELOC_RL78_HI8:
       if (size != 1)
-       as_bad (_("%%hi8 only applies to .byte"));
-      type = exp->X_md;
+       {
+         /* Fixups to assembler generated expressions do not use %hi or %lo.  */
+         if (frag->fr_file)
+           as_bad (_("%%hi8 only applies to .byte"));
+       }
+      else
+       type = exp->X_md;
       break;
     default:
       break;
@@ -638,13 +735,514 @@ rl78_cons_fix_new (fragS *       frag,
     }
 }
 
-/* No relaxation just yet */
+\f
+/*----------------------------------------------------------------------*/
+/* To recap: we estimate everything based on md_estimate_size, then
+   adjust based on rl78_relax_frag.  When it all settles, we call
+   md_convert frag to update the bytes.  The relaxation types and
+   relocations are in fragP->tc_frag_data, which is a copy of that
+   rl78_bytes.
+
+   Our scheme is as follows: fr_fix has the size of the smallest
+   opcode (like BRA.S).  We store the number of total bytes we need in
+   fr_subtype.  When we're done relaxing, we use fr_subtype and the
+   existing opcode bytes to figure out what actual opcode we need to
+   put in there.  If the fixup isn't resolvable now, we use the
+   maximal size.  */
+
+#define TRACE_RELAX 0
+#define tprintf if (TRACE_RELAX) printf
+
+
+typedef enum
+{
+  OT_other,
+  OT_bt,
+  OT_bt_sfr,
+  OT_bt_es,
+  OT_bc,
+  OT_bh,
+  OT_sk,
+  OT_call,
+  OT_br,
+} op_type_T;
+
+/* We're looking for these types of relaxations:
+
+   BT          00110001 sbit0cc1 addr----      (cc is 10 (BF) or 01 (BT))
+   B~T         00110001 sbit0cc1 00000011 11101110 pcrel16- -------- (BR $!pcrel20)
+
+   BT sfr      00110001 sbit0cc0 sfr----- addr----
+   BT ES:      00010001 00101110 sbit0cc1 addr----
+
+   BC          110111cc addr----
+   B~C         110111cc 00000011 11101110 pcrel16- -------- (BR $!pcrel20)
+
+   BH          01100001 110c0011 00000011 11101110 pcrel16- -------- (BR $!pcrel20)
+   B~H         01100001 110c0011 00000011 11101110 pcrel16- -------- (BR $!pcrel20)
+*/
+
+/* Given the opcode bytes at OP, figure out which opcode it is and
+   return the type of opcode.  We use this to re-encode the opcode as
+   a different size later.  */
+
+static op_type_T
+rl78_opcode_type (char * ops)
+{
+  unsigned char *op = (unsigned char *)ops;
+
+  if (op[0] == 0x31
+      && ((op[1] & 0x0f) == 0x05
+         || (op[1] & 0x0f) == 0x03))
+    return OT_bt;
+
+  if (op[0] == 0x31
+      && ((op[1] & 0x0f) == 0x04
+         || (op[1] & 0x0f) == 0x02))
+    return OT_bt_sfr;
+
+  if (op[0] == 0x11
+      && op[1] == 0x31
+      && ((op[2] & 0x0f) == 0x05
+         || (op[2] & 0x0f) == 0x03))
+    return OT_bt_es;
+
+  if ((op[0] & 0xfc) == 0xdc)
+    return OT_bc;
+
+  if (op[0] == 0x61
+      && (op[1] & 0xef) == 0xc3)
+    return OT_bh;
+
+  if (op[0] == 0x61
+      && (op[1] & 0xcf) == 0xc8)
+    return OT_sk;
+
+  if (op[0] == 0x61
+      && (op[1] & 0xef) == 0xe3)
+    return OT_sk;
+
+  if (op[0] == 0xfc)
+    return OT_call;
+
+  if ((op[0] & 0xec) == 0xec)
+    return OT_br;
+
+  return OT_other;
+}
+
+/* Returns zero if *addrP has the target address.  Else returns nonzero
+   if we cannot compute the target address yet.  */
+
+static int
+rl78_frag_fix_value (fragS *    fragP,
+                    segT       segment,
+                    int        which,
+                    addressT * addrP,
+                    int        need_diff,
+                    addressT * sym_addr)
+{
+  addressT addr = 0;
+  rl78_bytesT * b = fragP->tc_frag_data;
+  expressionS * exp = & b->fixups[which].exp;
+
+  if (need_diff && exp->X_op != O_subtract)
+    return 1;
+
+  if (exp->X_add_symbol)
+    {
+      if (S_FORCE_RELOC (exp->X_add_symbol, 1))
+       return 1;
+      if (S_GET_SEGMENT (exp->X_add_symbol) != segment)
+       return 1;
+      addr += S_GET_VALUE (exp->X_add_symbol);
+    }
+
+  if (exp->X_op_symbol)
+    {
+      if (exp->X_op != O_subtract)
+       return 1;
+      if (S_FORCE_RELOC (exp->X_op_symbol, 1))
+       return 1;
+      if (S_GET_SEGMENT (exp->X_op_symbol) != segment)
+       return 1;
+      addr -= S_GET_VALUE (exp->X_op_symbol);
+    }
+  if (sym_addr)
+    * sym_addr = addr;
+  addr += exp->X_add_number;
+  * addrP = addr;
+  return 0;
+}
+
+/* Estimate how big the opcode is after this relax pass.  The return
+   value is the difference between fr_fix and the actual size.  We
+   compute the total size in rl78_relax_frag and store it in fr_subtype,
+   so we only need to subtract fx_fix and return it.  */
+
 int
 md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED)
 {
-  return 0;
+  int opfixsize;
+  int delta;
+
+  /* This is the size of the opcode that's accounted for in fr_fix.  */
+  opfixsize = fragP->fr_fix - (fragP->fr_opcode - fragP->fr_literal);
+  /* This is the size of the opcode that isn't.  */
+  delta = (fragP->fr_subtype - opfixsize);
+
+  tprintf (" -> opfixsize %d delta %d\n", opfixsize, delta);
+  return delta;
+}
+
+/* Given the new addresses for this relax pass, figure out how big
+   each opcode must be.  We store the total number of bytes needed in
+   fr_subtype.  The return value is the difference between the size
+   after the last pass and the size after this pass, so we use the old
+   fr_subtype to calculate the difference.  */
+
+int
+rl78_relax_frag (segT segment ATTRIBUTE_UNUSED, fragS * fragP, long stretch)
+{
+  addressT addr0, sym_addr;
+  addressT mypc;
+  int disp;
+  int oldsize = fragP->fr_subtype;
+  int newsize = oldsize;
+  op_type_T optype;
+  int ri;
+
+  mypc = fragP->fr_address + (fragP->fr_opcode - fragP->fr_literal);
+
+  /* If we ever get more than one reloc per opcode, this is the one
+     we're relaxing.  */
+  ri = 0;
+
+  optype = rl78_opcode_type (fragP->fr_opcode);
+  /* Try to get the target address.  */
+  if (rl78_frag_fix_value (fragP, segment, ri, & addr0,
+                          fragP->tc_frag_data->relax[ri].type != RL78_RELAX_BRANCH,
+                          & sym_addr))
+    {
+      /* If we don't expect the linker to do relaxing, don't emit
+        expanded opcodes that only the linker will relax.  */
+      if (!linkrelax)
+       return newsize - oldsize;
+
+      /* If we don't, we must use the maximum size for the linker.  */
+      switch (fragP->tc_frag_data->relax[ri].type)
+       {
+       case RL78_RELAX_BRANCH:
+         switch (optype)
+           {
+           case OT_bt:
+             newsize = 6;
+             break;
+           case OT_bt_sfr:
+           case OT_bt_es:
+             newsize = 7;
+             break;
+           case OT_bc:
+             newsize = 5;
+             break;
+           case OT_bh:
+             newsize = 6;
+             break;
+           case OT_sk:
+             newsize = 2;
+             break;
+           default:
+             newsize = oldsize;
+             break;
+           }
+         break;
+
+       }
+      fragP->fr_subtype = newsize;
+      tprintf (" -> new %d old %d delta %d (external)\n", newsize, oldsize, newsize-oldsize);
+      return newsize - oldsize;
+    }
+
+  if (sym_addr > mypc)
+    addr0 += stretch;
+
+  switch (fragP->tc_frag_data->relax[ri].type)
+    {
+    case  RL78_RELAX_BRANCH:
+      disp = (int) addr0 - (int) mypc;
+
+      switch (optype)
+       {
+       case OT_bt:
+         if (disp >= -128 && (disp - (oldsize-2)) <= 127)
+           newsize = 3;
+         else
+           newsize = 6;
+         break;
+       case OT_bt_sfr:
+       case OT_bt_es:
+         if (disp >= -128 && (disp - (oldsize-3)) <= 127)
+           newsize = 4;
+         else
+           newsize = 7;
+         break;
+       case OT_bc:
+         if (disp >= -128 && (disp - (oldsize-1)) <= 127)
+           newsize = 2;
+         else
+           newsize = 5;
+         break;
+       case OT_bh:
+         if (disp >= -128 && (disp - (oldsize-2)) <= 127)
+           newsize = 3;
+         else
+           newsize = 6;
+         break;
+       case OT_sk:
+         newsize = 2;
+         break;
+       default:
+         newsize = oldsize;
+         break;
+       }
+      break;
+    }
+
+  /* This prevents infinite loops in align-heavy sources.  */
+  if (newsize < oldsize)
+    {
+      if (fragP->tc_frag_data->times_shrank > 10
+         && fragP->tc_frag_data->times_grown > 10)
+       newsize = oldsize;
+      if (fragP->tc_frag_data->times_shrank < 20)
+       fragP->tc_frag_data->times_shrank ++;
+    }
+  else if (newsize > oldsize)
+    {
+      if (fragP->tc_frag_data->times_grown < 20)
+       fragP->tc_frag_data->times_grown ++;
+    }
+
+  fragP->fr_subtype = newsize;
+  tprintf (" -> new %d old %d delta %d\n", newsize, oldsize, newsize-oldsize);
+  return newsize - oldsize;
+}
+
+/* This lets us test for the opcode type and the desired size in a
+   switch statement.  */
+#define OPCODE(type,size) ((type) * 16 + (size))
+
+/* Given the opcode stored in fr_opcode and the number of bytes we
+   think we need, encode a new opcode.  We stored a pointer to the
+   fixup for this opcode in the tc_frag_data structure.  If we can do
+   the fixup here, we change the relocation type to "none" (we test
+   for that in tc_gen_reloc) else we change it to the right type for
+   the new (biggest) opcode.  */
+
+void
+md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
+                segT    segment ATTRIBUTE_UNUSED,
+                fragS * fragP ATTRIBUTE_UNUSED)
+{
+  rl78_bytesT * rl78b = fragP->tc_frag_data;
+  addressT addr0, mypc;
+  int disp;
+  int reloc_type, reloc_adjust;
+  char * op = fragP->fr_opcode;
+  int keep_reloc = 0;
+  int ri;
+  int fi = (rl78b->n_fixups > 1) ? 1 : 0;
+  fixS * fix = rl78b->fixups[fi].fixP;
+
+  /* If we ever get more than one reloc per opcode, this is the one
+     we're relaxing.  */
+  ri = 0;
+
+  /* We used a new frag for this opcode, so the opcode address should
+     be the frag address.  */
+  mypc = fragP->fr_address + (fragP->fr_opcode - fragP->fr_literal);
+  tprintf ("\033[32mmypc: 0x%x\033[0m\n", (int)mypc);
+
+  /* Try to get the target address.  If we fail here, we just use the
+     largest format.  */
+  if (rl78_frag_fix_value (fragP, segment, 0, & addr0,
+                          fragP->tc_frag_data->relax[ri].type != RL78_RELAX_BRANCH, 0))
+    {
+      /* We don't know the target address.  */
+      keep_reloc = 1;
+      addr0 = 0;
+      disp = 0;
+      tprintf ("unknown addr ? - %x = ?\n", (int)mypc);
+    }
+  else
+    {
+      /* We know the target address, and it's in addr0.  */
+      disp = (int) addr0 - (int) mypc;
+      tprintf ("known addr %x - %x = %d\n", (int)addr0, (int)mypc, disp);
+    }
+
+  if (linkrelax)
+    keep_reloc = 1;
+
+  reloc_type = BFD_RELOC_NONE;
+  reloc_adjust = 0;
+
+  switch (fragP->tc_frag_data->relax[ri].type)
+    {
+    case RL78_RELAX_BRANCH:
+      switch (OPCODE (rl78_opcode_type (fragP->fr_opcode), fragP->fr_subtype))
+       {
+
+       case OPCODE (OT_bt, 3): /* BT A,$ - no change.  */
+         disp -= 3;
+         op[2] = disp;
+         reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+         break;
+
+       case OPCODE (OT_bt, 6): /* BT A,$ - long version.  */
+         disp -= 3;
+         op[1] ^= 0x06; /* toggle conditional.  */
+         op[2] = 3; /* displacement over long branch.  */
+         disp -= 3;
+         op[3] = 0xEE; /* BR $!addr20 */
+         op[4] = disp & 0xff;
+         op[5] = disp >> 8;
+         reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+         reloc_adjust = 2;
+         break;
+
+       case OPCODE (OT_bt_sfr, 4): /* BT PSW,$ - no change.  */
+         disp -= 4;
+         op[3] = disp;
+         reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+         break;
+
+       case OPCODE (OT_bt_sfr, 7): /* BT PSW,$ - long version.  */
+         disp -= 4;
+         op[1] ^= 0x06; /* toggle conditional.  */
+         op[3] = 3; /* displacement over long branch.  */
+         disp -= 3;
+         op[4] = 0xEE; /* BR $!addr20 */
+         op[5] = disp & 0xff;
+         op[6] = disp >> 8;
+         reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+         reloc_adjust = 2;
+         break;
+
+       case OPCODE (OT_bt_es, 4): /* BT ES:[HL],$ - no change.  */
+         disp -= 4;
+         op[3] = disp;
+         reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+         break;
+
+       case OPCODE (OT_bt_es, 7): /* BT PSW,$ - long version.  */
+         disp -= 4;
+         op[2] ^= 0x06; /* toggle conditional.  */
+         op[3] = 3; /* displacement over long branch.  */
+         disp -= 3;
+         op[4] = 0xEE; /* BR $!addr20 */
+         op[5] = disp & 0xff;
+         op[6] = disp >> 8;
+         reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+         reloc_adjust = 2;
+         break;
+
+       case OPCODE (OT_bc, 2): /* BC $ - no change.  */
+         disp -= 2;
+         op[1] = disp;
+         reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+         break;
+
+       case OPCODE (OT_bc, 5): /* BC $ - long version.  */
+         disp -= 2;
+         op[0] ^= 0x02; /* toggle conditional.  */
+         op[1] = 3;
+         disp -= 3;
+         op[2] = 0xEE; /* BR $!addr20 */
+         op[3] = disp & 0xff;
+         op[4] = disp >> 8;
+         reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+         reloc_adjust = 2;
+         break;
+
+       case OPCODE (OT_bh, 3): /* BH $ - no change.  */
+         disp -= 3;
+         op[2] = disp;
+         reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+         break;
+
+       case OPCODE (OT_bh, 6): /* BC $ - long version.  */
+         disp -= 3;
+         op[1] ^= 0x10; /* toggle conditional.  */
+         op[2] = 3;
+         disp -= 3;
+         op[3] = 0xEE; /* BR $!addr20 */
+         op[4] = disp & 0xff;
+         op[5] = disp >> 8;
+         reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+         reloc_adjust = 2;
+         break;
+
+       case OPCODE (OT_sk, 2): /* SK<cond> - no change */
+         reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+         break;
+
+       default:
+         reloc_type = fix ? fix->fx_r_type : BFD_RELOC_NONE;
+         break;
+       }
+      break;
+
+    default:
+      if (rl78b->n_fixups)
+       {
+         reloc_type = fix->fx_r_type;
+         reloc_adjust = 0;
+       }
+      break;
+    }
+
+  if (rl78b->n_fixups)
+    {
+
+      fix->fx_r_type = reloc_type;
+      fix->fx_where += reloc_adjust;
+      switch (reloc_type)
+       {
+       case BFD_RELOC_NONE:
+         fix->fx_size = 0;
+         break;
+       case BFD_RELOC_8:
+         fix->fx_size = 1;
+         break;
+       case BFD_RELOC_16_PCREL:
+         fix->fx_size = 2;
+         break;
+       }
+    }
+
+  fragP->fr_fix = fragP->fr_subtype + (fragP->fr_opcode - fragP->fr_literal);
+  tprintf ("fragP->fr_fix now %ld (%d + (%p - %p)\n", (long) fragP->fr_fix,
+         fragP->fr_subtype, fragP->fr_opcode, fragP->fr_literal);
+  fragP->fr_var = 0;
+
+  tprintf ("compare 0x%lx vs 0x%lx - 0x%lx = 0x%lx (%p)\n",
+          (long)fragP->fr_fix,
+          (long)fragP->fr_next->fr_address, (long)fragP->fr_address,
+          (long)(fragP->fr_next->fr_address - fragP->fr_address),
+          fragP->fr_next);
+
+  if (fragP->fr_next != NULL
+      && fragP->fr_next->fr_address - fragP->fr_address != fragP->fr_fix)
+    as_bad (_("bad frag at %p : fix %ld addr %ld %ld \n"), fragP,
+           (long) fragP->fr_fix,
+           (long) fragP->fr_address, (long) fragP->fr_next->fr_address);
 }
 
+/* End of relaxation code.
+  ----------------------------------------------------------------------*/
+\f
+
 arelent **
 tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp)
 {
@@ -657,6 +1255,12 @@ tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp)
       return reloc;
     }
 
+  if (fixp->fx_r_type == BFD_RELOC_RL78_RELAX && !linkrelax)
+    {
+      reloc[0] = NULL;
+      return reloc;
+    }
+
   if (fixp->fx_subsy
       && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
     {
@@ -664,8 +1268,8 @@ tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp)
       fixp->fx_subsy = NULL;
     }
 
-  reloc[0]               = (arelent *) xmalloc (sizeof (arelent));
-  reloc[0]->sym_ptr_ptr   = (asymbol **) xmalloc (sizeof (asymbol *));
+  reloc[0]               = XNEW (arelent);
+  reloc[0]->sym_ptr_ptr   = XNEW (asymbol *);
   * reloc[0]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc[0]->address       = fixp->fx_frag->fr_address + fixp->fx_where;
   reloc[0]->addend        = fixp->fx_offset;
@@ -677,15 +1281,25 @@ tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp)
     }
 
 #define OPX(REL,SYM,ADD)                                                       \
-  reloc[rp]               = (arelent *) xmalloc (sizeof (arelent));            \
-  reloc[rp]->sym_ptr_ptr   = (asymbol **) xmalloc (sizeof (asymbol *));                \
+  reloc[rp]               = XNEW (arelent);            \
+  reloc[rp]->sym_ptr_ptr   = XNEW (asymbol *);         \
   reloc[rp]->howto         = bfd_reloc_type_lookup (stdoutput, REL);           \
   reloc[rp]->addend        = ADD;                                              \
   * reloc[rp]->sym_ptr_ptr = SYM;                                              \
   reloc[rp]->address       = fixp->fx_frag->fr_address + fixp->fx_where;       \
   reloc[++rp] = NULL
 #define OPSYM(SYM) OPX(BFD_RELOC_RL78_SYM, SYM, 0)
-#define OPIMM(IMM) OPX(BFD_RELOC_RL78_SYM, abs_symbol.bsym, IMM)
+
+  /* FIXME: We cannot do the normal thing for an immediate value reloc,
+     ie creating a RL78_SYM reloc in the *ABS* section with an offset
+     equal to the immediate value we want to store.  This fails because
+     the reloc processing in bfd_perform_relocation and bfd_install_relocation
+     will short circuit such relocs and never pass them on to the special
+     reloc processing code.  So instead we create a RL78_SYM reloc against
+     the __rl78_abs__ symbol and arrange for the linker scripts to place
+     this symbol at address 0.  */
+#define OPIMM(IMM) OPX (BFD_RELOC_RL78_SYM, symbol_get_bfdsym (rl78_abs_sym), IMM)
+
 #define OP(OP) OPX(BFD_RELOC_RL78_##OP, *reloc[0]->sym_ptr_ptr, 0)
 #define SYM0() reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RL78_SYM)
 
@@ -722,8 +1336,8 @@ tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp)
       break;
 
     case BFD_RELOC_RL78_CODE:
-      SYM0 ();
-      OP (ABS16);
+      reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RL78_16U);
+      reloc[1] = NULL;
       break;
 
     case BFD_RELOC_RL78_LO16:
@@ -808,6 +1422,11 @@ md_apply_fix (struct fix * f ATTRIBUTE_UNUSED,
   char * op;
   unsigned long val;
 
+  /* We always defer overflow checks for these to the linker, as it
+     needs to do PLT stuff.  */
+  if (f->fx_r_type == BFD_RELOC_RL78_CODE)
+    f->fx_no_overflow = 1;
+
   if (f->fx_addsy && S_FORCE_RELOC (f->fx_addsy, 1))
     return;
   if (f->fx_subsy && S_FORCE_RELOC (f->fx_subsy, 1))
@@ -816,22 +1435,36 @@ md_apply_fix (struct fix * f ATTRIBUTE_UNUSED,
   op = f->fx_frag->fr_literal + f->fx_where;
   val = (unsigned long) * t;
 
+  if (f->fx_addsy == NULL)
+    f->fx_done = 1;
+
   switch (f->fx_r_type)
     {
     case BFD_RELOC_NONE:
       break;
 
     case BFD_RELOC_RL78_RELAX:
-      f->fx_done = 1;
+      f->fx_done = 0;
       break;
 
-    case BFD_RELOC_8:
     case BFD_RELOC_8_PCREL:
+      if ((long)val < -128 || (long)val > 127)
+       as_bad_where (f->fx_file, f->fx_line,
+                     _("value of %ld too large for 8-bit branch"),
+                     val);
+      /* Fall through.  */
+    case BFD_RELOC_8:
+    case BFD_RELOC_RL78_SADDR: /* We need to store the 8 LSB, but this works.  */
       op[0] = val;
       break;
 
-    case BFD_RELOC_16:
     case BFD_RELOC_16_PCREL:
+      if ((long)val < -32768 || (long)val > 32767)
+       as_bad_where (f->fx_file, f->fx_line,
+                     _("value of %ld too large for 16-bit branch"),
+                     val);
+      /* Fall through.  */
+    case BFD_RELOC_16:
     case BFD_RELOC_RL78_CODE:
       op[0] = val;
       op[1] = val >> 8;
@@ -844,35 +1477,49 @@ md_apply_fix (struct fix * f ATTRIBUTE_UNUSED,
       break;
 
     case BFD_RELOC_32:
-    case BFD_RELOC_RL78_DIFF:
       op[0] = val;
       op[1] = val >> 8;
       op[2] = val >> 16;
       op[3] = val >> 24;
       break;
 
+    case BFD_RELOC_RL78_DIFF:
+      op[0] = val;
+      if (f->fx_size > 1)
+       op[1] = val >> 8;
+      if (f->fx_size > 2)
+       op[2] = val >> 16;
+      if (f->fx_size > 3)
+       op[3] = val >> 24;
+      break;
+
+    case BFD_RELOC_RL78_HI8:
+      val = val >> 16;
+      op[0] = val;
+      break;
+
+    case BFD_RELOC_RL78_HI16:
+      val = val >> 16;
+      op[0] = val;
+      op[1] = val >> 8;
+      break;
+
+    case BFD_RELOC_RL78_LO16:
+      op[0] = val;
+      op[1] = val >> 8;
+      break;
+
     default:
       as_bad (_("Unknown reloc in md_apply_fix: %s"),
              bfd_get_reloc_code_name (f->fx_r_type));
       break;
     }
 
-  if (f->fx_addsy == NULL)
-    f->fx_done = 1;
 }
 
 valueT
 md_section_align (segT segment, valueT size)
 {
-  int align = bfd_get_section_alignment (stdoutput, segment);
-  return ((size + (1 << align) - 1) & (-1 << align));
-}
-
-void
-md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
-                segT    segment ATTRIBUTE_UNUSED,
-                fragS * fragP ATTRIBUTE_UNUSED)
-{
-  /* No relaxation yet */
-  fragP->fr_var = 0;
+  int align = bfd_section_alignment (segment);
+  return ((size + (1 << align) - 1) & -(1 << align));
 }
This page took 0.034221 seconds and 4 git commands to generate.