]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-m32c.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gas / config / tc-m32c.c
index 8f4f50225ed4ccccd0007eb1c0908ac990c34c7a..c490278f711214dabd42bc9f0cf63d3beddc46dc 100644 (file)
@@ -1,12 +1,12 @@
 /* tc-m32c.c -- Assembler for the Renesas M32C.
-   Copyright (C) 2005 Free Software Foundation.
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
    Contributed by RedHat.
 
    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,
    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, 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
-#include <stdio.h>
 #include "as.h"
-#include "subsegs.h"     
+#include "subsegs.h"
 #include "symcat.h"
 #include "opcodes/m32c-desc.h"
 #include "opcodes/m32c-opc.h"
 #include "cgen.h"
 #include "elf/common.h"
 #include "elf/m32c.h"
-#include "libbfd.h"
-#include "libiberty.h"
 #include "safe-ctype.h"
-#include "bfd.h"
 
 /* Structure to hold all of the different components
    describing an individual instruction.  */
@@ -55,6 +51,9 @@ typedef struct
 }
 m32c_insn;
 
+#define rl_for(_insn) (CGEN_ATTR_CGEN_INSN_RL_TYPE_VALUE (&((_insn).insn->base->attrs)))
+#define relaxable(_insn) (CGEN_ATTR_CGEN_INSN_RELAXABLE_VALUE (&((_insn).insn->base->attrs)))
+
 const char comment_chars[]        = ";";
 const char line_comment_chars[]   = "#";
 const char line_separator_chars[] = "|";
@@ -67,11 +66,15 @@ const char * md_shortopts = M32C_SHORTOPTS;
 /* assembler options */
 #define OPTION_CPU_M16C               (OPTION_MD_BASE)
 #define OPTION_CPU_M32C        (OPTION_MD_BASE + 1)
+#define OPTION_LINKRELAX       (OPTION_MD_BASE + 2)
+#define OPTION_H_TICK_HEX      (OPTION_MD_BASE + 3)
 
 struct option md_longopts[] =
 {
   { "m16c",       no_argument,       NULL, OPTION_CPU_M16C   },
   { "m32c",       no_argument,       NULL, OPTION_CPU_M32C   },
+  { "relax",      no_argument,       NULL, OPTION_LINKRELAX   },
+  { "h-tick-hex", no_argument,       NULL, OPTION_H_TICK_HEX  },
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
@@ -83,7 +86,7 @@ size_t md_longopts_size = sizeof (md_longopts);
 
 static unsigned long m32c_mach = bfd_mach_m16c;
 static int cpu_mach = (1 << MACH_M16C);
-static int insn_size;
+static int m32c_relax = 0;
 
 /* Flags to set in the elf header */
 static flagword m32c_flags = DEFAULT_FLAGS;
@@ -100,7 +103,7 @@ set_isa (enum isa_attr isa_num)
 static void s_bss (int);
 
 int
-md_parse_option (int c, char * arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char * arg ATTRIBUTE_UNUSED)
 {
   switch (c)
     {
@@ -118,6 +121,14 @@ md_parse_option (int c, char * arg ATTRIBUTE_UNUSED)
       set_isa (ISA_M32C);
       break;
 
+    case OPTION_LINKRELAX:
+      m32c_relax = 1;
+      break;
+
+    case OPTION_H_TICK_HEX:
+      enable_h_tick_hex = 1;
+      break;
+
     default:
       return 0;
     }
@@ -128,7 +139,7 @@ void
 md_show_usage (FILE * stream)
 {
   fprintf (stream, _(" M32C specific command line options:\n"));
-} 
+}
 
 static void
 s_bss (int ignore ATTRIBUTE_UNUSED)
@@ -144,6 +155,7 @@ s_bss (int ignore ATTRIBUTE_UNUSED)
 const pseudo_typeS md_pseudo_table[] =
 {
   { "bss",     s_bss,          0},
+  { "3byte",   cons,           3 },
   { "word",    cons,           4 },
   { NULL,      NULL,           0 }
 };
@@ -153,7 +165,7 @@ void
 md_begin (void)
 {
   /* Initialize the `cgen' interface.  */
-  
+
   /* Set the machine number and endian.  */
   gas_cgen_cpu_desc = m32c_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, cpu_mach,
                                          CGEN_CPU_OPEN_ENDIAN,
@@ -172,22 +184,6 @@ md_begin (void)
 
   /* Set the machine type */
   bfd_default_set_arch_mach (stdoutput, bfd_arch_m32c, m32c_mach);
-
-  insn_size = 0;
-}
-
-void
-m32c_md_end (void)
-{
-  int i, n_nops;
-
-  if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
-    {
-      /* Pad with nops for objdump.  */
-      n_nops = (32 - ((insn_size) % 32)) / 8;
-      for (i = 1; i <= n_nops; i++)
-       md_assemble ("nop");
-    }
 }
 
 void
@@ -247,21 +243,21 @@ m32c_indirect_operand (char *str)
       if (s[0] == '[' && s[1] == '[')
        indirection[operand] = relative;
     }
-   
+
   if (indirection[1] == none && indirection[2] == none)
     return FALSE;
-  
+
   operand = 1;
   ns_len = strlen (str);
-  new_str = (char*) xmalloc (ns_len);
+  new_str = XNEWVEC (char, ns_len);
   ns = new_str;
   ns_end = ns + ns_len;
+
   for (s = str; *s; s++)
     {
       if (s[0] == ',')
        operand = 2;
+
       if (s[0] == '[' && ! brace_n[operand])
        {
          brace_n[operand] += 1;
@@ -269,7 +265,7 @@ m32c_indirect_operand (char *str)
          if (indirection[operand] != none)
            continue;
        }
+
       else if (s[0] == '[' && brace_n[operand])
        {
          brace_n[operand] += 1;
@@ -301,13 +297,13 @@ m32c_indirect_operand (char *str)
       {
        fprintf (stderr, "Unmatched [[operand-%d]] %d\n", operand, brace_n[operand]);
       }
-       
+
   if (indirection[1] != none && indirection[2] != none)
-    md_assemble ("src-dest-indirect");
+    md_assemble ((char *) "src-dest-indirect");
   else if (indirection[1] != none)
-    md_assemble ("src-indirect");
+    md_assemble ((char *) "src-indirect");
   else if (indirection[2] != none)
-    md_assemble ("dest-indirect");
+    md_assemble ((char *) "dest-indirect");
 
   md_assemble (new_str);
   free (new_str);
@@ -320,6 +316,9 @@ md_assemble (char * str)
   static int last_insn_had_delay_slot = 0;
   m32c_insn insn;
   char *    errmsg;
+  finished_insnS results;
+  int rl_type;
+  int insn_size;
 
   if (m32c_mach == bfd_mach_m32c && m32c_indirect_operand (str))
     return;
@@ -329,26 +328,60 @@ md_assemble (char * str)
 
   insn.insn = m32c_cgen_assemble_insn
     (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
-  
+
   if (!insn.insn)
     {
       as_bad ("%s", errmsg);
       return;
     }
 
+  results.num_fixups = 0;
   /* 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, NULL);
+                       CGEN_FIELDS_BITSIZE (& insn.fields), 1, &results);
 
   last_insn_had_delay_slot
     = CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
+  (void) last_insn_had_delay_slot;
   insn_size = CGEN_INSN_BITSIZE(insn.insn);
+
+  rl_type = rl_for (insn);
+
+  /* We have to mark all the jumps, because we need to adjust them
+     when we delete bytes, but we only need to mark the displacements
+     if they're symbolic - if they're not, we've already picked the
+     shortest opcode by now.  The linker, however, will still have to
+     check any operands to see if they're the displacement type, since
+     we don't know (nor record) *which* operands are relaxable.  */
+  if (m32c_relax
+      && rl_type != RL_TYPE_NONE
+      && (rl_type == RL_TYPE_JUMP || results.num_fixups)
+      && !relaxable (insn))
+    {
+      int reloc = 0;
+      int addend = results.num_fixups + 16 * insn_size/8;
+
+      switch (rl_for (insn))
+       {
+       case RL_TYPE_JUMP:  reloc = BFD_RELOC_M32C_RL_JUMP;  break;
+       case RL_TYPE_1ADDR: reloc = BFD_RELOC_M32C_RL_1ADDR; break;
+       case RL_TYPE_2ADDR: reloc = BFD_RELOC_M32C_RL_2ADDR; break;
+       }
+      if (insn.insn->base->num == M32C_INSN_JMP16_S
+         || insn.insn->base->num == M32C_INSN_JMP32_S)
+       addend = 0x10;
+
+      fix_new (results.frag,
+              results.addr - results.frag->fr_literal,
+              0, abs_section_sym, addend, 0,
+              reloc);
+    }
 }
 
 /* The syntax in the manual says constants begin with '#'.
    We just ignore it.  */
 
-void 
+void
 md_operand (expressionS * exp)
 {
   /* In case of a syntax error, escape back to try next syntax combo. */
@@ -359,8 +392,8 @@ md_operand (expressionS * exp)
 valueT
 md_section_align (segT segment, valueT size)
 {
-  int align = bfd_get_section_alignment (stdoutput, segment);
-  return ((size + (1 << align) - 1) & (-1 << align));
+  int align = bfd_section_alignment (segment);
+  return ((size + (1 << align) - 1) & -(1 << align));
 }
 
 symbolS *
@@ -405,7 +438,12 @@ const relax_typeS md_relax_table[] =
  /* 19 */ { 32767, -32768, 3, 20 }, /* jsr16.w */
  /* 20 */ {     0,      0, 4,  0 }, /* jsr16.a */
  /* 21 */ { 32767, -32768, 3, 11 }, /* jsr32.w */
- /* 22 */ {     0,      0, 4,  0 }  /* jsr32.a */
+ /* 22 */ {     0,      0, 4,  0 }, /* jsr32.a */
+
+ /* 23 */ {     0,      0, 3,  0 }, /* adjnz pc8 */
+ /* 24 */ {     0,      0, 4,  0 }, /* adjnz disp8 pc8 */
+ /* 25 */ {     0,      0, 5,  0 }, /* adjnz disp16 pc8 */
+ /* 26 */ {     0,      0, 6,  0 }  /* adjnz disp24 pc8 */
 };
 
 enum {
@@ -415,7 +453,12 @@ enum {
   M32C_MACRO_JCND16_A,
   M32C_MACRO_JCND32_W,
   M32C_MACRO_JCND32_A,
-} M32C_Macros;
+  /* the digit is the array index of the pcrel byte */
+  M32C_MACRO_ADJNZ_2,
+  M32C_MACRO_ADJNZ_3,
+  M32C_MACRO_ADJNZ_4,
+  M32C_MACRO_ADJNZ_5,
+};
 
 static struct {
   int insn;
@@ -451,7 +494,12 @@ static struct {
  /* 19 */ {  M32C_INSN_JSR16_W,     3, M32C_INSN_JSR16_A,     2 },
  /* 20 */ {  M32C_INSN_JSR16_A,     4, M32C_INSN_JSR16_A,     0 },
  /* 21 */ {  M32C_INSN_JSR32_W,     3, M32C_INSN_JSR32_A,     2 },
- /* 22 */ {  M32C_INSN_JSR32_A,     4, M32C_INSN_JSR32_A,     0 }
+ /* 22 */ {  M32C_INSN_JSR32_A,     4, M32C_INSN_JSR32_A,     0 },
+
+ /* 23 */ { -M32C_MACRO_ADJNZ_2,    3, -M32C_MACRO_ADJNZ_2,    0 },
+ /* 24 */ { -M32C_MACRO_ADJNZ_3,    4, -M32C_MACRO_ADJNZ_3,    0 },
+ /* 25 */ { -M32C_MACRO_ADJNZ_4,    5, -M32C_MACRO_ADJNZ_4,    0 },
+ /* 26 */ { -M32C_MACRO_ADJNZ_5,    6, -M32C_MACRO_ADJNZ_5,    0 }
 };
 #define NUM_MAPPINGS (sizeof (subtype_mappings) / sizeof (subtype_mappings[0]))
 
@@ -466,11 +514,21 @@ m32c_prepare_relax_scan (fragS *fragP, offsetT *aim, relax_substateT this_state)
 }
 
 static int
-insn_to_subtype (int insn)
+insn_to_subtype (int inum, const CGEN_INSN *insn)
 {
   unsigned int i;
+
+  if (insn
+      && (strncmp (insn->base->mnemonic, "adjnz", 5) == 0
+         || strncmp (insn->base->mnemonic, "sbjnz", 5) == 0))
+    {
+      i = 23 + insn->base->bitsize/8 - 3;
+      /*printf("mapping %d used for %s\n", i, insn->base->mnemonic);*/
+      return i;
+    }
+
   for (i=0; i<NUM_MAPPINGS; i++)
-    if (insn == subtype_mappings[i].insn)
+    if (inum == subtype_mappings[i].insn)
       {
        /*printf("mapping %d used\n", i);*/
        return i;
@@ -495,14 +553,14 @@ md_estimate_size_before_relax (fragS * fragP, segT segment ATTRIBUTE_UNUSED)
   int where = fragP->fr_opcode - fragP->fr_literal;
 
   if (fragP->fr_subtype == 1)
-    fragP->fr_subtype = insn_to_subtype (fragP->fr_cgen.insn->base->num);
+    fragP->fr_subtype = insn_to_subtype (fragP->fr_cgen.insn->base->num, fragP->fr_cgen.insn);
 
   if (S_GET_SEGMENT (fragP->fr_symbol) != segment)
     {
       int new_insn;
 
       new_insn = subtype_mappings[fragP->fr_subtype].insn_for_extern;
-      fragP->fr_subtype = insn_to_subtype (new_insn);
+      fragP->fr_subtype = insn_to_subtype (new_insn, 0);
     }
 
   if (fragP->fr_cgen.insn->base
@@ -520,7 +578,7 @@ md_estimate_size_before_relax (fragS * fragP, segT segment ATTRIBUTE_UNUSED)
     }
 
   return subtype_mappings[fragP->fr_subtype].bytes - (fragP->fr_fix - where);
-} 
+}
 
 /* *fragP has been relaxed to its final size, and now needs to have
    the bytes inside it modified to conform to the new size.
@@ -549,12 +607,12 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 {
   int addend;
   int operand;
-  int new_insn;
   int where = fragP->fr_opcode - fragP->fr_literal;
+  int rl_where = fragP->fr_opcode - fragP->fr_literal;
   unsigned char *op = (unsigned char *)fragP->fr_opcode;
+  int rl_addend = 0;
 
   addend = target_address_for (fragP) - (fragP->fr_address + where);
-  new_insn = subtype_mappings[fragP->fr_subtype].insn;
 
   fragP->fr_fix = where + subtype_mappings[fragP->fr_subtype].bytes;
 
@@ -563,6 +621,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
     case M32C_INSN_JCND16_5:
       op[1] = addend - 1;
       operand = M32C_OPERAND_LAB_8_8;
+      rl_addend = 0x21;
       break;
 
     case -M32C_MACRO_JCND16_5_W:
@@ -573,7 +632,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[4] = (addend - 3) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
       where += 2;
-      new_insn = M32C_INSN_JMP16_W;
+      rl_addend = 0x51;
       break;
 
     case -M32C_MACRO_JCND16_5_A:
@@ -582,13 +641,14 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[2] = 0xfc;
       operand = M32C_OPERAND_LAB_8_24;
       where += 2;
-      new_insn = M32C_INSN_JMP16_A;
+      rl_addend = 0x61;
       break;
 
 
     case M32C_INSN_JCND16:
       op[2] = addend - 2;
       operand = M32C_OPERAND_LAB_16_8;
+      rl_addend = 0x31;
       break;
 
     case -M32C_MACRO_JCND16_W:
@@ -599,7 +659,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[5] = (addend - 4) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
       where += 3;
-      new_insn = M32C_INSN_JMP16_W;
+      rl_addend = 0x61;
       break;
 
     case -M32C_MACRO_JCND16_A:
@@ -608,18 +668,20 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[3] = 0xfc;
       operand = M32C_OPERAND_LAB_8_24;
       where += 3;
-      new_insn = M32C_INSN_JMP16_A;
+      rl_addend = 0x71;
       break;
 
     case M32C_INSN_JMP16_S:
       op[0] = 0x60 | ((addend-2) & 0x07);
       operand = M32C_OPERAND_LAB_5_3;
+      rl_addend = 0x10;
       break;
 
     case M32C_INSN_JMP16_B:
       op[0] = 0xfe;
       op[1] = addend - 1;
       operand = M32C_OPERAND_LAB_8_8;
+      rl_addend = 0x21;
       break;
 
     case M32C_INSN_JMP16_W:
@@ -627,6 +689,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[1] = addend - 1;
       op[2] = (addend - 1) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
+      rl_addend = 0x31;
       break;
 
     case M32C_INSN_JMP16_A:
@@ -635,11 +698,13 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[2] = 0;
       op[3] = 0;
       operand = M32C_OPERAND_LAB_8_24;
+      rl_addend = 0x41;
       break;
 
     case M32C_INSN_JCND32:
       op[1] = addend - 1;
       operand = M32C_OPERAND_LAB_8_8;
+      rl_addend = 0x21;
       break;
 
     case -M32C_MACRO_JCND32_W:
@@ -650,7 +715,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[4] = (addend - 3) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
       where += 2;
-      new_insn = M32C_INSN_JMP32_W;
+      rl_addend = 0x51;
       break;
 
     case -M32C_MACRO_JCND32_A:
@@ -659,21 +724,21 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[2] = 0xcc;
       operand = M32C_OPERAND_LAB_8_24;
       where += 2;
-      new_insn = M32C_INSN_JMP32_A;
+      rl_addend = 0x61;
       break;
 
-
-
     case M32C_INSN_JMP32_S:
       addend = ((addend-2) & 0x07);
       op[0] = 0x4a | (addend & 0x01) | ((addend << 3) & 0x30);
       operand = M32C_OPERAND_LAB32_JMP_S;
+      rl_addend = 0x10;
       break;
 
     case M32C_INSN_JMP32_B:
       op[0] = 0xbb;
       op[1] = addend - 1;
       operand = M32C_OPERAND_LAB_8_8;
+      rl_addend = 0x21;
       break;
 
     case M32C_INSN_JMP32_W:
@@ -681,6 +746,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[1] = addend - 1;
       op[2] = (addend - 1) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
+      rl_addend = 0x31;
       break;
 
     case M32C_INSN_JMP32_A:
@@ -689,6 +755,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[2] = 0;
       op[3] = 0;
       operand = M32C_OPERAND_LAB_8_24;
+      rl_addend = 0x41;
       break;
 
 
@@ -697,6 +764,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[1] = addend - 1;
       op[2] = (addend - 1) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
+      rl_addend = 0x31;
       break;
 
     case M32C_INSN_JSR16_A:
@@ -705,6 +773,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[2] = 0;
       op[3] = 0;
       operand = M32C_OPERAND_LAB_8_24;
+      rl_addend = 0x41;
       break;
 
     case M32C_INSN_JSR32_W:
@@ -712,6 +781,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[1] = addend - 1;
       op[2] = (addend - 1) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
+      rl_addend = 0x31;
       break;
 
     case M32C_INSN_JSR32_A:
@@ -720,9 +790,29 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       op[2] = 0;
       op[3] = 0;
       operand = M32C_OPERAND_LAB_8_24;
+      rl_addend = 0x41;
       break;
 
-
+    case -M32C_MACRO_ADJNZ_2:
+      rl_addend = 0x31;
+      op[2] = addend - 2;
+      operand = M32C_OPERAND_LAB_16_8;
+      break;
+    case -M32C_MACRO_ADJNZ_3:
+      rl_addend = 0x41;
+      op[3] = addend - 2;
+      operand = M32C_OPERAND_LAB_24_8;
+      break;
+    case -M32C_MACRO_ADJNZ_4:
+      rl_addend = 0x51;
+      op[4] = addend - 2;
+      operand = M32C_OPERAND_LAB_32_8;
+      break;
+    case -M32C_MACRO_ADJNZ_5:
+      rl_addend = 0x61;
+      op[5] = addend - 2;
+      operand = M32C_OPERAND_LAB_40_8;
+      break;
 
     default:
       printf("\nHey!  Need more opcode converters! missing: %d %s\n\n",
@@ -731,10 +821,23 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
       abort();
     }
 
+  if (m32c_relax)
+    {
+      if (operand != M32C_OPERAND_LAB_8_24)
+       fragP->fr_offset = (fragP->fr_address + where);
+
+      fix_new (fragP,
+              rl_where,
+              0, abs_section_sym, rl_addend, 0,
+              BFD_RELOC_M32C_RL_JUMP);
+    }
+
   if (S_GET_SEGMENT (fragP->fr_symbol) != sec
-      || operand == M32C_OPERAND_LAB_8_24)
+      || operand == M32C_OPERAND_LAB_8_24
+      || (m32c_relax && (operand != M32C_OPERAND_LAB_5_3
+                        && operand != M32C_OPERAND_LAB32_JMP_S)))
     {
-      assert (fragP->fr_cgen.insn != 0);
+      gas_assert (fragP->fr_cgen.insn != 0);
       gas_cgen_record_fixup (fragP,
                             where,
                             fragP->fr_cgen.insn,
@@ -742,7 +845,8 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
                             cgen_operand_lookup_by_num (gas_cgen_cpu_desc,
                                                         operand),
                             fragP->fr_cgen.opinfo,
-                            fragP->fr_symbol, fragP->fr_offset);
+                            fragP->fr_symbol,
+                            fragP->fr_offset);
     }
 }
 \f
@@ -786,11 +890,15 @@ md_cgen_lookup_reloc (const CGEN_INSN *    insn ATTRIBUTE_UNUSED,
   } op_reloc_table[] = {
 
     /* PC-REL relocs for 8-bit fields.  */
+    { M32C_OPERAND_LAB_8_8,    BFD_RELOC_8_PCREL, 1 },
     { M32C_OPERAND_LAB_16_8,   BFD_RELOC_8_PCREL, 2 },
     { M32C_OPERAND_LAB_24_8,   BFD_RELOC_8_PCREL, 3 },
     { M32C_OPERAND_LAB_32_8,   BFD_RELOC_8_PCREL, 4 },
     { M32C_OPERAND_LAB_40_8,   BFD_RELOC_8_PCREL, 5 },
 
+    /* PC-REL relocs for 16-bit fields.  */
+    { M32C_OPERAND_LAB_8_16,   BFD_RELOC_16_PCREL, 1 },
+
     /* Absolute relocs for 8-bit fields.  */
     { M32C_OPERAND_IMM_8_QI,   BFD_RELOC_8, 1 },
     { M32C_OPERAND_IMM_16_QI,  BFD_RELOC_8, 2 },
@@ -890,6 +998,68 @@ md_cgen_lookup_reloc (const CGEN_INSN *    insn ATTRIBUTE_UNUSED,
   return BFD_RELOC_NONE;
 }
 
+void
+m32c_cons_fix_new (fragS *     frag,
+                  int          where,
+                  int          size,
+                  expressionS *exp,
+                  bfd_reloc_code_real_type type)
+{
+  switch (size)
+    {
+    case 1:
+      type = BFD_RELOC_8;
+      break;
+    case 2:
+      type = BFD_RELOC_16;
+      break;
+    case 3:
+      type = BFD_RELOC_24;
+      break;
+    case 4:
+    default:
+      type = BFD_RELOC_32;
+      break;
+    case 8:
+      type = BFD_RELOC_64;
+      break;
+    }
+
+  fix_new_exp (frag, where, (int) size, exp, 0, type);
+}
+
+void
+m32c_apply_fix (struct fix *f, valueT *t, segT s)
+{
+  if (f->fx_r_type == BFD_RELOC_M32C_RL_JUMP
+      || f->fx_r_type == BFD_RELOC_M32C_RL_1ADDR
+      || f->fx_r_type == BFD_RELOC_M32C_RL_2ADDR)
+    return;
+  gas_cgen_md_apply_fix (f, t, s);
+}
+
+arelent *
+tc_gen_reloc (asection *sec, fixS *fx)
+{
+  if (fx->fx_r_type == BFD_RELOC_M32C_RL_JUMP
+      || fx->fx_r_type == BFD_RELOC_M32C_RL_1ADDR
+      || fx->fx_r_type == BFD_RELOC_M32C_RL_2ADDR)
+    {
+      arelent * reloc;
+
+      reloc = XNEW (arelent);
+
+      reloc->sym_ptr_ptr = XNEW (asymbol *);
+      *reloc->sym_ptr_ptr = symbol_get_bfdsym (fx->fx_addsy);
+      reloc->address = fx->fx_frag->fr_address + fx->fx_where;
+      reloc->howto = bfd_reloc_type_lookup (stdoutput, fx->fx_r_type);
+      reloc->addend = fx->fx_offset;
+      return reloc;
+
+    }
+  return gas_cgen_tc_gen_reloc (sec, fx);
+}
+
 /* 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.  */
@@ -914,12 +1084,40 @@ m32c_force_relocation (fixS * fixp)
        case M32C_OPERAND_DSP_24_U16:
        case M32C_OPERAND_IMM_24_HI:
          return 1;
+
+        /* If we're doing linker relaxing, we need to keep all the
+          pc-relative jumps in case we need to fix them due to
+          deleted bytes between the jump and its destination.  */
+       case M32C_OPERAND_LAB_8_8:
+       case M32C_OPERAND_LAB_8_16:
+       case M32C_OPERAND_LAB_8_24:
+       case M32C_OPERAND_LAB_16_8:
+       case M32C_OPERAND_LAB_24_8:
+       case M32C_OPERAND_LAB_32_8:
+       case M32C_OPERAND_LAB_40_8:
+         if (m32c_relax)
+           return 1;
+       default:
+         break;
        }
     }
   else
     {
-      if (fixp->fx_r_type == BFD_RELOC_16)
-       return 1;
+      switch (fixp->fx_r_type)
+       {
+       case BFD_RELOC_16:
+         return 1;
+
+       case BFD_RELOC_M32C_RL_JUMP:
+       case BFD_RELOC_M32C_RL_1ADDR:
+       case BFD_RELOC_M32C_RL_2ADDR:
+       case BFD_RELOC_8_PCREL:
+       case BFD_RELOC_16_PCREL:
+         if (m32c_relax)
+           return 1;
+       default:
+         break;
+       }
     }
 
   return generic_force_reloc (fixp);
@@ -937,53 +1135,10 @@ md_number_to_chars (char * buf, valueT val, int n)
    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 *
+const char *
 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
@@ -1065,25 +1220,23 @@ m32c_fix_adjustable (fixS * fixP)
   if (S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE)
     return 0;
 
+  if (m32c_relax)
+    return 0;
+
   return 1;
 }
 
 /* Worker function for m32c_is_colon_insn().  */
-static char restore_colon PARAMS ((int));
-
-static char
-restore_colon (int advance_i_l_p_by)
+static int
+restore_colon (char *next_i_l_p, char *nul_char)
 {
-  char c;
-
   /* Restore the colon, and advance input_line_pointer to
      the end of the new symbol.  */
-  * input_line_pointer = ':';
-  input_line_pointer += advance_i_l_p_by;
-  c = * input_line_pointer;
-  * input_line_pointer = 0;
-
-  return c;
+  *input_line_pointer = *nul_char;
+  input_line_pointer = next_i_l_p;
+  *nul_char = *next_i_l_p;
+  *next_i_l_p = 0;
+  return 1;
 }
 
 /* Determines if the symbol starting at START and ending in
@@ -1091,28 +1244,31 @@ restore_colon (int advance_i_l_p_by)
    (but which has now been replaced bu a NUL) is in fact an
    :Z, :S, :Q, or :G suffix.
    If it is, then it restores the colon, advances INPUT_LINE_POINTER
-   to the real end of the instruction/symbol, and returns the character
-   that really terminated the symbol.  Otherwise it returns 0.  */
-char
-m32c_is_colon_insn (char *start ATTRIBUTE_UNUSED)
+   to the real end of the instruction/symbol, saves the char there to
+   NUL_CHAR and pokes a NUL, and returns 1.  Otherwise it returns 0.  */
+int
+m32c_is_colon_insn (char *start ATTRIBUTE_UNUSED, char *nul_char)
 {
   char * i_l_p = input_line_pointer;
 
+  if (*nul_char == '"')
+    ++i_l_p;
+
   /* Check to see if the text following the colon is 'G' */
   if (TOLOWER (i_l_p[1]) == 'g' && (i_l_p[2] == ' ' || i_l_p[2] == '\t'))
-    return restore_colon (2);
+    return restore_colon (i_l_p + 2, nul_char);
 
   /* Check to see if the text following the colon is 'Q' */
   if (TOLOWER (i_l_p[1]) == 'q' && (i_l_p[2] == ' ' || i_l_p[2] == '\t'))
-    return restore_colon (2);
+    return restore_colon (i_l_p + 2, nul_char);
 
   /* Check to see if the text following the colon is 'S' */
   if (TOLOWER (i_l_p[1]) == 's' && (i_l_p[2] == ' ' || i_l_p[2] == '\t'))
-    return restore_colon (2);
+    return restore_colon (i_l_p + 2, nul_char);
 
   /* Check to see if the text following the colon is 'Z' */
   if (TOLOWER (i_l_p[1]) == 'z' && (i_l_p[2] == ' ' || i_l_p[2] == '\t'))
-    return restore_colon (2);
+    return restore_colon (i_l_p + 2, nul_char);
 
   return 0;
 }