* config/tc-mips.h (tc_frob_file): Define.
authorIan Lance Taylor <ian@airs.com>
Thu, 28 Mar 1996 20:37:02 +0000 (20:37 +0000)
committerIan Lance Taylor <ian@airs.com>
Thu, 28 Mar 1996 20:37:02 +0000 (20:37 +0000)
(mips_frob_file): Declare.
* config/tc-mips.c (struct mips_hi_fixup): Define.
(mips_hi_fixup_list): New static variable.
(imm_unmatched_hi): New static variable.
(md_assemble): Clear imm_reloc, imm_unmatched_hi, and
offset_reloc.  Pass imm_unmatched_hi to append_insn.
(append_insn): Add unmatched_hi parameter.  If it is set, add the
new fixup to mips_hi_fixup_list.  Change all callers.
(mips_ip): Set imm_unmatched_hi when appropriate.
(mips_frob_file): New function.

gas/ChangeLog
gas/config/tc-mips.c
gas/config/tc-mips.h

index 763d0b5..d2d1a05 100644 (file)
@@ -1,3 +1,17 @@
+Thu Mar 28 15:27:47 1996  Ian Lance Taylor  <ian@cygnus.com>
+
+       * config/tc-mips.h (tc_frob_file): Define.
+       (mips_frob_file): Declare.
+       * config/tc-mips.c (struct mips_hi_fixup): Define.
+       (mips_hi_fixup_list): New static variable.
+       (imm_unmatched_hi): New static variable.
+       (md_assemble): Clear imm_reloc, imm_unmatched_hi, and
+       offset_reloc.  Pass imm_unmatched_hi to append_insn.
+       (append_insn): Add unmatched_hi parameter.  If it is set, add the
+       new fixup to mips_hi_fixup_list.  Change all callers.
+       (mips_ip): Set imm_unmatched_hi when appropriate.
+       (mips_frob_file): New function.
+
 Thu Mar 28 11:47:59 1996  Doug Evans  <dje@canuck.cygnus.com>
 
        * configure.in (sparc-*-solaris2*): Renamed from sparc*-*-solaris2*.
index da69803..19b271d 100644 (file)
@@ -292,6 +292,31 @@ static int prev_insn_unreordered;
 /* Non-zero if the previous previous instruction was in a .set
    noreorder.  */
 static int prev_prev_insn_unreordered;
+
+/* For ECOFF and ELF, relocations against symbols are done in two
+   parts, with a HI relocation and a LO relocation.  Each relocation
+   has only 16 bits of space to store an addend.  This means that in
+   order for the linker to handle carries correctly, it must be able
+   to locate both the HI and the LO relocation.  This means that the
+   relocations must appear in order in the relocation table.
+
+   In order to implement this, we keep track of each unmatched HI
+   relocation.  We then sort them so that they immediately precede the
+   corresponding LO relocation. */
+
+struct mips_hi_fixup
+{
+  /* Next HI fixup.  */
+  struct mips_hi_fixup *next;
+  /* This fixup.  */
+  fixS *fixp;
+  /* The section this fixup is in.  */
+  segT seg;
+};
+
+/* The list of unmatched HI relocs.  */
+
+static struct mips_hi_fixup *mips_hi_fixup_list;
 \f
 /* Since the MIPS does not have multiple forms of PC relative
    instructions, we do not have to do relaxing as is done on other
@@ -384,7 +409,8 @@ static int reg_needs_delay PARAMS ((int));
 static void append_insn PARAMS ((char *place,
                                 struct mips_cl_insn * ip,
                                 expressionS * p,
-                                bfd_reloc_code_real_type r));
+                                bfd_reloc_code_real_type r,
+                                boolean));
 static void mips_no_prev_insn PARAMS ((void));
 static void mips_emit_delays PARAMS ((void));
 #ifdef USE_STDARG
@@ -519,11 +545,21 @@ mips_pop_insert ()
 \f
 static char *expr_end;
 
+/* Expressions which appear in instructions.  These are set by
+   mips_ip.  */
+
 static expressionS imm_expr;
 static expressionS offset_expr;
+
+/* Relocs associated with imm_expr and offset_expr.  */
+
 static bfd_reloc_code_real_type imm_reloc;
 static bfd_reloc_code_real_type offset_reloc;
 
+/* This is set by mips_ip if imm_reloc is an unmatched HI16_S reloc.  */
+
+static boolean imm_unmatched_hi;
+
 /*
  * This function is called once, at assembler startup time.  It should
  * set up all the tables, etc. that the MD part of the assembler will need.
@@ -775,7 +811,10 @@ md_assemble (str)
   struct mips_cl_insn insn;
 
   imm_expr.X_op = O_absent;
+  imm_reloc = BFD_RELOC_UNUSED;
+  imm_unmatched_hi = false;
   offset_expr.X_op = O_absent;
+  offset_reloc = BFD_RELOC_UNUSED;
 
   mips_ip (str, &insn);
   if (insn_error)
@@ -790,11 +829,12 @@ md_assemble (str)
   else
     {
       if (imm_expr.X_op != O_absent)
-       append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc);
+       append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc,
+                    imm_unmatched_hi);
       else if (offset_expr.X_op != O_absent)
-       append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc);
+       append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false);
       else
-       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED);
+       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false);
     }
 }
 
@@ -876,11 +916,12 @@ reg_needs_delay (reg)
    used with RELOC_TYPE.  */
 
 static void
-append_insn (place, ip, address_expr, reloc_type)
+append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
      char *place;
      struct mips_cl_insn *ip;
      expressionS *address_expr;
      bfd_reloc_code_real_type reloc_type;
+     boolean unmatched_hi;
 {
   register unsigned long prev_pinfo, pinfo;
   char *f;
@@ -1112,10 +1153,24 @@ append_insn (place, ip, address_expr, reloc_type)
          /* Don't generate a reloc if we are writing into a variant
             frag.  */
          if (place == NULL)
-           fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
-                               address_expr,
-                               reloc_type == BFD_RELOC_16_PCREL_S2,
-                               reloc_type);
+           {
+             fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+                                 address_expr,
+                                 reloc_type == BFD_RELOC_16_PCREL_S2,
+                                 reloc_type);
+             if (unmatched_hi)
+               {
+                 struct mips_hi_fixup *hi_fixup;
+
+                 assert (reloc_type == BFD_RELOC_HI16_S);
+                 hi_fixup = ((struct mips_hi_fixup *)
+                             xmalloc (sizeof (struct mips_hi_fixup)));
+                 hi_fixup->fixp = fixp;
+                 hi_fixup->seg = now_seg;
+                 hi_fixup->next = mips_hi_fixup_list;
+                 mips_hi_fixup_list = hi_fixup;
+               }
+           }
        }
     }
 
@@ -1633,7 +1688,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   va_end (args);
   assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-  append_insn (place, &insn, ep, r);
+  append_insn (place, &insn, ep, r, false);
 }
 
 /*
@@ -1698,10 +1753,10 @@ macro_build_lui (place, counter, ep, regnum)
   if (r == BFD_RELOC_UNUSED)
     {
       insn.insn_opcode |= high_expr.X_add_number;
-      append_insn (place, &insn, NULL, r);
+      append_insn (place, &insn, NULL, r, false);
     }
   else
-    append_insn (place, &insn, &high_expr, r);
+    append_insn (place, &insn, &high_expr, r, false);
 }
 
 /*                     set_at()
@@ -3648,7 +3703,8 @@ macro (ip)
                       "d,v,t", tempreg, tempreg, GP);
          macro_build ((char *) NULL, &icnt, &offset_expr,
                       mips_isa < 3 ? "lw" : "ld",
-                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16);
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
+                      tempreg);
          p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
                        RELAX_ENCODE (12, 12 + gpdel, gpdel, 8 + gpdel, 0, 0),
                        offset_expr.X_add_symbol, (long) 0, (char *) NULL);
@@ -3659,7 +3715,7 @@ macro (ip)
            }
          macro_build (p, &icnt, &offset_expr,
                       mips_isa < 3 ? "lw" : "ld",
-                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16);
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
          p += 4;
          macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
          p += 4;
@@ -4904,12 +4960,11 @@ macro2 (ip)
     as_warn ("Macro used $at after \".set noat\"");
 }
 
+/* This routine assembles an instruction into its binary format.  As a
+   side effect, it sets one of the global variables imm_reloc or
+   offset_reloc to the type of relocation to do if one of the operands
+   is an address expression.  */
 
-/*
-This routine assembles an instruction into its binary format.  As a side
-effect it sets one of the global variables imm_reloc or offset_reloc to the
-type of relocation to do if one of the operands is an address expression.
-*/
 static void
 mips_ip (str, ip)
      char *str;
@@ -5478,7 +5533,10 @@ mips_ip (str, ip)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
                      else if (c == 'h')
-                       imm_reloc = BFD_RELOC_HI16_S;
+                       {
+                         imm_reloc = BFD_RELOC_HI16_S;
+                         imm_unmatched_hi = true;
+                       }
                      else
                        imm_reloc = BFD_RELOC_HI16;
                    }
@@ -5590,7 +5648,10 @@ mips_ip (str, ip)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
                      else if (c == 'h')
-                       imm_reloc = BFD_RELOC_HI16_S;
+                       {
+                         imm_reloc = BFD_RELOC_HI16_S;
+                         imm_unmatched_hi = true;
+                       }
                      else
                        imm_reloc = BFD_RELOC_HI16;
                    }
@@ -6240,6 +6301,75 @@ cons_fix_new_mips (frag, where, nbytes, exp)
               nbytes == 2 ? BFD_RELOC_16 : BFD_RELOC_32);
 }
 
+/* Sort any unmatched HI16_S relocs so that they immediately precede
+   the corresponding LO reloc.  This is called before md_apply_fix and
+   tc_gen_reloc.  Unmatched HI16_S relocs can only be generated by
+   explicit use of the %hi modifier.  */
+
+void
+mips_frob_file ()
+{
+  struct mips_hi_fixup *l;
+
+  for (l = mips_hi_fixup_list; l != NULL; l = l->next)
+    {
+      segment_info_type *seginfo;
+      fixS *f, *prev;
+
+      assert (l->fixp->fx_r_type == BFD_RELOC_HI16_S);
+
+      /* Check quickly whether the next fixup happens to be a matching
+         %lo.  */
+      if (l->fixp->fx_next != NULL
+         && l->fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+         && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+         && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+       continue;
+
+      /* Look through the fixups for this segment for a matching %lo.
+         When we find one, move the %hi just in front of it.  */
+      seginfo = seg_info (l->seg);
+      prev = NULL;
+      for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+       {
+         /* Check whether this is a %lo fixup which matches l->fixp;
+             we can't use it if the %lo is already matching a %hi.  */
+         if (f->fx_r_type == BFD_RELOC_LO16
+             && f->fx_addsy == l->fixp->fx_addsy
+             && f->fx_offset == l->fixp->fx_offset
+             && (prev == NULL
+                 || prev->fx_r_type != BFD_RELOC_HI16_S
+                 || prev->fx_addsy != f->fx_addsy
+                 || prev->fx_offset !=  f->fx_offset))
+           {
+             fixS **pf;
+
+             /* Move l->fixp before f.  */
+             for (pf = &seginfo->fix_root;
+                  *pf != l->fixp;
+                  pf = &(*pf)->fx_next)
+               assert (*pf != NULL);
+
+             *pf = l->fixp->fx_next;
+
+             l->fixp->fx_next = f;
+             if (prev == NULL)
+               seginfo->fix_root = l->fixp;
+             else
+               prev->fx_next = l->fixp;
+
+             break;
+           }
+
+         prev = f;
+       }
+
+      if (f == NULL)
+       as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+                      "Unmatched %%hi reloc");
+    }
+}
+
 /* When generating embedded PIC code we need to use a special
    relocation to represent the difference of two symbols in the .text
    section (switch tables use a difference of this sort).  See
index a701e76..c8672b7 100644 (file)
@@ -27,7 +27,6 @@
 #define TARGET_ARCH bfd_arch_mips
 
 #define ONLY_STANDARD_ESCAPES
-#define BACKSLASH_V
 #define WORKING_DOT_WORD       1
 #define OLD_FLOAT_READS
 #define REPEAT_CONS_EXPRESSIONS
@@ -79,6 +78,9 @@ extern int mips_parse_long_option PARAMS ((const char *));
 #define tc_frob_label(sym) mips_define_label (sym)
 extern void mips_define_label PARAMS ((struct symbol *));
 
+#define tc_frob_file() mips_frob_file ()
+extern void mips_frob_file PARAMS ((void));
+
 #define TC_CONS_FIX_NEW cons_fix_new_mips
 extern void cons_fix_new_mips ();