This is the mail archive of the binutils@sources.redhat.com 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]

[PATCH]: Fix HC12 gas to handle indexed addressing mode (Bug savannah/1825)


Hi!

The gas HC12 is handling PC-relative addressing modes incorrectly.  This is reported in:

http://savannah.gnu.org/bugs/?func=detailbug&bug_id=1825&group_id=2424

This patch fixes that and also improves the assembler to use either a 5, 9 or 16-bit
offset in the case an offset is not yet known at the place it is used (we were always
using a 16-bit offset, even if it was resolved later on).  To handle the 'sym1 - sym2'
offset case, I've followed the cris port and provide a specific md_relax_frag.

Committed.  Update of gas testsuite to follow.

	Stephane

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.
Index: config/tc-m68hc11.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-m68hc11.c,v
retrieving revision 1.31
diff -u -p -r1.31 tc-m68hc11.c
--- config/tc-m68hc11.c	1 Dec 2002 10:20:06 -0000	1.31
+++ config/tc-m68hc11.c	1 Dec 2002 10:54:05 -0000
@@ -1933,14 +1933,21 @@ build_indexed_byte (op, format, move_ins
         }
       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_operand
   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, se
 	    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: config/tc-m68hc11.h
===================================================================
RCS file: /cvs/src/src/gas/config/tc-m68hc11.h,v
retrieving revision 1.10
diff -u -p -r1.10 tc-m68hc11.h
--- config/tc-m68hc11.h	5 Sep 2002 00:01:18 -0000	1.10
+++ config/tc-m68hc11.h	1 Dec 2002 10:54:05 -0000
@@ -93,6 +93,15 @@ extern int m68hc11_parse_long_option PAR
 #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 */

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