FreeBSD/contrib/binutils/gas/config/tc-mips.c
/* Given a mips16 variant frag FRAGP, return non-zero if it needs an
   extended opcode.  SEC is the section the frag is in.  */
Given a mips16 variant frag FRAGP, return non-zero if it needs an
extended opcode.  SEC is the section the frag is in.
Given a mips16 variant frag FRAGP return non - zero if it needs an extended opcode . SEC is the section the frag is in .
static int
mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
{
  int type;
  const struct mips16_immed_operand *op;
  offsetT val;
  int mintiny, maxtiny;
  segT symsec;
  fragS *sym_frag;
  if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
    return 0;
  if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
    return 1;
  type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
  op = mips16_immed_operands;
  while (op->type != type)
    {
      ++op;
      assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
    }
  if (op->unsp)
    {
      if (type == '<' || type == '>' || type == '[' || type == ']')
	{
	  mintiny = 1;
	  maxtiny = 1 << op->nbits;
	}
      else
	{
	  mintiny = 0;
	  maxtiny = (1 << op->nbits) - 1;
	}
    }
  else
    {
      mintiny = - (1 << (op->nbits - 1));
      maxtiny = (1 << (op->nbits - 1)) - 1;
    }
  sym_frag = symbol_get_frag (fragp->fr_symbol);
  val = S_GET_VALUE (fragp->fr_symbol);
  symsec = S_GET_SEGMENT (fragp->fr_symbol);
  if (op->pcrel)
    {
      addressT addr;
      if (sec == NULL)
	{
	  if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
	    return 1;
	}
      else
	{
	  if (symsec != sec)
	    {
	      fragp->fr_subtype =
		RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
	      as_bad_where (fragp->fr_file, fragp->fr_line,
			    _("unsupported PC relative reference to different section"));
	      return 1;
	    }
	  if (fragp != sym_frag && sym_frag->fr_address == 0)
	    return 0;
	}
      if (stretch != 0
	  && sym_frag->relax_marker != fragp->relax_marker)
	{
	  fragS *f;
	  for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next)
	    {
	      if (f->fr_type == rs_align || f->fr_type == rs_align_code)
		{
		  if (stretch < 0)
		    stretch = - ((- stretch)
				 & ~ ((1 << (int) f->fr_offset) - 1));
		  else
		    stretch &= ~ ((1 << (int) f->fr_offset) - 1);
		  if (stretch == 0)
		    break;
		}
	    }
	  if (f != NULL)
	    val += stretch;
	}
      addr = fragp->fr_address + fragp->fr_fix;
      if (type == 'p' || type == 'q')
	{
	  addr += 2;
	  if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
	    addr += 2;
	  if ((val & 1) != 0)
	    --val;
	}
      else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
	addr -= 4;
      else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
	addr -= 2;
      val -= addr & ~ ((1 << op->shift) - 1);
      if (type == 'p' || type == 'q')
	val /= 2;
      if ((val & ((1 << op->shift) - 1)) != 0 && sec == NULL)
	{
	  fragp->fr_subtype =
	    RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
	  return 1;
	}
      if (val == ((maxtiny + 1) << op->shift)
	  && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
	  && sec == NULL)
	{
	  fragp->fr_subtype =
	    RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
	  return 1;
	}
    }
  else if (symsec != absolute_section && sec != NULL)
    as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
  if ((val & ((1 << op->shift) - 1)) != 0
      || val < (mintiny << op->shift)
      || val > (maxtiny << op->shift))
    return 1;
  else
    return 0;
}
static int mips16_extended_frag ( fragS * fragp , asection * sec , long stretch ) { int type ; const struct mips16_immed_operand * op ; offsetT val ; int mintiny , maxtiny ; segT symsec ; fragS * sym_frag ; if ( RELAX_MIPS16_USER_SMALL ( fragp -> fr_subtype ) ) return 0 ; if ( RELAX_MIPS16_USER_EXT ( fragp -> fr_subtype ) ) return 1 ; type = RELAX_MIPS16_TYPE ( fragp -> fr_subtype ) ; op = mips16_immed_operands ; while ( op -> type != type ) { ++ op ; assert ( op < mips16_immed_operands + MIPS16_NUM_IMMED ) ; } if ( op -> unsp ) { if ( type == ' ' || type == ' ' || type == ' ' || type == ' ' ) { mintiny = 1 ; maxtiny = 1 << op -> nbits ; } else { mintiny = 0 ; maxtiny = ( 1 << op -> nbits ) - 1 ; } } else { mintiny = - ( 1 << ( op -> nbits - 1 ) ) ; maxtiny = ( 1 << ( op -> nbits - 1 ) ) - 1 ; } sym_frag = symbol_get_frag ( fragp -> fr_symbol ) ; val = S_GET_VALUE ( fragp -> fr_symbol ) ; symsec = S_GET_SEGMENT ( fragp -> fr_symbol ) ; if ( op -> pcrel ) { addressT addr ; if ( sec == NULL ) { if ( RELAX_MIPS16_LONG_BRANCH ( fragp -> fr_subtype ) ) return 1 ; } else { if ( symsec != sec ) { fragp -> fr_subtype = RELAX_MIPS16_MARK_LONG_BRANCH ( fragp -> fr_subtype ) ; as_bad_where ( fragp -> fr_file , fragp -> fr_line , _ ( " " ) ) ; return 1 ; } if ( fragp != sym_frag && sym_frag -> fr_address == 0 ) return 0 ; } if ( stretch != 0 && sym_frag -> relax_marker != fragp -> relax_marker ) { fragS * f ; for ( f = fragp ; f != NULL && f != sym_frag ; f = f -> fr_next ) { if ( f -> fr_type == rs_align || f -> fr_type == rs_align_code ) { if ( stretch < 0 ) stretch = - ( ( - stretch ) & ~ ( ( 1 << ( int ) f -> fr_offset ) - 1 ) ) ; else stretch &= ~ ( ( 1 << ( int ) f -> fr_offset ) - 1 ) ; if ( stretch == 0 ) break ; } } if ( f != NULL ) val += stretch ; } addr = fragp -> fr_address + fragp -> fr_fix ; if ( type == ' ' || type == ' ' ) { addr += 2 ; if ( RELAX_MIPS16_EXTENDED ( fragp -> fr_subtype ) ) addr += 2 ; if ( ( val & 1 ) != 0 ) -- val ; } else if ( RELAX_MIPS16_JAL_DSLOT ( fragp -> fr_subtype ) ) addr -= 4 ; else if ( RELAX_MIPS16_DSLOT ( fragp -> fr_subtype ) ) addr -= 2 ; val -= addr & ~ ( ( 1 << op -> shift ) - 1 ) ; if ( type == ' ' || type == ' ' ) val /= 2 ; if ( ( val & ( ( 1 << op -> shift ) - 1 ) ) != 0 && sec == NULL ) { fragp -> fr_subtype = RELAX_MIPS16_MARK_LONG_BRANCH ( fragp -> fr_subtype ) ; return 1 ; } if ( val == ( ( maxtiny + 1 ) << op -> shift ) && ! RELAX_MIPS16_EXTENDED ( fragp -> fr_subtype ) && sec == NULL ) { fragp -> fr_subtype = RELAX_MIPS16_MARK_LONG_BRANCH ( fragp -> fr_subtype ) ; return 1 ; } } else if ( symsec != absolute_section && sec != NULL ) as_bad_where ( fragp -> fr_file , fragp -> fr_line , _ ( " " ) ) ; if ( ( val & ( ( 1 << op -> shift ) - 1 ) ) != 0 || val < ( mintiny << op -> shift ) || val > ( maxtiny << op -> shift ) ) return 1 ; else return 0 ; }
Given a mips16 variant frag FRAGP, return non-zero if it needs an
extended opcode.
Given a mips16 variant frag FRAGP return non - zero if it needs an extended opcode .
/* We won't have the section when we are called from
         mips_relax_frag.  However, we will always have been called
         from md_estimate_size_before_relax first.  If this is a
         branch to a different section, we mark it as such.  If SEC is
         NULL, and the frag is not marked, then it must be a branch to
         the same section.  */ /* Must have been called from md_estimate_size_before_relax.  */ /* FIXME: We should support this, and let the linker
                 catch branches and loads that are out of range.  */ /* Assume non-extended on the first relaxation pass.
	       The address we have calculated will be bogus if this is
	       a forward branch to another frag, as the forward frag
	       will have fr_address == 0.  */ /* In this case, we know for sure that the symbol fragment is in
	 the same section.  If the relax_marker of the symbol fragment
	 differs from the relax_marker of this fragment, we have not
	 yet adjusted the symbol fragment fr_address.  We want to add
	 in STRETCH in order to get a better estimate of the address.
	 This particularly matters because of the shift bits.  */ /* Adjust stretch for any alignment frag.  Note that if have
             been expanding the earlier code, the symbol may be
             defined in what appears to be an earlier frag.  FIXME:
             This doesn't handle the fr_subtype field, which specifies
             a maximum number of bytes to skip when doing an
             alignment.  */ /* The base address rules are complicated.  The base address of
         a branch is the following instruction.  The base address of a
         PC relative load or add is the instruction itself, but if it
         is in a delay slot (in which case it can not be extended) use
         the address of the instruction whose delay slot it is in.  */ /* If we are currently assuming that this frag should be
	     extended, then, the current address is two bytes
	     higher.  */ /* Ignore the low bit in the target, since it will be set
             for a text label.  */ /* Branch offsets have an implicit 0 in the lowest bit.  */ /* If any of the shifted bits are set, we must use an extended
         opcode.  If the address depends on the size of this
         instruction, this can lead to a loop, so we arrange to always
         use an extended opcode.  We only check this when we are in
         the main relaxation loop, when SEC is NULL.  */ /* If we are about to mark a frag as extended because the value
         is precisely maxtiny + 1, then there is a chance of an
         infinite loop as in the following code:
	     la	$4,foo
	     .skip	1020
	     .align	2
	   foo:
	 In this case when the la is extended, foo is 0x3fc bytes
	 away, so the la can be shrunk, but then foo is 0x400 away, so
	 the la must be extended.  To avoid this loop, we mark the
	 frag as extended if it was small, and is about to become
	 extended with a value of maxtiny + 1.  */