2002-11-30 Nathanael Nerode <neroden@gcc.gnu.org>
[deliverable/binutils-gdb.git] / gas / config / tc-cris.c
index 0ca98f597a8732ec2ae1aad6c51533ebc6e576b7..8f4b63324b3eeac14f3fcf0a0c13761fd5c500d4 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-cris.c -- Assembler code for the CRIS CPU core.
-   Copyright 2000, 2001 Free Software Foundation, Inc.
+   Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
 
    Contributed by Axis Communications AB, Lund, Sweden.
    Originally written for GAS 1.38.1 by Mikael Asker.
 #define SYNTAX_USER_SYM_NO_LEADING_UNDERSCORE "no_leading_underscore"
 #define REGISTER_PREFIX_CHAR '$'
 
+/* True for expressions where getting X_add_symbol and X_add_number is
+   enough to get the "base" and "offset"; no need to make_expr_symbol.
+   It's not enough to check if X_op_symbol is NULL; that misses unary
+   operations like O_uminus.  */
+#define SIMPLE_EXPR(EXP) \
+ ((EXP)->X_op == O_constant || (EXP)->X_op == O_symbol)
+
 /* Like in ":GOT", ":GOTOFF" etc.  Other ports use '@', but that's in
    line_separator_chars for CRIS, so we avoid it.  */
 #define PIC_SUFFIX_CHAR ':'
@@ -330,6 +337,98 @@ cris_target_format ()
     }
 }
 
+/* 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:
+     move.d [pc+sym2-(sym1-2)],r10
+    sym1:
+   The offset can be 8, 16 or 32 bits long.  */
+
+long
+cris_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 dword cases may get here
+     because of the different reasons that they aren't relaxable.  */
+  switch (fragP->fr_subtype)
+    {
+    case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_DWORD):
+    case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD):
+      /* When we get to these states, the frag won't grow any more.  */
+      return 0;
+
+    case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_WORD):
+    case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE):
+      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;
+}
+
 /* Prepare machine-dependent frags for relaxation.
 
    Called just before relaxation starts. Any symbol that is now undefined
@@ -386,6 +485,17 @@ md_estimate_size_before_relax (fragP, segment_type)
            = ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD);
          fragP->fr_var = md_cris_relax_table[fragP->fr_subtype].rlx_length;
        }
+      else if (!symbol_resolved_p (fragP->fr_symbol))
+       {
+         /* The symbol will eventually be completely resolved as an
+            absolute expression, but right now it depends on the result
+            of relaxation and we don't know anything else about the
+            value.  We start relaxation with the assumption that it'll
+            fit in a byte.  */
+         fragP->fr_subtype
+           = ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE);
+         fragP->fr_var = md_cris_relax_table[fragP->fr_subtype].rlx_length;
+       }
       else
        {
          /* Absolute expression.  */
@@ -526,7 +636,10 @@ md_convert_frag (abfd, sec, fragP)
       break;
 
     case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE):
-      var_partp[0] = target_address - (address_of_var_part + 1);
+      if (symbolP == NULL)
+       as_fatal (_("internal inconsistency in %s: bdapq no symbol"),
+                   __FUNCTION__);
+      opcodep[0] = S_GET_VALUE (symbolP);
       var_part_size = 0;
       break;
 
@@ -536,7 +649,10 @@ md_convert_frag (abfd, sec, fragP)
       opcodep[0] = BDAP_PC_LOW + (1 << 4);
       opcodep[1] &= 0xF0;
       opcodep[1] |= BDAP_INCR_HIGH;
-      md_number_to_chars (var_partp, (long) (target_address), 2);
+      if (symbolP == NULL)
+       as_fatal (_("internal inconsistency in %s: bdap.w with no symbol"),
+                 __FUNCTION__);
+      md_number_to_chars (var_partp, S_GET_VALUE (symbolP), 2);
       var_part_size = 2;
       break;
 
@@ -808,17 +924,17 @@ md_assemble (str)
            is_undefined = 1;
        }
 
-      if (output_instruction.expr.X_op == O_constant
-         || to_seg == now_seg || is_undefined)
+      if (to_seg == now_seg || is_undefined)
        {
          /* Handle complex expressions.  */
          valueT addvalue
-           = (output_instruction.expr.X_op_symbol != NULL
-              ? 0 : output_instruction.expr.X_add_number);
+           = (SIMPLE_EXPR (&output_instruction.expr)
+              ? output_instruction.expr.X_add_number
+              : 0);
          symbolS *sym
-           = (output_instruction.expr.X_op_symbol != NULL
-              ? make_expr_symbol (&output_instruction.expr)
-              : output_instruction.expr.X_add_symbol);
+           = (SIMPLE_EXPR (&output_instruction.expr)
+              ? output_instruction.expr.X_add_symbol
+              : make_expr_symbol (&output_instruction.expr));
 
          /* If is_undefined, then the expression may BECOME now_seg.  */
          length_code = is_undefined ? STATE_UNDF : STATE_BYTE;
@@ -832,8 +948,7 @@ md_assemble (str)
        {
          /* We have: to_seg != now_seg && to_seg != undefined_section.
             This means it is a branch to a known symbol in another
-            section.  Code in data?  Weird but valid.  Emit a 32-bit
-            branch.  */
+            section, perhaps an absolute address.  Emit a 32-bit branch.  */
          char *cond_jump = frag_more (10);
 
          gen_cond_branch_32 (opcodep, cond_jump, frag_now,
@@ -2386,10 +2501,10 @@ gen_bdap (base_regno, exprP)
     {
       /* Handle complex expressions.  */
       valueT addvalue
-       = exprP->X_op_symbol != NULL ? 0 : exprP->X_add_number;
+       = SIMPLE_EXPR (exprP) ? exprP->X_add_number : 0;
       symbolS *sym
-       = (exprP->X_op_symbol != NULL
-          ? make_expr_symbol (exprP) : exprP->X_add_symbol);
+       = (SIMPLE_EXPR (exprP)
+          ? exprP->X_add_symbol : make_expr_symbol (exprP));
 
       /* The expression is not defined yet but may become absolute.  We
         make it a relocation to be relaxed.  */
@@ -2570,7 +2685,7 @@ cris_get_pic_suffix (cPP, relocp, exprP)
                break;
 
              /* Allow complex expressions as the constant part.  It still
-                has to be a assembly-time constant or there will be an
+                has to be an assembly-time constant or there will be an
                 error emitting the reloc.  This makes the PIC qualifiers
                 idempotent; foo:GOTOFF+32 == foo+32:GOTOFF.  The former we
                 recognize here; the latter is parsed in the incoming
@@ -2895,10 +3010,8 @@ tc_gen_reloc (section, fixP)
   relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
 
   if (fixP->fx_pcrel)
-    /* FIXME: Is this correct?  */
-    relP->addend = fixP->fx_addnumber;
+    relP->addend = 0;
   else
-    /* At least *this one* is correct.  */
     relP->addend = fixP->fx_offset;
 
   /* This is the standard place for KLUDGEs to work around bugs in
@@ -2972,14 +3085,16 @@ md_show_usage (stream)
 /* Apply a fixS (fixup of an instruction or data that we didn't have
    enough info to complete immediately) to the data in a frag.  */
 
-int
+void
 md_apply_fix3 (fixP, valP, seg)
      fixS *fixP;
      valueT *valP;
      segT seg;
 {
-  long val = *valP;
-
+  /* This assignment truncates upper bits if valueT is 64 bits (as with
+     --enable-64-bit-bfd), which is fine here, though we cast to avoid
+     any compiler warnings.  */
+  long val = (long) *valP;
   char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
 
   if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
@@ -2992,24 +3107,13 @@ md_apply_fix3 (fixP, valP, seg)
     }
   else
     {
-      /* I took this from tc-arc.c, since we used to not support
-        fx_subsy != NULL.  I'm not totally sure it's TRT.  */
+      /* We can't actually support subtracting a symbol.  */
       if (fixP->fx_subsy != (symbolS *) NULL)
-       {
-         if (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section)
-           val -= S_GET_VALUE (fixP->fx_subsy);
-         else
-           {
-             /* We can't actually support subtracting a symbol.  */
-             as_bad_where (fixP->fx_file, fixP->fx_line,
-                           _("expression too complex"));
-           }
-       }
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("expression too complex"));
 
       cris_number_to_imm (buf, val, fixP->fx_size, fixP, seg);
     }
-
-  return 1;
 }
 
 /* All relocations are relative to the location just after the fixup;
@@ -3044,10 +3148,10 @@ md_undefined_symbol (name)
   return 0;
 }
 
-/* Definition of TC_FORCE_RELOCATION.
-   FIXME: Unsure of this.  Can we omit it?  Just copied from tc-i386.c
-   when doing multi-object format with ELF, since it's the only other
-   multi-object-format target with a.out and ELF.  */
+/* If this function returns non-zero, it prevents the relocation
+   against symbol(s) in the FIXP from being replaced with relocations
+   against section symbols, and guarantees that a relocation will be
+   emitted even when the value can be resolved locally.  */
 int
 md_cris_force_relocation (fixp)
      struct fix *fixp;
@@ -3068,7 +3172,7 @@ md_cris_force_relocation (fixp)
       ;
     }
 
-  return 0;
+  return S_FORCE_RELOC (fixp->fx_addsy);
 }
 
 /* Check and emit error if broken-word handling has failed to fix up a
This page took 0.028703 seconds and 4 git commands to generate.