This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[m32c] initial insn relaxation changes


This adds a generic insn relaxer for the m32c, which is heavy with
addressing modes and really wants link-time relaxation.  Note that
this only covers the "generic" addressing modes for operands, but
handles all the various jumps (because you have to get those right
anyway if you're moving code around).  We already had assemble-time
relaxations; those would grow insns if needed.  These shrink insns
when possible.  Link relaxation is enabled by default in the linker
(it's needed for call thunks anyway), but doesn't do anything unless
the assembler provides the special relocs; linker relaxation is *not*
enabled by default in the assembler.

[include/elf]
	* m32c.h: Add relax relocs.

[cpu]
	* m32c.cpu (RL_TYPE): New attribute, with macros.
	(Lab-8-24): Add RELAX.
	(unary-insn-defn-g, binary-arith-imm-dst-defn,
	binary-arith-imm4-dst-defn): Add 1ADDR attribute.
	(binary-arith-src-dst-defn): Add 2ADDR attribute.
	(jcnd16-5, jcnd16, jcnd32, jmp16.s, jmp16.b, jmp16.w, jmp16.a,
	jmp32.s, jmp32.b, jmp32.w, jmp32.a, jsr16.w, jsr16.a): Add JUMP
	attribute.
	(jsri16, jsri32): Add 1ADDR attribute.
	(jsr32.w, jsr32.a): Add JUMP attribute.
	
[opcodes]
	* m32c-desc.c: Regenerate with linker relaxation attributes.
	* m32c-desc.h: Likewise.
	* m32c-dis.c: Likewise.
	* m32c-opc.c: Likewise.

[gas]
	* config/tc-m32c.h (md_apply_fix): Define to m32c_apply_fix.
	(tc_gen_reloc): Don't define.
	* config/tc-m32c.c (rl_for, relaxable): New convenience macros.
	(OPTION_LINKRELAX): New.
	(md_longopts): Add it.
	(m32c_relax): New.
	(md_parse_options): Set it.
	(md_assemble): Emit relaxation relocs as needed.
	(md_convert_frag): Emit relaxation relocs as needed.
	(md_cgen_lookup_reloc): Add LAB_8_8 and LAB_8_16.
	(m32c_apply_fix): New.
	(tc_gen_reloc): New.
	(m32c_force_relocation): Force out jump relocs when relaxing.
	(m32c_fix_adjustable): Return false if relaxing.

[bfd]
	* elf32-m32c.c (m32c_elf_howto_table): Add relaxation relocs.
	(m32c_elf_relocate_section): Don't relocate them.
	(compare_reloc): New.
	(relax_reloc): Remove.
	(m32c_offset_for_reloc): New.
	(m16c_addr_encodings): New.
	(m16c_jmpaddr_encodings): New.
	(m32c_addr_encodings): New.
	(m32c_elf_relax_section): Relax jumps and address displacements.
	(m32c_elf_relax_delete_bytes): Adjust for internal syms.  Fix up
	short jumps.

	* reloc.c: Add m32c relax relocs.
	* libbfd.h: Regenerate.
	
Index: include/elf/m32c.h
===================================================================
RCS file: /cvs/src/src/include/elf/m32c.h,v
retrieving revision 1.2
diff -p -U3 -r1.2  include/elf/m32c.h
--- include/elf/m32c.h	26 Jul 2005 03:21:52 -0000	1.2
+++ include/elf/m32c.h	24 Feb 2006 22:02:24 -0000
@@ -40,6 +40,14 @@ along with this program; if not, write t
     /* Bits 16..31 of an address, for LDE's A1A0 etc. */
      RELOC_NUMBER (R_M32C_HI16, 9)
 
+    /* These are relocs we need when relaxing.  */
+    /* Marks various jump opcodes.  */
+     RELOC_NUMBER (R_M32C_RL_JUMP, 10)
+    /* Marks standard one-address form.  */
+     RELOC_NUMBER (R_M32C_RL_1ADDR, 11)
+    /* Marks standard two-address form.  */
+     RELOC_NUMBER (R_M32C_RL_2ADDR, 12)
+
      END_RELOC_NUMBERS (R_M32C_max)
 
 #define EF_M32C_CPU_M16C	0x00000075      /* default */
Index: cpu/m32c.cpu
===================================================================
RCS file: /cvs/src/src/cpu/m32c.cpu,v
retrieving revision 1.9
diff -p -U3 -r1.9  cpu/m32c.cpu
--- cpu/m32c.cpu	6 Jan 2006 23:25:53 -0000	1.9
+++ cpu/m32c.cpu	24 Feb 2006 22:06:09 -0000
@@ -1,6 +1,6 @@
 ; Renesas M32C CPU description.  -*- Scheme -*-
 ;
-; Copyright 2005 Free Software Foundation, Inc.
+; Copyright 2005, 2006 Free Software Foundation, Inc.
 ;
 ; Contributed by Red Hat Inc; developed under contract from Renesas.
 ;
@@ -139,6 +139,13 @@
 	)
 )
 
+(define-attr
+  (type enum)
+  (name RL_TYPE)
+  (values NONE JUMP 1ADDR 2ADDR)
+  (default NONE)
+  )
+
 ; Macros to simplify MACH attribute specification.
 
 (define-pmacro all-isas () (ISA m16c,m32c))
@@ -150,6 +157,11 @@
 
 (define-pmacro (machine size)
   (MACH (.sym m size c)) (ISA (.sym m size c)))
+
+(define-pmacro RL_JUMP  (RL_TYPE JUMP))
+(define-pmacro RL_1ADDR (RL_TYPE 1ADDR))
+(define-pmacro RL_2ADDR (RL_TYPE 2ADDR))
+
 
 ;=============================================================
 ; Fields
@@ -2097,7 +2109,7 @@
 
 (dnop Lab-8-8     "8 bit label"  (all-isas RELAX) h-iaddr f-lab-8-8)
 (dnop Lab-8-16    "16 bit label" (all-isas RELAX) h-iaddr f-lab-8-16)
-(dnop Lab-8-24    "24 bit label" (all-isas) h-iaddr f-lab-8-24)
+(dnop Lab-8-24    "24 bit label" (all-isas RELAX) h-iaddr f-lab-8-24)
 (dnop Lab-16-8    "8 bit label"  (all-isas RELAX) h-iaddr f-lab-16-8)
 (dnop Lab-24-8    "8 bit label"  (all-isas) h-iaddr f-lab-24-8)
 (dnop Lab-32-8    "8 bit label"  (all-isas) h-iaddr f-lab-32-8)
@@ -5859,7 +5871,7 @@
 (define-pmacro (unary-insn-defn-g mach group mode wstr op encoding sem opg)
   (dni (.sym op mach wstr - group)
        (.str op wstr opg " dst" mach "-" group "-" mode)
-       ((machine mach))
+       ((machine mach) RL_1ADDR)
        (.str op wstr opg " ${dst" mach "-" group "-" mode "}")
        encoding
        (sem mode (.sym dst mach - group - mode))
@@ -6075,7 +6087,7 @@
 (define-pmacro (binary-arith-imm-dst-defn mach src dstgroup dmode wstr op suffix encoding sem)
   (dni (.sym op mach wstr - imm-G - dstgroup)
        (.str op wstr " " mach "-imm-G-" dstgroup "-" dmode)
-       ((machine mach))
+       ((machine mach) RL_1ADDR)
        (.str op wstr "$"suffix " #${" src "},${dst" mach "-" dstgroup "-" dmode "}")
        encoding
        (sem dmode src (.sym dst mach - dstgroup - dmode))
@@ -6185,7 +6197,7 @@
 (define-pmacro (binary-arith-imm4-dst-defn mach src dstgroup mode wstr op encoding sem)
   (dni (.sym op mach wstr - imm4-Q - dstgroup)
        (.str op wstr " " mach "-imm4-Q-" dstgroup "-" mode)
-       ((machine mach))
+       ((machine mach) RL_1ADDR)
        (.str op wstr "$Q #${" src "},${dst" mach "-" dstgroup "-" mode "}")
        encoding
        (sem mode src (.sym dst mach - dstgroup - mode))
@@ -6261,7 +6273,7 @@
 (define-pmacro (binary-arith-src-dst-defn mach srcgroup dstgroup smode dmode wstr op suffix encoding sem)
   (dni (.sym op mach wstr - srcgroup - dstgroup)
        (.str op wstr " dst" mach "-" srcgroup "-" dstgroup "-" dmode)
-       ((machine mach))
+       ((machine mach) RL_2ADDR)
        (.str op wstr "$" suffix " ${src" mach "-" srcgroup "-" smode "},${dst" mach "-" dstgroup "-" dmode "}")
        encoding
        (sem dmode (.sym src mach - srcgroup - smode) (.sym dst mach - dstgroup - dmode))
@@ -8058,7 +8070,7 @@
 
 (dni jcnd16-5
      "jCnd label"
-     (RELAXABLE (machine 16))
+     (RL_JUMP RELAXABLE (machine 16))
      "j$cond16j5 ${Lab-8-8}"
      (+ (f-0-4 #x6) (f-4-1 1) cond16j5 Lab-8-8)
      (jcnd16-sem cond16j5 Lab-8-8)
@@ -8067,7 +8079,7 @@
 
 (dni jcnd16
      "jCnd label"
-     (RELAXABLE (machine 16))
+     (RL_JUMP RELAXABLE (machine 16))
      "j$cond16j ${Lab-16-8}"
      (+ (f-0-4 #x7) (f-4-4 #xD) (f-8-4 #xC) cond16j Lab-16-8)
      (jcnd16-sem cond16j Lab-16-8)
@@ -8076,7 +8088,7 @@
 
 (dni jcnd32
      "jCnd label"
-     (RELAXABLE (machine 32))
+     (RL_JUMP RELAXABLE (machine 32))
      "j$cond32j ${Lab-8-8}"
      (+ (f-0-1 1) (f-4-3 5) cond32j Lab-8-8)
      (jcnd32-sem cond32j Lab-8-8)
@@ -8088,25 +8100,25 @@
 ;-------------------------------------------------------------
 
 ; jmp.s label3 (m16 #1)
-(dni jmp16.s "jmp.s Lab-5-3" (RELAXABLE (machine 16))
+(dni jmp16.s "jmp.s Lab-5-3" (RL_JUMP RELAXABLE (machine 16))
      ("jmp.s ${Lab-5-3}")
      (+ (f-0-4 6) (f-4-1 0) Lab-5-3)
      (sequence () (set pc Lab-5-3))
      ())
 ; jmp.b label8 (m16 #2)
-(dni jmp16.b "jmp.b Lab-8-8" (RELAXABLE (machine 16))
+(dni jmp16.b "jmp.b Lab-8-8" (RL_JUMP RELAXABLE (machine 16))
      ("jmp.b ${Lab-8-8}")
      (+ (f-0-4 #xF) (f-4-4 #xE) Lab-8-8)
      (sequence () (set pc Lab-8-8))
      ())
 ; jmp.w label16 (m16 #3)
-(dni jmp16.w "jmp.w Lab-8-16" (RELAXABLE (machine 16))
+(dni jmp16.w "jmp.w Lab-8-16" (RL_JUMP RELAXABLE (machine 16))
      ("jmp.w ${Lab-8-16}")
      (+ (f-0-4 #xF) (f-4-4 4) Lab-8-16)
      (sequence () (set pc Lab-8-16))
      ())
 ; jmp.a label24 (m16 #4)
-(dni jmp16.a "jmp.a Lab-8-24" ((machine 16))
+(dni jmp16.a "jmp.a Lab-8-24" (RL_JUMP RELAXABLE (machine 16))
      ("jmp.a ${Lab-8-24}")
      (+ (f-0-4 #xF) (f-4-4 #xC) Lab-8-24)
      (sequence () (set pc Lab-8-24))
@@ -8133,32 +8145,32 @@
 ; jmp.s label3 (m32 #1)
 (dni jmp32.s
      "jmp.s label"
-     (RELAXABLE (machine 32))
+     (RL_JUMP RELAXABLE (machine 32))
      "jmp.s ${Lab32-jmp-s}"
      (+ (f-0-2 1) (f-4-3 5) Lab32-jmp-s)
      (set pc Lab32-jmp-s)
      ()
 )
 ; jmp.b label8 (m32 #2)
-(dni jmp32.b "jmp.b Lab-8-8" (RELAXABLE (machine 32))
+(dni jmp32.b "jmp.b Lab-8-8" (RL_JUMP RELAXABLE (machine 32))
      ("jmp.b ${Lab-8-8}")
      (+ (f-0-4 #xB) (f-4-4 #xB) Lab-8-8)
      (set pc Lab-8-8)
      ())
 ; jmp.w label16 (m32 #3)
-(dni jmp32.w "jmp.w Lab-8-16" (RELAXABLE (machine 32))
+(dni jmp32.w "jmp.w Lab-8-16" (RL_JUMP RELAXABLE (machine 32))
      ("jmp.w ${Lab-8-16}")
      (+ (f-0-4 #xC) (f-4-4 #xE) Lab-8-16)
      (set pc Lab-8-16)
      ())
 ; jmp.a label24 (m32 #4)
-(dni jmp32.a "jmp.a Lab-8-24" ((machine 32))
+(dni jmp32.a "jmp.a Lab-8-24" (RL_JUMP RELAXABLE (machine 32))
      ("jmp.a ${Lab-8-24}")
      (+ (f-0-4 #xC) (f-4-4 #xC) Lab-8-24)
      (set pc Lab-8-24)
      ())
 ; jmp.s imm8 (m32 #1)
-(dni jmps32 "jmps Imm-8-QI" ((machine 32))
+(dni jmps32 "jmps Imm-8-QI" (RL_JUMP (machine 32))
      ("jmps #${Imm-8-QI}")
      (+ (f-0-4 #xD) (f-4-4 #xC) Imm-8-QI)
      (set pc Imm-8-QI)
@@ -8190,13 +8202,13 @@
 )
 
 ; jsr.w label16 (m16 #1)
-(dni jsr16.w "jsr.w Lab-8-16" (RELAXABLE (machine 16))
+(dni jsr16.w "jsr.w Lab-8-16" (RL_JUMP RELAXABLE (machine 16))
      ("jsr.w ${Lab-8-16}")
      (+ (f-0-4 #xF) (f-4-4 5) Lab-8-16)
      (jsr16-sem 3 Lab-8-16)
      ())
 ; jsr.a label24 (m16 #2)
-(dni jsr16.a "jsr.a Lab-8-24" ((machine 16))
+(dni jsr16.a "jsr.a Lab-8-24" (RL_JUMP RELAXABLE (machine 16))
      ("jsr.a ${Lab-8-24}")
      (+ (f-0-4 #xF) (f-4-4 #xD) Lab-8-24)
      (jsr16-sem 4 Lab-8-24)
@@ -8206,14 +8218,14 @@
   (begin
     (dni (.sym jsri16 mode - op16)
 	 (.str "jsri." mode " " op16)
-	 ((machine 16))
+	 (RL_1ADDR (machine 16))
 	 (.str "jsri." mode " ${" op16 "}")
 	 (+ op16-1 op16-2 op16-3 op16)
 	 (op16-sem len op16)
 	 ())
     (dni (.sym jsri32 mode - op32)
 	 (.str "jsri." mode " " op32)
-	 ((machine 32))
+	 (RL_1ADDR (machine 32))
 	 (.str "jsri." mode " ${" op32 "}")
 	 (+ op32-1 op32-2 op32-3 op32-4 op32)
 	 (op32-sem len op32)
@@ -8227,7 +8239,7 @@
 	      dst32-16-16-Unprefixed-HI (f-0-4 #xC) (f-7-1 1) (f-10-2 #x1) (f-12-4 #xF) jsr32-sem 4)
 (jsri-defn w dst16-basic-HI (f-0-4 #x7) (f-4-4 #xD) (f-8-4 #x3) jsr16-sem 
 	      dst32-basic-Unprefixed-HI (f-0-4 #xC) (f-7-1 1) (f-10-2 #x1) (f-12-4 #xF) jsr32-sem 2)
-(dni jsri32.w "jsr.w dst32-16-24-Unprefixed-HI" ((machine 32))
+(dni jsri32.w "jsr.w dst32-16-24-Unprefixed-HI" (RL_1ADDR (machine 32))
      ("jsri.w ${dst32-16-24-Unprefixed-HI}")
      (+ (f-0-4 #xC) (f-7-1 1) dst32-16-24-Unprefixed-HI (f-10-2 #x1) (f-12-4 #xF))
      (jsr32-sem 6 dst32-16-24-Unprefixed-HI)
@@ -8241,19 +8253,19 @@
 (jsri-defn a dst16-basic-SI (f-0-4 #x7) (f-4-4 #xD) (f-8-4 #x1) jsr16-sem 
 	      dst32-basic-Unprefixed-SI (f-0-4 #x9) (f-7-1 0) (f-10-2 #x0) (f-12-4 #x1) jsr32-sem 2)
 
-(dni jsri32.a "jsr.w dst32-16-24-Unprefixed-HI" ((machine 32))
+(dni jsri32.a "jsr.w dst32-16-24-Unprefixed-HI" (RL_1ADDR (machine 32))
      ("jsri.w ${dst32-16-24-Unprefixed-SI}")
      (+ (f-0-4 #x9) (f-7-1 0) dst32-16-24-Unprefixed-SI (f-10-2 #x0) (f-12-4 #x1))
      (jsr32-sem 6 dst32-16-24-Unprefixed-SI)
      ())
 ; jsr.w label16 (m32 #1)
-(dni jsr32.w "jsr.w label" (RELAXABLE (machine 32))
+(dni jsr32.w "jsr.w label" (RL_JUMP RELAXABLE (machine 32))
      ("jsr.w ${Lab-8-16}")
      (+ (f-0-4 #xC) (f-4-4 #xF) Lab-8-16)
      (jsr32-sem 3 Lab-8-16)
      ())
 ; jsr.a label16 (m32 #2)
-(dni jsr32.a "jsr.a label" ((machine 32))
+(dni jsr32.a "jsr.a label" (RL_JUMP (machine 32))
      ("jsr.a ${Lab-8-24}")
      (+ (f-0-4 #xC) (f-4-4 #xD) Lab-8-24)
      (jsr32-sem 4 Lab-8-24)
Index: gas/config/tc-m32c.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-m32c.c,v
retrieving revision 1.8
diff -p -U3 -r1.8  gas/config/tc-m32c.c
--- gas/config/tc-m32c.c	14 Dec 2005 02:27:34 -0000	1.8
+++ gas/config/tc-m32c.c	24 Feb 2006 22:02:26 -0000
@@ -55,6 +55,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 +70,13 @@ const char * md_shortopts = M32C_SHORTOP
 /* assembler options */
 #define OPTION_CPU_M16C	       (OPTION_MD_BASE)
 #define OPTION_CPU_M32C        (OPTION_MD_BASE + 1)
+#define OPTION_LINKRELAX       (OPTION_MD_BASE + 2)
 
 struct option md_longopts[] =
 {
   { "m16c",       no_argument,	      NULL, OPTION_CPU_M16C   },
   { "m32c",       no_argument,	      NULL, OPTION_CPU_M32C   },
+  { "relax",      no_argument,	      NULL, OPTION_LINKRELAX   },
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
@@ -84,6 +89,7 @@ size_t md_longopts_size = sizeof (md_lon
 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;
@@ -118,6 +124,10 @@ md_parse_option (int c, char * arg ATTRI
       set_isa (ISA_M32C);
       break;
 
+    case OPTION_LINKRELAX:
+      m32c_relax = 1;
+      break;
+
     default:
       return 0;
     }
@@ -153,7 +163,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,
@@ -320,6 +330,8 @@ md_assemble (char * str)
   static int last_insn_had_delay_slot = 0;
   m32c_insn insn;
   char *    errmsg;
+  finished_insnS results;
+  int rl_type;
 
   if (m32c_mach == bfd_mach_m32c && m32c_indirect_operand (str))
     return;
@@ -336,13 +348,46 @@ md_assemble (char * str)
       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);
   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 '#'.
@@ -551,18 +596,26 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
   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 op_base = 0;
+  int op_op = 0;
+  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;
 
+  op_base = 0;
+
   switch (subtype_mappings[fragP->fr_subtype].insn)
     {
     case M32C_INSN_JCND16_5:
       op[1] = addend - 1;
       operand = M32C_OPERAND_LAB_8_8;
+      op_op = 1;
+      rl_addend = 0x21;
       break;
 
     case -M32C_MACRO_JCND16_5_W:
@@ -574,6 +627,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       operand = M32C_OPERAND_LAB_8_16;
       where += 2;
       new_insn = M32C_INSN_JMP16_W;
+      op_base = 2;
+      op_op = 3;
+      rl_addend = 0x51;
       break;
 
     case -M32C_MACRO_JCND16_5_A:
@@ -583,12 +639,18 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       operand = M32C_OPERAND_LAB_8_24;
       where += 2;
       new_insn = M32C_INSN_JMP16_A;
+      op_base = 2;
+      op_op = 3;
+      rl_addend = 0x61;
       break;
 
 
     case M32C_INSN_JCND16:
       op[2] = addend - 2;
       operand = M32C_OPERAND_LAB_16_8;
+      op_base = 0;
+      op_op = 2;
+      rl_addend = 0x31;
       break;
 
     case -M32C_MACRO_JCND16_W:
@@ -600,6 +662,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       operand = M32C_OPERAND_LAB_8_16;
       where += 3;
       new_insn = M32C_INSN_JMP16_W;
+      op_base = 3;
+      op_op = 4;
+      rl_addend = 0x61;
       break;
 
     case -M32C_MACRO_JCND16_A:
@@ -609,17 +674,26 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       operand = M32C_OPERAND_LAB_8_24;
       where += 3;
       new_insn = M32C_INSN_JMP16_A;
+      op_base = 3;
+      op_op = 4;
+      rl_addend = 0x71;
       break;
 
     case M32C_INSN_JMP16_S:
       op[0] = 0x60 | ((addend-2) & 0x07);
       operand = M32C_OPERAND_LAB_5_3;
+      op_base = 0;
+      op_op = 0;
+      rl_addend = 0x10;
       break;
 
     case M32C_INSN_JMP16_B:
       op[0] = 0xfe;
       op[1] = addend - 1;
       operand = M32C_OPERAND_LAB_8_8;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x21;
       break;
 
     case M32C_INSN_JMP16_W:
@@ -627,6 +701,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       op[1] = addend - 1;
       op[2] = (addend - 1) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x31;
       break;
 
     case M32C_INSN_JMP16_A:
@@ -635,11 +712,17 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       op[2] = 0;
       op[3] = 0;
       operand = M32C_OPERAND_LAB_8_24;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x41;
       break;
 
     case M32C_INSN_JCND32:
       op[1] = addend - 1;
       operand = M32C_OPERAND_LAB_8_8;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x21;
       break;
 
     case -M32C_MACRO_JCND32_W:
@@ -651,6 +734,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       operand = M32C_OPERAND_LAB_8_16;
       where += 2;
       new_insn = M32C_INSN_JMP32_W;
+      op_base = 2;
+      op_op = 3;
+      rl_addend = 0x51;
       break;
 
     case -M32C_MACRO_JCND32_A:
@@ -660,6 +746,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       operand = M32C_OPERAND_LAB_8_24;
       where += 2;
       new_insn = M32C_INSN_JMP32_A;
+      op_base = 2;
+      op_op = 3;
+      rl_addend = 0x61;
       break;
 
 
@@ -668,12 +757,18 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       addend = ((addend-2) & 0x07);
       op[0] = 0x4a | (addend & 0x01) | ((addend << 3) & 0x30);
       operand = M32C_OPERAND_LAB32_JMP_S;
+      op_base = 0;
+      op_op = 0;
+      rl_addend = 0x10;
       break;
 
     case M32C_INSN_JMP32_B:
       op[0] = 0xbb;
       op[1] = addend - 1;
       operand = M32C_OPERAND_LAB_8_8;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x21;
       break;
 
     case M32C_INSN_JMP32_W:
@@ -681,6 +776,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       op[1] = addend - 1;
       op[2] = (addend - 1) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x31;
       break;
 
     case M32C_INSN_JMP32_A:
@@ -689,6 +787,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       op[2] = 0;
       op[3] = 0;
       operand = M32C_OPERAND_LAB_8_24;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x41;
       break;
 
 
@@ -697,6 +798,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       op[1] = addend - 1;
       op[2] = (addend - 1) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x31;
       break;
 
     case M32C_INSN_JSR16_A:
@@ -705,6 +809,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       op[2] = 0;
       op[3] = 0;
       operand = M32C_OPERAND_LAB_8_24;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x41;
       break;
 
     case M32C_INSN_JSR32_W:
@@ -712,6 +819,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       op[1] = addend - 1;
       op[2] = (addend - 1) >> 8;
       operand = M32C_OPERAND_LAB_8_16;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x31;
       break;
 
     case M32C_INSN_JSR32_A:
@@ -720,6 +830,9 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       op[2] = 0;
       op[3] = 0;
       operand = M32C_OPERAND_LAB_8_24;
+      op_base = 0;
+      op_op = 1;
+      rl_addend = 0x41;
       break;
 
 
@@ -731,8 +844,21 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_
       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_cgen_record_fixup (fragP,
@@ -786,11 +912,15 @@ md_cgen_lookup_reloc (const CGEN_INSN * 
   } 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 +1020,38 @@ md_cgen_lookup_reloc (const CGEN_INSN * 
   return BFD_RELOC_NONE;
 }
 
+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 = xmalloc (sizeof (* reloc));
+ 
+      reloc->sym_ptr_ptr = xmalloc (sizeof (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 +1076,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);
@@ -1065,6 +1255,9 @@ m32c_fix_adjustable (fixS * fixP)
   if (S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE)
     return 0;
 
+  if (m32c_relax)
+    return 0;
+
   return 1;
 }
 
Index: gas/config/tc-m32c.h
===================================================================
RCS file: /cvs/src/src/gas/config/tc-m32c.h,v
retrieving revision 1.2
diff -p -U3 -r1.2  gas/config/tc-m32c.h
--- gas/config/tc-m32c.h	11 Aug 2005 01:25:27 -0000	1.2
+++ gas/config/tc-m32c.h	24 Feb 2006 22:02:26 -0000
@@ -47,7 +47,8 @@ long md_pcrel_from_section PARAMS ((stru
 /* We don't need to handle .word strangely.  */
 #define WORKING_DOT_WORD
 
-#define md_apply_fix gas_cgen_md_apply_fix
+#define md_apply_fix m32c_apply_fix
+extern void m32c_apply_fix PARAMS ((struct fix *, valueT *, segT));
 
 #define tc_fix_adjustable(fixP) m32c_fix_adjustable (fixP)
 extern bfd_boolean m32c_fix_adjustable PARAMS ((struct fix *));
@@ -66,8 +67,6 @@ extern void m32c_prepare_relax_scan PARA
 /* Values passed to md_apply_fix don't include the symbol value.  */
 #define MD_APPLY_SYM_VALUE(FIX) 0
 
-#define tc_gen_reloc gas_cgen_tc_gen_reloc
-
 /* Call md_pcrel_from_section(), not md_pcrel_from().  */
 #define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
 extern long md_pcrel_from_section PARAMS ((struct fix *, segT));
Index: bfd/bfd-in2.h
===================================================================
RCS file: /cvs/src/src/bfd/bfd-in2.h,v
retrieving revision 1.380
diff -p -U3 -r1.380  bfd/bfd-in2.h
--- bfd/bfd-in2.h	17 Feb 2006 14:36:21 -0000	1.380
+++ bfd/bfd-in2.h	24 Feb 2006 22:02:27 -0000
@@ -8,8 +8,7 @@
 /* Main header file for the bfd library -- portable access to object files.
 
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
-   Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
    Contributed by Cygnus Support.
 
@@ -3217,6 +3216,9 @@ of the container.  */
 
 /* Renesas M16C/M32C Relocations.  */
   BFD_RELOC_M32C_HI8,
+  BFD_RELOC_M32C_RL_JUMP,
+  BFD_RELOC_M32C_RL_1ADDR,
+  BFD_RELOC_M32C_RL_2ADDR,
 
 /* Renesas M32R (formerly Mitsubishi M32R) relocs.
 This is a 24 bit absolute address.  */
Index: bfd/elf32-m32c.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-m32c.c,v
retrieving revision 1.2
diff -p -U3 -r1.2  bfd/elf32-m32c.c
--- bfd/elf32-m32c.c	26 Jul 2005 03:21:50 -0000	1.2
+++ bfd/elf32-m32c.c	24 Feb 2006 22:02:27 -0000
@@ -1,5 +1,5 @@
 /* M16C/M32C specific support for 32-bit ELF.
-   Copyright (C) 2005
+   Copyright (C) 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -74,7 +74,7 @@ static reloc_howto_type m32c_elf_howto_t
 	 "R_M32C_16",		/* name */
 	 FALSE,			/* partial_inplace */
 	 0,			/* src_mask */
-	 0x0000ffff,		/* dst_mask */
+	 0xffff,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
 
   HOWTO (R_M32C_24,		/* type */
@@ -88,7 +88,7 @@ static reloc_howto_type m32c_elf_howto_t
 	 "R_M32C_24",		/* name */
 	 FALSE,			/* partial_inplace */
 	 0,			/* src_mask */
-	 0x00ffffff,            /* dst_mask */
+	 0xffffff,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
 
   HOWTO (R_M32C_32,		/* type */
@@ -116,7 +116,7 @@ static reloc_howto_type m32c_elf_howto_t
 	 "R_M32C_8_PCREL",	/* name */
 	 FALSE,			/* partial_inplace */
 	 0,     		/* src_mask */
-	 0x000000ff,   		/* dst_mask */
+	 0xff,   		/* dst_mask */
 	 TRUE), 		/* pcrel_offset */
 
   HOWTO (R_M32C_16_PCREL,	/* type */
@@ -130,7 +130,7 @@ static reloc_howto_type m32c_elf_howto_t
 	 "R_M32C_16_PCREL",	/* name */
 	 FALSE,			/* partial_inplace */
 	 0,     		/* src_mask */
-	 0,             	/* dst_mask */
+	 0xffff,             	/* dst_mask */
 	 TRUE), 		/* pcrel_offset */
 
   HOWTO (R_M32C_8,		/* type */
@@ -144,7 +144,7 @@ static reloc_howto_type m32c_elf_howto_t
 	 "R_M32C_8",		/* name */
 	 FALSE,			/* partial_inplace */
 	 0,     		/* src_mask */
-	 0x000000ff,		/* dst_mask */
+	 0xff,			/* dst_mask */
 	 FALSE), 		/* pcrel_offset */
 
   HOWTO (R_M32C_LO16,		/* type */
@@ -158,7 +158,7 @@ static reloc_howto_type m32c_elf_howto_t
 	 "R_M32C_LO16",		/* name */
 	 FALSE,			/* partial_inplace */
 	 0,     		/* src_mask */
-	 0x0000ffff,		/* dst_mask */
+	 0xffff,		/* dst_mask */
 	 FALSE), 		/* pcrel_offset */
 
   HOWTO (R_M32C_HI8,		/* type */
@@ -172,7 +172,7 @@ static reloc_howto_type m32c_elf_howto_t
 	 "R_M32C_HI8",		/* name */
 	 FALSE,			/* partial_inplace */
 	 0,     		/* src_mask */
-	 0x000000ff,		/* dst_mask */
+	 0xff,			/* dst_mask */
 	 FALSE), 		/* pcrel_offset */
 
   HOWTO (R_M32C_HI16,		/* type */
@@ -186,8 +186,51 @@ static reloc_howto_type m32c_elf_howto_t
 	 "R_M32C_HI16",		/* name */
 	 FALSE,			/* partial_inplace */
 	 0,     		/* src_mask */
-	 0x0000ffff,		/* dst_mask */
+	 0xffff,		/* dst_mask */
 	 FALSE), 		/* pcrel_offset */
+
+  HOWTO (R_M32C_RL_JUMP,	/* type */
+	 0,			/* rightshift */
+	 0,			/* size (0 = byte, 1 = short, 2 = long) */
+	 0,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_M32C_RL_JUMP",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,     		/* src_mask */
+	 0,   			/* dst_mask */
+	 FALSE), 		/* pcrel_offset */
+
+  HOWTO (R_M32C_RL_1ADDR,	/* type */
+	 0,			/* rightshift */
+	 0,			/* size (0 = byte, 1 = short, 2 = long) */
+	 0,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_M32C_RL_1ADDR",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,     		/* src_mask */
+	 0,   			/* dst_mask */
+	 FALSE), 		/* pcrel_offset */
+
+  HOWTO (R_M32C_RL_2ADDR,	/* type */
+	 0,			/* rightshift */
+	 0,			/* size (0 = byte, 1 = short, 2 = long) */
+	 0,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_M32C_RL_2ADDR",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,     		/* src_mask */
+	 0,   			/* dst_mask */
+	 FALSE), 		/* pcrel_offset */
+
 };
 
 /* Map BFD reloc types to M32C ELF reloc types.  */
@@ -209,7 +252,10 @@ static const struct m32c_reloc_map m32c_
   { BFD_RELOC_8,		R_M32C_8 },
   { BFD_RELOC_LO16,		R_M32C_LO16 },
   { BFD_RELOC_HI16,		R_M32C_HI16 },
-  { BFD_RELOC_M32C_HI8,		R_M32C_HI8 }
+  { BFD_RELOC_M32C_HI8,		R_M32C_HI8 },
+  { BFD_RELOC_M32C_RL_JUMP,	R_M32C_RL_JUMP },
+  { BFD_RELOC_M32C_RL_1ADDR,	R_M32C_RL_1ADDR },
+  { BFD_RELOC_M32C_RL_2ADDR,	R_M32C_RL_2ADDR }
 };
 
 static reloc_howto_type *
@@ -316,6 +362,13 @@ m32c_elf_relocate_section
       int                          r_type;
       
       r_type = ELF32_R_TYPE (rel->r_info);
+
+      /* These are only used for relaxing; we don't actually relocate
+	 anything with them, so skip them.  */
+      if (r_type == R_M32C_RL_JUMP
+	  || r_type == R_M32C_RL_1ADDR
+	  || r_type == R_M32C_RL_2ADDR)
+	continue;
       
       r_symndx = ELF32_R_SYM (rel->r_info);
 
@@ -344,7 +397,7 @@ m32c_elf_relocate_section
       h      = NULL;
       sym    = NULL;
       sec    = NULL;
-      
+
       if (r_symndx < symtab_hdr->sh_info)
 	{
 	  sym = local_syms + r_symndx;
@@ -355,7 +408,7 @@ m32c_elf_relocate_section
 	  
 	  name = bfd_elf_string_from_elf_section
 	    (input_bfd, symtab_hdr->sh_link, sym->st_name);
-	  name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
+	  name = (sym->st_name == 0) ? bfd_section_name (input_bfd, sec) : name;
 	}
       else
 	{
@@ -439,6 +492,17 @@ m32c_elf_relocate_section
 	  break;
 	}
 
+#if 0
+      printf("relocate %s at %06lx relocation %06lx addend %ld  ",
+	     m32c_elf_howto_table[ELF32_R_TYPE(rel->r_info)].name,
+	     rel->r_offset, relocation, rel->r_addend);
+      {
+	int i;
+	for (i=0; i<4; i++)
+	  printf(" %02x", contents[rel->r_offset+i]);
+	printf("\n");
+      }
+#endif
       r = _bfd_final_link_relocate (howto, input_bfd, input_section,
                                     contents, rel->r_offset, relocation,
                                     rel->r_addend);
@@ -1143,169 +1207,166 @@ m32c_elf_relax_plt_section (bfd *dynobj,
   return TRUE;
 }
 
-struct relax_reloc_s
+static int
+compare_reloc (const void *e1, const void *e2)
 {
-  int machine;
-  int opcode_mask;
-  bfd_vma opcode;		/* original opcode or insn part */
-  int relax_backward;		/* lbound */
-  int relax_forward;		/* hbound */
-  int value_shift;
-  int mask;
-  int new_opcode;		/* new opcode */
-  int old_reloc;		/* old relocation */
-  int new_reloc;		/* new relocation  */
-  int use_pcrel;
-  int delete_n;		/* # bytes differ between original and new */
-};
-static struct relax_reloc_s relax_reloc [] =
-  {
-#if 0
-    {
-      bfd_mach_m16c,
-      0xff,
-      0xfc,			/* jmp.a */
-      -32768,
-      32767,
-      2,
-      0xffffff00,
-      0xf4,			/* jmp.w */
-      R_M32C_8_ELABEL24,
-      R_M32C_8_PCREL16,
-      1,
-      1,
-    },
-    {
-      bfd_mach_m32c,
-      0xff,
-      0xcc,			/* jmp.a */
-      -32768,
-      32767,
-      2,
-      0xffffff00,
-      0xce,			/* jmp.w */
-      R_M32C_8_ELABEL24,
-      R_M32C_8_PCREL16,
-      1,
-      1,
-    },
-    {
-      bfd_mach_m32c,
-      0xff,
-      0xcd,			/* jsr.a */
-      -32768,
-      32767,
-      2,
-      0xffffff00,
-      0xcf,			/* jsr.w */
-      R_M32C_8_ELABEL24,
-      R_M32C_8_PCREL16,
-      1,
-      1,
-    },
-    {
-      bfd_mach_m16c,
-      0xff,
-      0xf4,			/* jmp.w */
-      -128,
-      127,
-      2,
-      0xffffff00,
-      0xfe,			/* jmp.b */
-      R_M32C_8_PCREL16,
-      R_M32C_8_PCREL8,
-      1,
-      1,
-    },
-    {
-      bfd_mach_m32c,
-      0xff,
-      0xce,			/* jmp.w */
-      -128,
-      127,
-      2,
-      0xffffff00,
-      0xbb,			/* jmp.b */
-      R_M32C_8_PCREL16,
-      R_M32C_8_PCREL8,
-      1,
-      1,
-    },
-    {
-      bfd_mach_m32c,
-      0xc0f6,
-      0x8096,			/* dest */
-      0,
-      0xffff,
-      3,
-      0xffff3fff,
-      0xc000,			/* abs16 */
-      R_M32C_24_ABS24,
-      R_M32C_24_ABS16,
-      0,
-      1,
-    },
-    {
-      bfd_mach_m32c,
-      0xc0f6,
-      0x80a6,			/* dest */
-      0,
-      0xffff,
-      4,
-      0xffff3fff,
-      0xc000,			/* abs16 */
-      R_M32C_32_ABS24,
-      R_M32C_32_ABS16,
-      0,
-      1,
-    },
-    {
-      bfd_mach_m32c,
-      0xc0f6,
-      0x80b6,			/* dest */
-      0,
-      0xffff,
-      5,
-      0xffff3fff,
-      0xc000,			/* abs16 */
-      R_M32C_40_ABS24,
-      R_M32C_40_ABS16,
-      0,
-      1,
-    },
-    {
-      bfd_mach_m32c,
-      0x30f0,
-      0x20b0,			/* src */
-      0,
-      0xffff,
-      2,
-      0xffffcfff,
-      0x3000,			/* abs16 */
-      R_M32C_16_ABS24,
-      R_M32C_16_ABS16,
-      0,
-      1,
-    },
-    {
-      bfd_mach_m32c,
-      0xc086,
-      0x8086,			/* dest */
-      0,
-      0xffff,
-      2,
-      0xffff3fff,
-      0xc000,			/* abs16 */
-      R_M32C_16_ABS24,
-      R_M32C_16_ABS16,
-      0,
-      1,
-    },
-#endif
+  const Elf_Internal_Rela *i1 = (const Elf_Internal_Rela *) e1;
+  const Elf_Internal_Rela *i2 = (const Elf_Internal_Rela *) e2;
+
+  if (i1->r_offset == i2->r_offset)
+    return 0;
+  else
+    return i1->r_offset < i2->r_offset ? -1 : 1;
+}
+
+#define OFFSET_FOR_RELOC(rel) m32c_offset_for_reloc (abfd, sec, rel, symtab_hdr, shndx_buf, intsyms)
+static bfd_vma
+m32c_offset_for_reloc (bfd *abfd,
+		       asection * sec,
+		       Elf_Internal_Rela *rel,
+		       Elf_Internal_Shdr *symtab_hdr,
+		       Elf_External_Sym_Shndx *shndx_buf,
+		       Elf_Internal_Sym *intsyms)
+{
+  bfd_vma symval;
+
+  /* Get the value of the symbol referred to by the reloc.  */
+  if (ELF32_R_SYM (rel->r_info) < symtab_hdr->sh_info)
+    {
+      /* A local symbol.  */
+      Elf_Internal_Sym *isym;
+      Elf_External_Sym_Shndx *shndx;
+
+      isym = intsyms + ELF32_R_SYM (rel->r_info);
+      shndx = shndx_buf + (shndx_buf ? ELF32_R_SYM (rel->r_info) : 0);
+
+      symval = (isym->st_value
+		+ sec->output_section->vma
+		+ sec->output_offset);
+    }
+  else
     {
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+      unsigned long indx;
+      struct elf_link_hash_entry *h;
+
+      /* An external symbol.  */
+      indx = ELF32_R_SYM (rel->r_info) - symtab_hdr->sh_info;
+      h = elf_sym_hashes (abfd)[indx];
+      BFD_ASSERT (h != NULL);
+
+      if (h->root.type != bfd_link_hash_defined
+	  && h->root.type != bfd_link_hash_defweak)
+	/* This appears to be a reference to an undefined
+	   symbol.  Just ignore it--it will be caught by the
+	   regular reloc processing.  */
+	return 0;
+
+      symval = (h->root.u.def.value
+		+ h->root.u.def.section->output_section->vma
+		+ h->root.u.def.section->output_offset);
     }
-  };
+  return symval;
+}
+
+static int bytes_saved = 0;
+
+static int bytes_to_reloc[] = {
+  R_M32C_NONE,
+  R_M32C_8,
+  R_M32C_16,
+  R_M32C_24,
+  R_M32C_32
+};
+
+/* What we use the bits in a relax reloc addend (R_M32C_RL_*) for.  */
+
+/* Mask for the number of relocs associated with this insn.  */
+#define RLA_RELOCS		0x0000000f
+/* Number of bytes gas emitted (before gas's relaxing) */
+#define RLA_NBYTES		0x00000ff0
+
+/* If the displacement is within the given range and the new encoding
+   differs from the old encoding (the index), then the insn can be
+   relaxed to the new encoding.  */
+typedef struct {
+  int bytes;
+  unsigned int max_disp;
+  unsigned char new_encoding;
+} EncodingTable;
+
+static EncodingTable m16c_addr_encodings[] = {
+  { 0,   0,  0 }, /* R0 */
+  { 0,   0,  1 }, /* R1 */
+  { 0,   0,  2 }, /* R2 */
+  { 0,   0,  3 }, /* R3 */
+  { 0,   0,  4 }, /* A0 */
+  { 0,   0,  5 }, /* A1 */
+  { 0,   0,  6 }, /* [A0] */
+  { 0,   0,  7 }, /* [A1] */
+  { 1,   0,  6 }, /* udsp:8[A0] */
+  { 1,   0,  7 }, /* udsp:8[A1] */
+  { 1,   0, 10 }, /* udsp:8[SB] */
+  { 1,   0, 11 }, /* sdsp:8[FB] */
+  { 2, 255,  8 }, /* udsp:16[A0] */
+  { 2, 255,  9 }, /* udsp:16[A1] */
+  { 2, 255, 10 }, /* udsp:16[SB] */
+  { 2,   0, 15 }, /* abs:16 */
+};
+
+static EncodingTable m16c_jmpaddr_encodings[] = {
+  { 0,   0,  0 }, /* R0 */
+  { 0,   0,  1 }, /* R1 */
+  { 0,   0,  2 }, /* R2 */
+  { 0,   0,  3 }, /* R3 */
+  { 0,   0,  4 }, /* A0 */
+  { 0,   0,  5 }, /* A1 */
+  { 0,   0,  6 }, /* [A0] */
+  { 0,   0,  7 }, /* [A1] */
+  { 1,   0,  6 }, /* udsp:8[A0] */
+  { 1,   0,  7 }, /* udsp:8[A1] */
+  { 1,   0, 10 }, /* udsp:8[SB] */
+  { 1,   0, 11 }, /* sdsp:8[FB] */
+  { 3, 255,  8 }, /* udsp:20[A0] */
+  { 3, 255,  9 }, /* udsp:20[A1] */
+  { 2, 255, 10 }, /* udsp:16[SB] */
+  { 2,   0, 15 }, /* abs:16 */
+};
+
+static EncodingTable m32c_addr_encodings[] = {
+  { 0,     0,  0 }, /* [A0] */
+  { 0,     0,  1 }, /* [A1] */
+  { 0,     0,  2 }, /* A0 */
+  { 0,     0,  3 }, /* A1 */
+  { 1,     0,  0 }, /* udsp:8[A0] */
+  { 1,     0,  1 }, /* udsp:8[A1] */
+  { 1,     0,  6 }, /* udsp:8[SB] */
+  { 1,     0,  7 }, /* sdsp:8[FB] */
+  { 2,   255,  4 }, /* udsp:16[A0] */
+  { 2,   255,  5 }, /* udsp:16[A1] */
+  { 2,   255,  6 }, /* udsp:16[SB] */
+  { 2,   127,  7 }, /* sdsp:16[FB] */
+  { 3, 65535, 8 }, /* udsp:24[A0] */
+  { 3, 65535, 9 }, /* udsp:24[A1] */
+  { 3, 65535, 15 }, /* abs24 */
+  { 2,     0, 15 }, /* abs16 */
+  { 0,     0, 16 }, /* R2 */
+  { 0,     0, 17 }, /* R3 */
+  { 0,     0, 18 }, /* R0 */
+  { 0,     0, 19 }, /* R1 */
+  { 0,     0, 20 }, /*  */
+  { 0,     0, 21 }, /*  */
+  { 0,     0, 22 }, /*  */
+  { 0,     0, 23 }, /*  */
+  { 0,     0, 24 }, /*  */
+  { 0,     0, 25 }, /*  */
+  { 0,     0, 26 }, /*  */
+  { 0,     0, 27 }, /*  */
+  { 0,     0, 28 }, /*  */
+  { 0,     0, 29 }, /*  */
+  { 0,     0, 30 }, /*  */
+  { 0,     0, 31 }, /*  */
+};
+
 static bfd_boolean
 m32c_elf_relax_section
     (bfd *                  abfd,
@@ -1317,11 +1378,11 @@ m32c_elf_relax_section
   Elf_Internal_Shdr *shndx_hdr;
   Elf_Internal_Rela *internal_relocs;
   Elf_Internal_Rela *free_relocs = NULL;
-  Elf_Internal_Rela *irel, *irelend;
+  Elf_Internal_Rela *irel, *irelend, *srel;
   bfd_byte * contents = NULL;
   bfd_byte * free_contents = NULL;
-  Elf32_External_Sym *extsyms = NULL;
-  Elf32_External_Sym *free_extsyms = NULL;
+  Elf_Internal_Sym *intsyms = NULL;
+  Elf_Internal_Sym *free_intsyms = NULL;
   Elf_External_Sym_Shndx *shndx_buf = NULL;
   int machine;
 
@@ -1343,12 +1404,43 @@ m32c_elf_relax_section
       || (sec->flags & SEC_CODE) == 0)
     return TRUE;
 
-  /* Relaxing doesn't quite work right yet.  */
-  return TRUE;
-
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
 
+  /* Get the section contents.  */
+  if (elf_section_data (sec)->this_hdr.contents != NULL)
+    contents = elf_section_data (sec)->this_hdr.contents;
+  /* Go get them off disk.  */
+  else if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+    goto error_return;
+
+  /* Read this BFD's symbols.  */
+  /* Get cached copy if it exists.  */
+  if (symtab_hdr->contents != NULL)
+    {
+      intsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+    }
+  else
+    {
+      intsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr, symtab_hdr->sh_info, 0, NULL, NULL, NULL);
+      symtab_hdr->contents = (bfd_byte *) intsyms;
+    }
+
+  if (shndx_hdr->sh_size != 0)
+    {
+      bfd_size_type amt;
+
+      amt = symtab_hdr->sh_info;
+      amt *= sizeof (Elf_External_Sym_Shndx);
+      shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+      if (shndx_buf == NULL)
+	goto error_return;
+      if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
+	  || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
+	goto error_return;
+      shndx_hdr->contents = (bfd_byte *) shndx_buf;
+    }
+
   /* Get a copy of the native relocations.  */
   internal_relocs = (_bfd_elf_link_read_relocs
 		     (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
@@ -1358,188 +1450,388 @@ m32c_elf_relax_section
   if (! link_info->keep_memory)
     free_relocs = internal_relocs;
 
+  /* The RL_ relocs must be just before the operand relocs they go
+     with, so we must sort them to guarantee this.  */
+  qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela),
+         compare_reloc);
+
   /* Walk through them looking for relaxing opportunities.  */
   irelend = internal_relocs + sec->reloc_count;
 
   for (irel = internal_relocs; irel < irelend; irel++)
     {
       bfd_vma symval;
-      bfd_vma insn;
+      unsigned char *insn, *gap, *einsn;
       bfd_vma pc;
-      bfd_signed_vma pcrel_value;
-      bfd_vma addend;
-      int to_delete;
-      int i;
-
-      /* Get the section contents.  */
-      if (contents == NULL)
-	{
-	  if (elf_section_data (sec)->this_hdr.contents != NULL)
-	    contents = elf_section_data (sec)->this_hdr.contents;
-	  /* Go get them off disk.  */
-	  else if (!bfd_malloc_and_get_section (abfd, sec, &contents))
-	    goto error_return;
-	}
-
-      /* Read this BFD's symbols if we haven't done so already.  */
-      if (extsyms == NULL)
-	{
-	  /* Get cached copy if it exists.  */
-	  if (symtab_hdr->contents != NULL)
-	    extsyms = (Elf32_External_Sym *) symtab_hdr->contents;
-	  else
-	    {
-	      bfd_size_type amt = symtab_hdr->sh_size;
+      bfd_signed_vma pcrel;
+      int relax_relocs;
+      int gap_size;
+      int new_type;
+      int posn;
+      int enc;
+      EncodingTable *enctbl;
+      EncodingTable *e;
+
+      if (ELF32_R_TYPE(irel->r_info) != R_M32C_RL_JUMP
+	  && ELF32_R_TYPE(irel->r_info) != R_M32C_RL_1ADDR
+	  && ELF32_R_TYPE(irel->r_info) != R_M32C_RL_2ADDR)
+	continue;
 
-	      /* Go get them off disk.  */
-	      extsyms = (Elf32_External_Sym *) bfd_malloc (amt);
-	      if (extsyms == NULL)
-		goto error_return;
-	      free_extsyms = extsyms;
-	      if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0
-		  || bfd_bread (extsyms, amt, abfd) != amt)
-		goto error_return;
-	      symtab_hdr->contents = (bfd_byte *) extsyms;
-	    }
+      srel = irel;
 
-	  if (shndx_hdr->sh_size != 0)
-	    {
-	      bfd_size_type amt;
+      /* There will always be room for the relaxed insn, since it is smaller
+	 than the one it would replace.  */
+      BFD_ASSERT (irel->r_offset < sec->size);
 
-	      amt = symtab_hdr->sh_info;
-	      amt *= sizeof (Elf_External_Sym_Shndx);
-	      shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
-	      if (shndx_buf == NULL)
-		goto error_return;
-	      if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
-		  || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
-		goto error_return;
-	      shndx_hdr->contents = (bfd_byte *) shndx_buf;
-	    }
-	}
+      insn = contents + irel->r_offset;
+      relax_relocs = irel->r_addend % 16;
 
-      /* Get the value of the symbol referred to by the reloc.  */
-      if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
-	{
-	  /* A local symbol.  */
-	  Elf32_External_Sym *esym;
-	  Elf_External_Sym_Shndx *shndx;
-	  Elf_Internal_Sym isym;
-
-	  esym = extsyms + ELF32_R_SYM (irel->r_info);
-	  shndx = shndx_buf + (shndx_buf ? ELF32_R_SYM (irel->r_info) : 0);
-	  bfd_elf32_swap_symbol_in (abfd, esym, shndx, &isym);
-
-	  symval = (isym.st_value
-		    + sec->output_section->vma
-		    + sec->output_offset);
-	}
-      else
+      /* Ok, we only have three relocs we care about, and they're all
+	 fake.  The lower four bits of the addend is always the number
+	 of following relocs (hence the qsort above) that are assigned
+	 to this opcode.  The next 8 bits of the addend indicates the
+	 number of bytes in the insn.  We use the rest of them
+	 ourselves as flags for the more expensive operations (defines
+	 above).  The three relocs are:
+
+	 RL_JUMP: This marks all direct jump insns.  We check the
+		displacement and replace them with shorter jumps if
+		they're in range.  We also use this to find JMP.S
+		insns and manually shorten them when we delete bytes.
+		We have to decode these insns to figure out what to
+		do.
+
+	 RL_1ADDR: This is a :G or :Q insn, which has a single
+		"standard" operand.  We have to extract the type
+		field, see if it's a wide displacement, then figure
+		out if we can replace it with a narrow displacement.
+		We don't have to decode these insns.
+
+	 RL_2ADDR: Similarly, but two "standard" operands.  Note that
+		r_addend may still be 1, as standard operands don't
+		always have displacements.  Gas shouldn't give us one
+		with zero operands, but since we don't know which one
+		has the displacement, we check them both anyway.
+
+	 These all point to the beginning of the insn itself, not the
+	 operands.
+
+	 Note that we only relax one step at a time, relying on the
+	 linker to call us repeatedly.  Thus, there is no code for
+	 JMP.A->JMP.B although that will happen in two steps.
+	 Likewise, for 2ADDR relaxes, we do one operand per cycle.
+      */
+
+      /* Get the value of the symbol referred to by the reloc.  Just
+         in case this is the last reloc in the list, use the RL's
+         addend to choose between this reloc (no addend) or the next
+         (yes addend, which means at least one following reloc).  */
+      srel = irel + (relax_relocs ? 1 : 0);
+      symval = OFFSET_FOR_RELOC (srel);
+
+      /* Setting gap_size nonzero is the flag which means "something
+	 shrunk".  */
+      gap_size = 0;
+      new_type = ELF32_R_TYPE(srel->r_info);
+
+      pc = sec->output_section->vma + sec->output_offset
+	+ srel->r_offset;
+      pcrel = symval - pc + srel->r_addend;
+
+      if (machine == bfd_mach_m16c)
 	{
-	  unsigned long indx;
-	  struct elf_link_hash_entry *h;
+	  /* R8C / M16C */
 
-	  /* An external symbol.  */
-	  indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
-	  h = elf_sym_hashes (abfd)[indx];
-	  BFD_ASSERT (h != NULL);
-
-	  if (h->root.type != bfd_link_hash_defined
-	      && h->root.type != bfd_link_hash_defweak)
-	    /* This appears to be a reference to an undefined
-	       symbol.  Just ignore it--it will be caught by the
-	       regular reloc processing.  */
-	    continue;
+	  switch (ELF32_R_TYPE(irel->r_info))
+	    {
 
-	  symval = (h->root.u.def.value
-		    + h->root.u.def.section->output_section->vma
-		    + h->root.u.def.section->output_offset);
-	}
+	    case R_M32C_RL_JUMP:
+	      switch (insn[0])
+		{
+		case 0xfe: /* jmp.b */
+		  if (pcrel >= 2 && pcrel <= 9)
+		    {
+		      /* Relax JMP.B -> JMP.S.  We need to get rid of
+			 the following reloc though. */
+		      insn[0] = 0x60 | (pcrel - 2);
+		      new_type = R_M32C_NONE;
+		      irel->r_addend = 0x10;
+		      gap_size = 1;
+		      gap = insn + 1;
+		    }
+		  break;
+
+		case 0xf4: /* jmp.w */
+		  /* 128 is allowed because it will be one byte closer
+		     after relaxing.  Likewise for all other pc-rel
+		     jumps.  */
+		  if (pcrel <= 128 && pcrel >= -128)
+		    {
+		      /* Relax JMP.W -> JMP.B */
+		      insn[0] = 0xfe;
+		      insn[1] = 0;
+		      new_type = R_M32C_8_PCREL;
+		      gap_size = 1;
+		      gap = insn + 2;
+		    }
+		  break;
+
+		case 0xfc: /* jmp.a */
+		  if (pcrel <= 32768 && pcrel >= -32768)
+		    {
+		      /* Relax JMP.A -> JMP.W */
+		      insn[0] = 0xf4;
+		      insn[1] = 0;
+		      insn[2] = 0;
+		      new_type = R_M32C_16_PCREL;
+		      gap_size = 1;
+		      gap = insn + 3;
+		    }
+		  break;
+
+		case 0xfd: /* jsr.a */
+		  if (pcrel <= 32768 && pcrel >= -32768)
+		    {
+		      /* Relax JSR.A -> JSR.W */
+		      insn[0] = 0xf5;
+		      insn[1] = 0;
+		      insn[2] = 0;
+		      new_type = R_M32C_16_PCREL;
+		      gap_size = 1;
+		      gap = insn + 3;
+		    }
+		  break;
+		}
+	      break;
 
-      /* There will always be room for the relaxed insn, since it is smaller
-	 than the one it would replace.  */
-      BFD_ASSERT (irel->r_offset <= sec->size - 2);
+	    case R_M32C_RL_2ADDR:
+	      /* xxxx xxxx srce dest [src-disp] [dest-disp]*/
+
+	      enctbl = m16c_addr_encodings;
+	      posn = 2;
+	      enc = (insn[1] >> 4) & 0x0f;
+	      e = & enctbl[enc];
+
+	      if (srel->r_offset == irel->r_offset + posn
+		  && e->new_encoding != enc
+		  && symval <= e->max_disp)
+		{
+		  insn[1] &= 0x0f;
+		  insn[1] |= e->new_encoding << 4;
+		  gap_size = e->bytes - enctbl[e->new_encoding].bytes;
+		  gap = insn + posn + enctbl[e->new_encoding].bytes;
+		  new_type = bytes_to_reloc[enctbl[e->new_encoding].bytes];
+		  break;
+		}
+	      if (relax_relocs == 2)
+		srel ++;
+	      posn += e->bytes;
+
+	      goto try_1addr_16;
+
+	    case R_M32C_RL_1ADDR:
+	      /* xxxx xxxx xxxx dest [disp] */
+
+	      enctbl = m16c_addr_encodings;
+	      posn = 2;
+	      
+	      /* Check the opcode for jumps.  We know it's safe to
+		 do this because all 2ADDR insns are at least two
+		 bytes long.  */
+	      enc = insn[0] * 256 + insn[1];
+	      enc &= 0xfff0;
+	      if (enc == 0x7d20
+		  || enc == 0x7d00
+		  || enc == 0x7d30
+		  || enc == 0x7d10)
+		{
+		  enctbl = m16c_jmpaddr_encodings;
+		}
+
+	    try_1addr_16:
+	      /* srel, posn, and enc must be set here.  */
+
+	      symval = OFFSET_FOR_RELOC (srel);
+	      enc = insn[1] & 0x0f;
+	      e = & enctbl[enc];
+
+	      if (srel->r_offset == irel->r_offset + posn
+		  && e->new_encoding != enc
+		  && symval <= e->max_disp)
+		{
+		  insn[1] &= 0xf0;
+		  insn[1] |= e->new_encoding;
+		  gap_size = e->bytes - enctbl[e->new_encoding].bytes;
+		  gap = insn + posn + enctbl[e->new_encoding].bytes;
+		  new_type = bytes_to_reloc[enctbl[e->new_encoding].bytes];
+		  break;
+		}
 
-      insn = bfd_get_16 (abfd, contents + irel->r_offset + 0);
+	      break;
 
-      addend = irel->r_addend;
-      for (i = 0; relax_reloc[i].machine; i++)
+	    } /* Ends switch (reloc type) for m16c.  */
+	}
+      else /* machine == bfd_mach_m32c */
 	{
-#ifdef DEBUG
-	  _bfd_error_handler ("insn %x %d mask %x opcode %x =%x\n",
-			      insn, i, relax_reloc[i].opcode_mask,
-			      relax_reloc[i].opcode,
-			      (insn & relax_reloc[i].opcode_mask) == relax_reloc[i].opcode);
-#endif
-	  if (!(machine == relax_reloc[i].machine
-		&& (insn & relax_reloc[i].opcode_mask) == relax_reloc[i].opcode
-		&& (relax_reloc[i].old_reloc
-		    == (int) ELF32_R_TYPE(irel->r_info))))
-	    continue;
+	  /* M32CM / M32C */
 
-	  /* At this point we've confirmed we have a matching insn.  Now
-	     ensure the operand is in range.  */
-	  if (relax_reloc[i].use_pcrel)
+	  switch (ELF32_R_TYPE(irel->r_info))
 	    {
-	      pc = sec->output_section->vma + sec->output_offset
-		+ irel->r_offset;
-	      pcrel_value = symval - pc;
-#ifndef USE_REL /* put in for learning purposes */
-	      pcrel_value += addend;
-#else
-	      addend = bfd_get_signed_16 (abfd, contents + irel->r_offset + 2);
-	      pcrel_value += addend;
-#endif
-	    }
-	  else
-	    pcrel_value = symval;
 
-	  if (pcrel_value >= relax_reloc[i].relax_backward
-	      && pcrel_value < relax_reloc[i].relax_forward + 2)
-	    {
-	      /* We can relax to a shorter operand.  */
-	      insn = (insn & relax_reloc[i].mask) | relax_reloc[i].new_opcode;
+	    case R_M32C_RL_JUMP:
+	      switch (insn[0])
+		{
+		case 0xbb: /* jmp.b */
+		  if (pcrel >= 2 && pcrel <= 9)
+		    {
+		      int p = pcrel - 2;
+		      /* Relax JMP.B -> JMP.S.  We need to get rid of
+			 the following reloc though. */
+		      insn[0] = 0x4a | ((p << 3) & 0x30) | (p & 1);
+		      new_type = R_M32C_NONE;
+		      irel->r_addend = 0x10;
+		      gap_size = 1;
+		      gap = insn + 1;
+		    }
+		  break;
+
+		case 0xce: /* jmp.w */
+		  if (pcrel <= 128 && pcrel >= -128)
+		    {
+		      /* Relax JMP.W -> JMP.B */
+		      insn[0] = 0xbb;
+		      insn[1] = 0;
+		      new_type = R_M32C_8_PCREL;
+		      gap_size = 1;
+		      gap = insn + 2;
+		    }
+		  break;
+
+		case 0xcc: /* jmp.a */
+		  if (pcrel <= 32768 && pcrel >= -32768)
+		    {
+		      /* Relax JMP.A -> JMP.W */
+		      insn[0] = 0xce;
+		      insn[1] = 0;
+		      insn[2] = 0;
+		      new_type = R_M32C_16_PCREL;
+		      gap_size = 1;
+		      gap = insn + 3;
+		    }
+		  break;
+
+		case 0xcd: /* jsr.a */
+		  if (pcrel <= 32768 && pcrel >= -32768)
+		    {
+		      /* Relax JSR.A -> JSR.W */
+		      insn[0] = 0xcf;
+		      insn[1] = 0;
+		      insn[2] = 0;
+		      new_type = R_M32C_16_PCREL;
+		      gap_size = 1;
+		      gap = insn + 3;
+		    }
+		  break;
+		}
+	      break;
 
-	      to_delete = relax_reloc[i].delete_n;
+	    case R_M32C_RL_2ADDR:
+	      /* xSSS DDDx DDSS xxxx [src-disp] [dest-disp]*/
 
-	      /* Rewrite the insn.  */
-	      bfd_put_16 (abfd, insn, contents + irel->r_offset);
+	      einsn = insn;
+	      posn = 2;
+	      if (einsn[0] == 1)
+		{
+		  /* prefix; remove it as far as the RL reloc is concerned.  */
+		  einsn ++;
+		  posn ++;
+		}
 
-	      /* Set the new reloc type.  */
-	      irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
-					   relax_reloc[i].new_reloc);
-	      irel->r_addend = pcrel_value;
-	    }
-	  else
-	    continue;
+	      enctbl = m32c_addr_encodings;
+	      enc = ((einsn[0] & 0x70) >> 2) | ((einsn[1] & 0x30) >> 4);
+	      e = & enctbl[enc];
+
+	      if (srel->r_offset == irel->r_offset + posn
+		  && e->new_encoding != enc
+		  && symval <= e->max_disp)
+		{
+		  einsn[0] &= 0x8f;
+		  einsn[0] |= (e->new_encoding & 0x1c) << 2;
+		  einsn[1] &= 0xcf;
+		  einsn[1] |= (e->new_encoding & 0x03) << 4;
+		  gap_size = e->bytes - enctbl[e->new_encoding].bytes;
+		  gap = insn + posn + enctbl[e->new_encoding].bytes;
+		  new_type = bytes_to_reloc[enctbl[e->new_encoding].bytes];
+		  break;
+		}
+	      if (relax_relocs == 2)
+		  srel ++;
+	      posn += e->bytes;
+
+	      goto try_1addr_32;
+
+	    case R_M32C_RL_1ADDR:
+	      /* xxxx DDDx DDxx xxxx [disp] */
+
+	      einsn = insn;
+	      posn = 2;
+	      if (einsn[0] == 1)
+		{
+		  /* prefix; remove it as far as the RL reloc is concerned.  */
+		  einsn ++;
+		  posn ++;
+		}
 
-#ifdef DEBUG
-	  _bfd_error_handler  ("insn %x pc %x index %d mask %x shift %d delete %d\n"
-			       "old reloc %s new reloc %s",
-			       insn, sec->output_section->vma
-			       + sec->output_offset + irel->r_offset + 2,
-			       i, relax_reloc[i].opcode_mask,
-			       relax_reloc[i].value_shift, to_delete,
-			       m32c_get_reloc (relax_reloc[i].old_reloc),
-			       m32c_get_reloc (relax_reloc[i].new_reloc));
-#endif
+	      enctbl = m32c_addr_encodings;
+
+	    try_1addr_32:
+	      /* srel, posn, and enc must be set here.  */
+
+	      symval = OFFSET_FOR_RELOC (srel);
+	      enc = ((einsn[0] & 0x0e) << 1) |  ((einsn[1] & 0xc0) >> 6);
+	      e = & enctbl[enc];
+
+	      if (srel->r_offset == irel->r_offset + posn
+		  && e->new_encoding != enc
+		  && symval <= e->max_disp)
+		{
+		  einsn[0] &= 0xf1;
+		  einsn[0] |= (e->new_encoding & 0x1c) >> 1;
+		  einsn[1] &= 0x3f;
+		  einsn[1] |= (e->new_encoding & 0x03) << 6;
+		  gap_size = e->bytes - enctbl[e->new_encoding].bytes;
+		  gap = insn + posn + enctbl[e->new_encoding].bytes;
+		  new_type = bytes_to_reloc[enctbl[e->new_encoding].bytes];
+		  break;
+		}
+
+	      break;
+
+	    } /* Ends switch (reloc type) for m32c.  */
+	}
+
+      if (gap_size == 0)
+	continue;
+
+      *again = TRUE;
+
+      srel->r_info = ELF32_R_INFO (ELF32_R_SYM (srel->r_info), new_type);
+
+      /* Note that we've changed the relocs, section contents, etc.  */
+      elf_section_data (sec)->relocs = internal_relocs;
+      free_relocs = NULL;
+      
+      elf_section_data (sec)->this_hdr.contents = contents;
+      free_contents = NULL;
+
+      symtab_hdr->contents = (bfd_byte *) intsyms;
+      free_intsyms = NULL;
+
+      bytes_saved += gap_size;
+
+      if (! m32c_elf_relax_delete_bytes(abfd, sec, gap - contents, gap_size))
+	goto error_return;
 
-	  /* Note that we've changed the relocs, section contents, etc.  */
-	  elf_section_data (sec)->relocs = internal_relocs;
-	  free_relocs = NULL;
-
-	  elf_section_data (sec)->this_hdr.contents = contents;
-	  free_contents = NULL;
-
-	  symtab_hdr->contents = (bfd_byte *) extsyms;
-	  free_extsyms = NULL;
-
-	  /* Delete TO_DELETE bytes of data.  */
-	  if (! m32c_elf_relax_delete_bytes
-	      (abfd, sec, irel->r_offset + relax_reloc[i].value_shift,
-	       to_delete))
-	    goto error_return;
-	} /* next relax_reloc */
     } /* next relocation */
 
   if (free_relocs != NULL)
@@ -1565,18 +1857,18 @@ m32c_elf_relax_section
       free (shndx_buf);
     }
 
-  if (free_extsyms != NULL)
+  if (free_intsyms != NULL)
     {
       if (! link_info->keep_memory)
-	free (free_extsyms);
+	free (free_intsyms);
       /* Cache the symbols for elf_link_input_bfd.  */
       else
-	symtab_hdr->contents = NULL /* (unsigned char *) extsyms*/;
+	{
+	symtab_hdr->contents = NULL /* (unsigned char *) intsyms*/;
+	}
 
-      free_extsyms = NULL;
+      free_intsyms = NULL;
     }
-  /* elf_link_input_bfd expects internal syms.  */
-  symtab_hdr->contents = NULL;
 
   return TRUE;
 
@@ -1590,8 +1882,8 @@ m32c_elf_relax_section
       shndx_hdr->contents = NULL;
       free (shndx_buf);
     }
-  if (free_extsyms != NULL)
-    free (free_extsyms);
+  if (free_intsyms != NULL)
+    free (free_intsyms);
   return FALSE;
 }
 
@@ -1612,20 +1904,15 @@ m32c_elf_relax_delete_bytes
   Elf_Internal_Rela *irelend;
   Elf_Internal_Rela *irelalign;
   bfd_vma toaddr;
-  Elf32_External_Sym *esym;
-  Elf32_External_Sym *esymend;
-  Elf32_External_Sym *extsyms;
+  Elf_Internal_Sym *isym;
+  Elf_Internal_Sym *isymend;
+  Elf_Internal_Sym *intsyms;
   Elf_External_Sym_Shndx *shndx_buf;
   Elf_External_Sym_Shndx *shndx;
   struct elf_link_hash_entry ** sym_hashes;
   struct elf_link_hash_entry ** end_hashes;
   unsigned int                  symcount;
- 
-  symtab_hdr = & elf_tdata (abfd)->symtab_hdr;
-  extsyms = (Elf32_External_Sym *) symtab_hdr->contents;
-  shndx_hdr  = & elf_tdata (abfd)->symtab_shndx_hdr;
-  shndx_buf  = (Elf_External_Sym_Shndx *) shndx_hdr->contents;
-  sec_shndx  = _bfd_elf_section_from_bfd_section (abfd, sec);
+
   contents   = elf_section_data (sec)->this_hdr.contents;
 
   /* The deletion must stop at the next ALIGN reloc for an aligment
@@ -1646,27 +1933,62 @@ m32c_elf_relax_delete_bytes
       /* Get the new reloc address.  */
       if (irel->r_offset > addr && irel->r_offset < toaddr)
 	irel->r_offset -= count;
-      if (irel->r_addend > addr && irel->r_addend < toaddr)
-	irel->r_addend -= count;
+
+      if (ELF32_R_TYPE(irel->r_info) == R_M32C_RL_JUMP
+	  && irel->r_addend == 0x10 /* one byte insn, no relocs */
+	  && irel->r_offset + 1 < addr
+	  && irel->r_offset + 7 > addr)
+	{
+	  bfd_vma disp;
+	  unsigned char *insn = &contents[irel->r_offset];
+	  disp = *insn;
+	  /* This is a JMP.S, which we have to manually update. */
+	  if (elf32_m32c_machine (abfd) == bfd_mach_m16c)
+	    {
+	      if ((*insn & 0xf8) != 0x60)
+		continue;
+	      disp = (disp & 7);
+	    }
+	  else
+	    {
+	      if ((*insn & 0xce) != 0x4a)
+		continue;
+	      disp = ((disp & 0x30) >> 3) | (disp & 1);
+	    }
+	  if (irel->r_offset + disp + 2 >= addr+count)
+	    {
+	      disp -= count;
+	      if (elf32_m32c_machine (abfd) == bfd_mach_m16c)
+		{
+		  *insn = (*insn & 0xf8) | disp;
+		}
+	      else
+		{
+		  *insn = (*insn & 0xce) | ((disp & 6) << 3) | (disp & 1);
+		}
+	    }
+	}
     }
 
   /* Adjust the local symbols defined in this section.  */
+  symtab_hdr = & elf_tdata (abfd)->symtab_hdr;
+  intsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+  isym = intsyms;
+  isymend = isym + symtab_hdr->sh_info;
+
+  sec_shndx  = _bfd_elf_section_from_bfd_section (abfd, sec);
+  shndx_hdr  = & elf_tdata (abfd)->symtab_shndx_hdr;
+  shndx_buf  = (Elf_External_Sym_Shndx *) shndx_hdr->contents;
   shndx = shndx_buf;
-  esym = extsyms;
-  esymend = esym + symtab_hdr->sh_info;
-  for (; esym < esymend; esym++, shndx = (shndx ? shndx + 1 : NULL))
-    {
-      Elf_Internal_Sym isym;
-      Elf_External_Sym_Shndx dummy;
 
-      bfd_elf32_swap_symbol_in (abfd, esym, shndx, &isym);
+  for (; isym < isymend; isym++, shndx = (shndx ? shndx + 1 : NULL))
+    {
 
-      if ((int) isym.st_shndx == sec_shndx
-	  && isym.st_value > addr
-	  && isym.st_value < toaddr)
+      if ((int) isym->st_shndx == sec_shndx
+	  && isym->st_value > addr
+	  && isym->st_value < toaddr)
 	{
-	  isym.st_value -= count;
-	  bfd_elf32_swap_symbol_out (abfd, &isym, (PTR) esym, (PTR) & dummy);
+	  isym->st_value -= count;
 	}
     }
 
@@ -1687,7 +2009,9 @@ m32c_elf_relax_delete_bytes
 	  && sym_hash->root.u.def.section == sec
 	  && sym_hash->root.u.def.value > addr
 	  && sym_hash->root.u.def.value < toaddr)
-	sym_hash->root.u.def.value -= count;
+	{
+	  sym_hash->root.u.def.value -= count;
+	}
     }
 
   return TRUE;
Index: bfd/libbfd.h
===================================================================
RCS file: /cvs/src/src/bfd/libbfd.h,v
retrieving revision 1.172
diff -p -U3 -r1.172  bfd/libbfd.h
--- bfd/libbfd.h	17 Feb 2006 14:36:21 -0000	1.172
+++ bfd/libbfd.h	24 Feb 2006 22:02:28 -0000
@@ -1374,6 +1374,9 @@ static const char *const bfd_reloc_code_
   "BFD_RELOC_DLX_LO16",
   "BFD_RELOC_DLX_JMP26",
   "BFD_RELOC_M32C_HI8",
+  "BFD_RELOC_M32C_RL_JUMP",
+  "BFD_RELOC_M32C_RL_1ADDR",
+  "BFD_RELOC_M32C_RL_2ADDR",
   "BFD_RELOC_M32R_24",
   "BFD_RELOC_M32R_10_PCREL",
   "BFD_RELOC_M32R_18_PCREL",
Index: bfd/reloc.c
===================================================================
RCS file: /cvs/src/src/bfd/reloc.c,v
retrieving revision 1.147
diff -p -U3 -r1.147  bfd/reloc.c
--- bfd/reloc.c	17 Feb 2006 14:36:21 -0000	1.147
+++ bfd/reloc.c	24 Feb 2006 22:02:28 -0000
@@ -3202,6 +3202,12 @@ ENUMDOC
 
 ENUM
   BFD_RELOC_M32C_HI8
+ENUMX
+  BFD_RELOC_M32C_RL_JUMP
+ENUMX
+  BFD_RELOC_M32C_RL_1ADDR
+ENUMX
+  BFD_RELOC_M32C_RL_2ADDR
 ENUMDOC
   Renesas M16C/M32C Relocations.
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]