Fix Bug savannah/1825:
authorStephane Carrez <stcarrez@nerim.fr>
Sun, 1 Dec 2002 11:02:10 +0000 (11:02 +0000)
committerStephane Carrez <stcarrez@nerim.fr>
Sun, 1 Dec 2002 11:02:10 +0000 (11:02 +0000)
* config/tc-m68hc11.h (md_relax_frag): Define to support relaxations
that are not pc-relative.
(m68hc11_relax_frag): Declare.

* config/tc-m68hc11.c (build_indexed_byte): Use a frag_var to handle
the offsetable indexed addressing modes (n,r).
(build_insn): Cleanup some locals.
(m68hc11_relax_frag): New function imported from tc-cris.c to handle
relaxation of difference between two symbols of same section.
(md_convert_frag): For INDEXED_OFFSET relaxs, use the displacement
only when this is a PC-relative operand and the offset is not absolute.
(md_estimate_size_before_relax): Convert the INDEXED_OFFSET,UNDEF frag
to INDEXED_OFFSET,STATE_BITS5 when the symbol is absolute; this will
be handled by m68hc11_relax_frag.

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

index f233231a721b669022b18393d3cab6c9b1815074..76b52d5d4ff9248e713c1192990e99ffdb3ee686 100644 (file)
@@ -1,3 +1,21 @@
+2002-12-01  Stephane Carrez  <stcarrez@nerim.fr>
+
+       Fix Bug savannah/1825:
+       * config/tc-m68hc11.h (md_relax_frag): Define to support relaxations
+       that are not pc-relative.
+       (m68hc11_relax_frag): Declare.
+
+       * config/tc-m68hc11.c (build_indexed_byte): Use a frag_var to handle
+       the offsetable indexed addressing modes (n,r).
+       (build_insn): Cleanup some locals.
+       (m68hc11_relax_frag): New function imported from tc-cris.c to handle
+       relaxation of difference between two symbols of same section.
+       (md_convert_frag): For INDEXED_OFFSET relaxs, use the displacement
+       only when this is a PC-relative operand and the offset is not absolute.
+       (md_estimate_size_before_relax): Convert the INDEXED_OFFSET,UNDEF frag
+       to INDEXED_OFFSET,STATE_BITS5 when the symbol is absolute; this will
+       be handled by m68hc11_relax_frag.
+
 2002-12-01  Stephane Carrez  <stcarrez@nerim.fr>
 
        * config/tc-m68hc11.c (elf_flags): Set default ABI to gcc default
index 34dbf6b41fde143bf90ab48ad97e8ed9db6b777d..2586902ff1344f0c537a6700d102db6f01f1d03f 100644 (file)
@@ -1933,14 +1933,21 @@ build_indexed_byte (op, format, move_insn)
         }
       else if (op->reg1 != REG_PC)
        {
-         byte = (byte << 3) | 0xe2;
+          symbolS *sym;
+          offsetT off;
+
          f = frag_more (1);
          number_to_chars_bigendian (f, byte, 1);
-
-         f = frag_more (2);
-         fix_new_exp (frag_now, f - frag_now->fr_literal, 2,
-                      &op->exp, FALSE, BFD_RELOC_16);
-         number_to_chars_bigendian (f, 0, 2);
+          sym = op->exp.X_add_symbol;
+          off = op->exp.X_add_number;
+          if (op->exp.X_op != O_symbol)
+            {
+              sym = make_expr_symbol (&op->exp);
+              off = 0;
+            }
+         frag_var (rs_machine_dependent, 2, 2,
+                   ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF),
+                   sym, off, f);
        }
       else
        {
@@ -2060,17 +2067,12 @@ build_insn (opcode, operands, nb_operands)
   char *f;
   long format;
   int move_insn = 0;
-  fragS *frag;
-  int where;
 
   /* Put the page code instruction if there is one.  */
   format = opcode->format;
 
-  frag = frag_now;
-  where = frag_now_fix ();
-
   if (format & M6811_OP_BRANCH)
-    fix_new (frag, where, 1,
+    fix_new (frag_now, frag_now_fix (), 1,
              &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP);
 
   if (format & OP_EXTENDED)
@@ -2732,6 +2734,97 @@ tc_gen_reloc (section, fixp)
   return reloc;
 }
 
+/* We need a port-specific relaxation function to cope with sym2 - sym1
+   relative expressions with both symbols in the same segment (but not
+   necessarily in the same frag as this insn), for example:
+     ldab sym2-(sym1-2),pc
+    sym1:
+   The offset can be 5, 9 or 16 bits long.  */
+
+long
+m68hc11_relax_frag (seg, fragP, stretch)
+     segT seg ATTRIBUTE_UNUSED;
+     fragS *fragP;
+     long stretch ATTRIBUTE_UNUSED;
+{
+  long growth;
+  offsetT aim = 0;
+  symbolS *symbolP;
+  const relax_typeS *this_type;
+  const relax_typeS *start_type;
+  relax_substateT next_state;
+  relax_substateT this_state;
+  const relax_typeS *table = TC_GENERIC_RELAX_TABLE;
+
+  /* We only have to cope with frags as prepared by
+     md_estimate_size_before_relax.  The STATE_BITS16 case may geet here
+     because of the different reasons that it's not relaxable.  */
+  switch (fragP->fr_subtype)
+    {
+    case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
+      /* When we get to this state, the frag won't grow any more.  */
+      return 0;
+
+    case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+    case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+      if (fragP->fr_symbol == NULL
+         || S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+       as_fatal (_("internal inconsistency problem in %s: fr_symbol %lx"),
+                 __FUNCTION__, (long) fragP->fr_symbol);
+      symbolP = fragP->fr_symbol;
+      if (symbol_resolved_p (symbolP))
+       as_fatal (_("internal inconsistency problem in %s: resolved symbol"),
+                 __FUNCTION__);
+      aim = S_GET_VALUE (symbolP);
+      break;
+
+    default:
+      as_fatal (_("internal inconsistency problem in %s: fr_subtype %d"),
+                 __FUNCTION__, fragP->fr_subtype);
+    }
+
+  /* The rest is stolen from relax_frag.  There's no obvious way to
+     share the code, but fortunately no requirement to keep in sync as
+     long as fragP->fr_symbol does not have its segment changed.  */
+
+  this_state = fragP->fr_subtype;
+  start_type = this_type = table + this_state;
+
+  if (aim < 0)
+    {
+      /* Look backwards.  */
+      for (next_state = this_type->rlx_more; next_state;)
+       if (aim >= this_type->rlx_backward)
+         next_state = 0;
+       else
+         {
+           /* Grow to next state.  */
+           this_state = next_state;
+           this_type = table + this_state;
+           next_state = this_type->rlx_more;
+         }
+    }
+  else
+    {
+      /* Look forwards.  */
+      for (next_state = this_type->rlx_more; next_state;)
+       if (aim <= this_type->rlx_forward)
+         next_state = 0;
+       else
+         {
+           /* Grow to next state.  */
+           this_state = next_state;
+           this_type = table + this_state;
+           next_state = this_type->rlx_more;
+         }
+    }
+
+  growth = this_type->rlx_length - start_type->rlx_length;
+  if (growth != 0)
+    fragP->fr_subtype = this_state;
+  return growth;
+}
+
 void
 md_convert_frag (abfd, sec, fragP)
      bfd *abfd ATTRIBUTE_UNUSED;
@@ -2799,25 +2892,32 @@ md_convert_frag (abfd, sec, fragP)
       break;
 
     case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+      if (fragP->fr_opcode[0] == 3
+          && fragP->fr_symbol != 0
+          && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+        value = disp;
       fragP->fr_opcode[0] = fragP->fr_opcode[0] << 6;
-      if ((fragP->fr_opcode[0] & 0x0ff) == 0x0c0)
-       fragP->fr_opcode[0] |= disp & 0x1f;
-      else
-       fragP->fr_opcode[0] |= value & 0x1f;
+      fragP->fr_opcode[0] |= value & 0x1f;
       break;
 
     case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+      if (fragP->fr_opcode[0] == 3
+          && fragP->fr_symbol != 0
+          && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+        value = disp;
       fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
       fragP->fr_opcode[0] |= 0xE0;
-      fix_new (fragP, fragP->fr_fix, 1,
-              fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_8);
+      fragP->fr_opcode[0] |= (value >> 8) & 1;
+      fragP->fr_opcode[1] = value;
       fragP->fr_fix += 1;
       break;
 
     case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
       fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
       fragP->fr_opcode[0] |= 0xe2;
-      if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa)
+      if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa
+          && fragP->fr_symbol != 0
+          && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
        {
          fixp = fix_new (fragP, fragP->fr_fix, 2,
                          fragP->fr_symbol, fragP->fr_offset,
@@ -2927,13 +3027,23 @@ md_estimate_size_before_relax (fragP, segment)
            case STATE_INDEXED_OFFSET:
              assert (current_architecture & cpu6812);
 
-             /* Switch the indexed operation to 16-bit mode.  */
-             fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
-             fragP->fr_opcode[0] |= 0xe2;
-             fragP->fr_fix++;
-             fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
-                      fragP->fr_offset, 0, BFD_RELOC_16);
-             fragP->fr_fix++;
+              if (fragP->fr_symbol
+                  && S_GET_SEGMENT (fragP->fr_symbol) == absolute_section)
+                {
+                   fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET,
+                                                     STATE_BITS5);
+                   /* Return the size of the variable part of the frag. */
+                   return md_relax_table[fragP->fr_subtype].rlx_length;
+                }
+              else
+                {
+                   /* Switch the indexed operation to 16-bit mode.  */
+                   fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
+                   fragP->fr_opcode[0] |= 0xe2;
+                   fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+                            fragP->fr_offset, 0, BFD_RELOC_16);
+                   fragP->fr_fix += 2;
+                }
              break;
 
            case STATE_XBCC_BRANCH:
index c57637f96e537364415f3ceccb36209a15daa111..e7ea44cd80ce4910244e2749f38567fd5ddf57a9 100644 (file)
@@ -93,6 +93,15 @@ extern int m68hc11_parse_long_option PARAMS ((char *));
 #define TC_GENERIC_RELAX_TABLE md_relax_table
 extern struct relax_type md_relax_table[];
 
+/* GAS only handles relaxations for pc-relative data targeting addresses
+   in the same segment, so we have to handle the rest on our own.  */
+#define md_relax_frag(SEG, FRAGP, STRETCH)             \
+ ((FRAGP)->fr_symbol != NULL                           \
+  && S_GET_SEGMENT ((FRAGP)->fr_symbol) == (SEG)       \
+  ? relax_frag (SEG, FRAGP, STRETCH)                   \
+  : m68hc11_relax_frag (SEG, FRAGP, STRETCH))
+extern long m68hc11_relax_frag PARAMS ((segT, fragS*, long));
+
 #define TC_HANDLES_FX_DONE
 
 #define DIFF_EXPR_OK           /* .-foo gets turned into PC relative relocs */