OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-stable/] [binutils-2.20.1/] [gas/] [config/] [tc-xtensa.c] - Diff between revs 816 and 818

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 816 Rev 818
/* tc-xtensa.c -- Assemble Xtensa instructions.
/* tc-xtensa.c -- Assemble Xtensa instructions.
   Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009
   Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009
   Free Software Foundation, Inc.
   Free Software Foundation, Inc.
 
 
   This file is part of GAS, the GNU Assembler.
   This file is part of GAS, the GNU Assembler.
 
 
   GAS is free software; you can redistribute it and/or modify
   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   the Free Software Foundation; either version 3, or (at your option)
   any later version.
   any later version.
 
 
   GAS is distributed in the hope that it will be useful,
   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   GNU General Public License for more details.
 
 
   You should have received a copy of the GNU General Public License
   You should have received a copy of the GNU General Public License
   along with GAS; see the file COPYING.  If not, write to
   along with GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
   the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */
   MA 02110-1301, USA.  */
 
 
#include <limits.h>
#include <limits.h>
#include "as.h"
#include "as.h"
#include "sb.h"
#include "sb.h"
#include "safe-ctype.h"
#include "safe-ctype.h"
#include "tc-xtensa.h"
#include "tc-xtensa.h"
#include "subsegs.h"
#include "subsegs.h"
#include "xtensa-relax.h"
#include "xtensa-relax.h"
#include "dwarf2dbg.h"
#include "dwarf2dbg.h"
#include "xtensa-istack.h"
#include "xtensa-istack.h"
#include "struc-symbol.h"
#include "struc-symbol.h"
#include "xtensa-config.h"
#include "xtensa-config.h"
 
 
/* Provide default values for new configuration settings.  */
/* Provide default values for new configuration settings.  */
#ifndef XSHAL_ABI
#ifndef XSHAL_ABI
#define XSHAL_ABI 0
#define XSHAL_ABI 0
#endif
#endif
 
 
#ifndef uint32
#ifndef uint32
#define uint32 unsigned int
#define uint32 unsigned int
#endif
#endif
#ifndef int32
#ifndef int32
#define int32 signed int
#define int32 signed int
#endif
#endif
 
 
/* Notes:
/* Notes:
 
 
   Naming conventions (used somewhat inconsistently):
   Naming conventions (used somewhat inconsistently):
      The xtensa_ functions are exported
      The xtensa_ functions are exported
      The xg_ functions are internal
      The xg_ functions are internal
 
 
   We also have a couple of different extensibility mechanisms.
   We also have a couple of different extensibility mechanisms.
   1) The idiom replacement:
   1) The idiom replacement:
      This is used when a line is first parsed to
      This is used when a line is first parsed to
      replace an instruction pattern with another instruction
      replace an instruction pattern with another instruction
      It is currently limited to replacements of instructions
      It is currently limited to replacements of instructions
      with constant operands.
      with constant operands.
   2) The xtensa-relax.c mechanism that has stronger instruction
   2) The xtensa-relax.c mechanism that has stronger instruction
      replacement patterns.  When an instruction's immediate field
      replacement patterns.  When an instruction's immediate field
      does not fit the next instruction sequence is attempted.
      does not fit the next instruction sequence is attempted.
      In addition, "narrow" opcodes are supported this way.  */
      In addition, "narrow" opcodes are supported this way.  */
 
 
 
 
/* Define characters with special meanings to GAS.  */
/* Define characters with special meanings to GAS.  */
const char comment_chars[] = "#";
const char comment_chars[] = "#";
const char line_comment_chars[] = "#";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = ";";
const char line_separator_chars[] = ";";
const char EXP_CHARS[] = "eE";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "rRsSfFdDxXpP";
const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 
 
 
/* Flags to indicate whether the hardware supports the density and
/* Flags to indicate whether the hardware supports the density and
   absolute literals options.  */
   absolute literals options.  */
 
 
bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
 
 
/* Maximum width we would pad an unreachable frag to get alignment.  */
/* Maximum width we would pad an unreachable frag to get alignment.  */
#define UNREACHABLE_MAX_WIDTH  8
#define UNREACHABLE_MAX_WIDTH  8
 
 
static vliw_insn cur_vinsn;
static vliw_insn cur_vinsn;
 
 
unsigned xtensa_num_pipe_stages;
unsigned xtensa_num_pipe_stages;
unsigned xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
unsigned xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
 
 
static enum debug_info_type xt_saved_debug_type = DEBUG_NONE;
static enum debug_info_type xt_saved_debug_type = DEBUG_NONE;
 
 
/* Some functions are only valid in the front end.  This variable
/* Some functions are only valid in the front end.  This variable
   allows us to assert that we haven't crossed over into the
   allows us to assert that we haven't crossed over into the
   back end.  */
   back end.  */
static bfd_boolean past_xtensa_end = FALSE;
static bfd_boolean past_xtensa_end = FALSE;
 
 
/* Flags for properties of the last instruction in a segment.  */
/* Flags for properties of the last instruction in a segment.  */
#define FLAG_IS_A0_WRITER       0x1
#define FLAG_IS_A0_WRITER       0x1
#define FLAG_IS_BAD_LOOPEND     0x2
#define FLAG_IS_BAD_LOOPEND     0x2
 
 
 
 
/* We define a special segment names ".literal" to place literals
/* We define a special segment names ".literal" to place literals
   into.  The .fini and .init sections are special because they
   into.  The .fini and .init sections are special because they
   contain code that is moved together by the linker.  We give them
   contain code that is moved together by the linker.  We give them
   their own special .fini.literal and .init.literal sections.  */
   their own special .fini.literal and .init.literal sections.  */
 
 
#define LITERAL_SECTION_NAME            xtensa_section_rename (".literal")
#define LITERAL_SECTION_NAME            xtensa_section_rename (".literal")
#define LIT4_SECTION_NAME               xtensa_section_rename (".lit4")
#define LIT4_SECTION_NAME               xtensa_section_rename (".lit4")
#define INIT_SECTION_NAME               xtensa_section_rename (".init")
#define INIT_SECTION_NAME               xtensa_section_rename (".init")
#define FINI_SECTION_NAME               xtensa_section_rename (".fini")
#define FINI_SECTION_NAME               xtensa_section_rename (".fini")
 
 
 
 
/* This type is used for the directive_stack to keep track of the
/* This type is used for the directive_stack to keep track of the
   state of the literal collection pools.  If lit_prefix is set, it is
   state of the literal collection pools.  If lit_prefix is set, it is
   used to determine the literal section names; otherwise, the literal
   used to determine the literal section names; otherwise, the literal
   sections are determined based on the current text section.  The
   sections are determined based on the current text section.  The
   lit_seg and lit4_seg fields cache these literal sections, with the
   lit_seg and lit4_seg fields cache these literal sections, with the
   current_text_seg field used a tag to indicate whether the cached
   current_text_seg field used a tag to indicate whether the cached
   values are valid.  */
   values are valid.  */
 
 
typedef struct lit_state_struct
typedef struct lit_state_struct
{
{
  char *lit_prefix;
  char *lit_prefix;
  segT current_text_seg;
  segT current_text_seg;
  segT lit_seg;
  segT lit_seg;
  segT lit4_seg;
  segT lit4_seg;
} lit_state;
} lit_state;
 
 
static lit_state default_lit_sections;
static lit_state default_lit_sections;
 
 
 
 
/* We keep a list of literal segments.  The seg_list type is the node
/* We keep a list of literal segments.  The seg_list type is the node
   for this list.  The literal_head pointer is the head of the list,
   for this list.  The literal_head pointer is the head of the list,
   with the literal_head_h dummy node at the start.  */
   with the literal_head_h dummy node at the start.  */
 
 
typedef struct seg_list_struct
typedef struct seg_list_struct
{
{
  struct seg_list_struct *next;
  struct seg_list_struct *next;
  segT seg;
  segT seg;
} seg_list;
} seg_list;
 
 
static seg_list literal_head_h;
static seg_list literal_head_h;
static seg_list *literal_head = &literal_head_h;
static seg_list *literal_head = &literal_head_h;
 
 
 
 
/* Lists of symbols.  We keep a list of symbols that label the current
/* Lists of symbols.  We keep a list of symbols that label the current
   instruction, so that we can adjust the symbols when inserting alignment
   instruction, so that we can adjust the symbols when inserting alignment
   for various instructions.  We also keep a list of all the symbols on
   for various instructions.  We also keep a list of all the symbols on
   literals, so that we can fix up those symbols when the literals are
   literals, so that we can fix up those symbols when the literals are
   later moved into the text sections.  */
   later moved into the text sections.  */
 
 
typedef struct sym_list_struct
typedef struct sym_list_struct
{
{
  struct sym_list_struct *next;
  struct sym_list_struct *next;
  symbolS *sym;
  symbolS *sym;
} sym_list;
} sym_list;
 
 
static sym_list *insn_labels = NULL;
static sym_list *insn_labels = NULL;
static sym_list *free_insn_labels = NULL;
static sym_list *free_insn_labels = NULL;
static sym_list *saved_insn_labels = NULL;
static sym_list *saved_insn_labels = NULL;
 
 
static sym_list *literal_syms;
static sym_list *literal_syms;
 
 
 
 
/* Flags to determine whether to prefer const16 or l32r
/* Flags to determine whether to prefer const16 or l32r
   if both options are available.  */
   if both options are available.  */
int prefer_const16 = 0;
int prefer_const16 = 0;
int prefer_l32r = 0;
int prefer_l32r = 0;
 
 
/* Global flag to indicate when we are emitting literals.  */
/* Global flag to indicate when we are emitting literals.  */
int generating_literals = 0;
int generating_literals = 0;
 
 
/* The following PROPERTY table definitions are copied from
/* The following PROPERTY table definitions are copied from
   <elf/xtensa.h> and must be kept in sync with the code there.  */
   <elf/xtensa.h> and must be kept in sync with the code there.  */
 
 
/* Flags in the property tables to specify whether blocks of memory
/* Flags in the property tables to specify whether blocks of memory
   are literals, instructions, data, or unreachable.  For
   are literals, instructions, data, or unreachable.  For
   instructions, blocks that begin loop targets and branch targets are
   instructions, blocks that begin loop targets and branch targets are
   designated.  Blocks that do not allow density, instruction
   designated.  Blocks that do not allow density, instruction
   reordering or transformation are also specified.  Finally, for
   reordering or transformation are also specified.  Finally, for
   branch targets, branch target alignment priority is included.
   branch targets, branch target alignment priority is included.
   Alignment of the next block is specified in the current block
   Alignment of the next block is specified in the current block
   and the size of the current block does not include any fill required
   and the size of the current block does not include any fill required
   to align to the next block.  */
   to align to the next block.  */
 
 
#define XTENSA_PROP_LITERAL             0x00000001
#define XTENSA_PROP_LITERAL             0x00000001
#define XTENSA_PROP_INSN                0x00000002
#define XTENSA_PROP_INSN                0x00000002
#define XTENSA_PROP_DATA                0x00000004
#define XTENSA_PROP_DATA                0x00000004
#define XTENSA_PROP_UNREACHABLE         0x00000008
#define XTENSA_PROP_UNREACHABLE         0x00000008
/* Instruction only properties at beginning of code.  */
/* Instruction only properties at beginning of code.  */
#define XTENSA_PROP_INSN_LOOP_TARGET    0x00000010
#define XTENSA_PROP_INSN_LOOP_TARGET    0x00000010
#define XTENSA_PROP_INSN_BRANCH_TARGET  0x00000020
#define XTENSA_PROP_INSN_BRANCH_TARGET  0x00000020
/* Instruction only properties about code.  */
/* Instruction only properties about code.  */
#define XTENSA_PROP_INSN_NO_DENSITY     0x00000040
#define XTENSA_PROP_INSN_NO_DENSITY     0x00000040
#define XTENSA_PROP_INSN_NO_REORDER     0x00000080
#define XTENSA_PROP_INSN_NO_REORDER     0x00000080
/* Historically, NO_TRANSFORM was a property of instructions,
/* Historically, NO_TRANSFORM was a property of instructions,
   but it should apply to literals under certain circumstances.  */
   but it should apply to literals under certain circumstances.  */
#define XTENSA_PROP_NO_TRANSFORM        0x00000100
#define XTENSA_PROP_NO_TRANSFORM        0x00000100
 
 
/*  Branch target alignment information.  This transmits information
/*  Branch target alignment information.  This transmits information
    to the linker optimization about the priority of aligning a
    to the linker optimization about the priority of aligning a
    particular block for branch target alignment: None, low priority,
    particular block for branch target alignment: None, low priority,
    high priority, or required.  These only need to be checked in
    high priority, or required.  These only need to be checked in
    instruction blocks marked as XTENSA_PROP_INSN_BRANCH_TARGET.
    instruction blocks marked as XTENSA_PROP_INSN_BRANCH_TARGET.
    Common usage is
    Common usage is
 
 
    switch (GET_XTENSA_PROP_BT_ALIGN (flags))
    switch (GET_XTENSA_PROP_BT_ALIGN (flags))
    case XTENSA_PROP_BT_ALIGN_NONE:
    case XTENSA_PROP_BT_ALIGN_NONE:
    case XTENSA_PROP_BT_ALIGN_LOW:
    case XTENSA_PROP_BT_ALIGN_LOW:
    case XTENSA_PROP_BT_ALIGN_HIGH:
    case XTENSA_PROP_BT_ALIGN_HIGH:
    case XTENSA_PROP_BT_ALIGN_REQUIRE:
    case XTENSA_PROP_BT_ALIGN_REQUIRE:
*/
*/
#define XTENSA_PROP_BT_ALIGN_MASK       0x00000600
#define XTENSA_PROP_BT_ALIGN_MASK       0x00000600
 
 
/* No branch target alignment.  */
/* No branch target alignment.  */
#define XTENSA_PROP_BT_ALIGN_NONE       0x0
#define XTENSA_PROP_BT_ALIGN_NONE       0x0
/* Low priority branch target alignment.  */
/* Low priority branch target alignment.  */
#define XTENSA_PROP_BT_ALIGN_LOW        0x1
#define XTENSA_PROP_BT_ALIGN_LOW        0x1
/* High priority branch target alignment.  */
/* High priority branch target alignment.  */
#define XTENSA_PROP_BT_ALIGN_HIGH       0x2
#define XTENSA_PROP_BT_ALIGN_HIGH       0x2
/* Required branch target alignment.  */
/* Required branch target alignment.  */
#define XTENSA_PROP_BT_ALIGN_REQUIRE    0x3
#define XTENSA_PROP_BT_ALIGN_REQUIRE    0x3
 
 
#define GET_XTENSA_PROP_BT_ALIGN(flag) \
#define GET_XTENSA_PROP_BT_ALIGN(flag) \
  (((unsigned) ((flag) & (XTENSA_PROP_BT_ALIGN_MASK))) >> 9)
  (((unsigned) ((flag) & (XTENSA_PROP_BT_ALIGN_MASK))) >> 9)
#define SET_XTENSA_PROP_BT_ALIGN(flag, align) \
#define SET_XTENSA_PROP_BT_ALIGN(flag, align) \
  (((flag) & (~XTENSA_PROP_BT_ALIGN_MASK)) | \
  (((flag) & (~XTENSA_PROP_BT_ALIGN_MASK)) | \
    (((align) << 9) & XTENSA_PROP_BT_ALIGN_MASK))
    (((align) << 9) & XTENSA_PROP_BT_ALIGN_MASK))
 
 
 
 
/* Alignment is specified in the block BEFORE the one that needs
/* Alignment is specified in the block BEFORE the one that needs
   alignment.  Up to 5 bits.  Use GET_XTENSA_PROP_ALIGNMENT(flags) to
   alignment.  Up to 5 bits.  Use GET_XTENSA_PROP_ALIGNMENT(flags) to
   get the required alignment specified as a power of 2.  Use
   get the required alignment specified as a power of 2.  Use
   SET_XTENSA_PROP_ALIGNMENT(flags, pow2) to set the required
   SET_XTENSA_PROP_ALIGNMENT(flags, pow2) to set the required
   alignment.  Be careful of side effects since the SET will evaluate
   alignment.  Be careful of side effects since the SET will evaluate
   flags twice.  Also, note that the SIZE of a block in the property
   flags twice.  Also, note that the SIZE of a block in the property
   table does not include the alignment size, so the alignment fill
   table does not include the alignment size, so the alignment fill
   must be calculated to determine if two blocks are contiguous.
   must be calculated to determine if two blocks are contiguous.
   TEXT_ALIGN is not currently implemented but is a placeholder for a
   TEXT_ALIGN is not currently implemented but is a placeholder for a
   possible future implementation.  */
   possible future implementation.  */
 
 
#define XTENSA_PROP_ALIGN               0x00000800
#define XTENSA_PROP_ALIGN               0x00000800
 
 
#define XTENSA_PROP_ALIGNMENT_MASK      0x0001f000
#define XTENSA_PROP_ALIGNMENT_MASK      0x0001f000
 
 
#define GET_XTENSA_PROP_ALIGNMENT(flag) \
#define GET_XTENSA_PROP_ALIGNMENT(flag) \
  (((unsigned) ((flag) & (XTENSA_PROP_ALIGNMENT_MASK))) >> 12)
  (((unsigned) ((flag) & (XTENSA_PROP_ALIGNMENT_MASK))) >> 12)
#define SET_XTENSA_PROP_ALIGNMENT(flag, align) \
#define SET_XTENSA_PROP_ALIGNMENT(flag, align) \
  (((flag) & (~XTENSA_PROP_ALIGNMENT_MASK)) | \
  (((flag) & (~XTENSA_PROP_ALIGNMENT_MASK)) | \
    (((align) << 12) & XTENSA_PROP_ALIGNMENT_MASK))
    (((align) << 12) & XTENSA_PROP_ALIGNMENT_MASK))
 
 
#define XTENSA_PROP_INSN_ABSLIT 0x00020000
#define XTENSA_PROP_INSN_ABSLIT 0x00020000
 
 
 
 
/* Structure for saving instruction and alignment per-fragment data
/* Structure for saving instruction and alignment per-fragment data
   that will be written to the object file.  This structure is
   that will be written to the object file.  This structure is
   equivalent to the actual data that will be written out to the file
   equivalent to the actual data that will be written out to the file
   but is easier to use.   We provide a conversion to file flags
   but is easier to use.   We provide a conversion to file flags
   in frag_flags_to_number.  */
   in frag_flags_to_number.  */
 
 
typedef struct frag_flags_struct frag_flags;
typedef struct frag_flags_struct frag_flags;
 
 
struct frag_flags_struct
struct frag_flags_struct
{
{
  /* is_literal should only be used after xtensa_move_literals.
  /* is_literal should only be used after xtensa_move_literals.
     If you need to check if you are generating a literal fragment,
     If you need to check if you are generating a literal fragment,
     then use the generating_literals global.  */
     then use the generating_literals global.  */
 
 
  unsigned is_literal : 1;
  unsigned is_literal : 1;
  unsigned is_insn : 1;
  unsigned is_insn : 1;
  unsigned is_data : 1;
  unsigned is_data : 1;
  unsigned is_unreachable : 1;
  unsigned is_unreachable : 1;
 
 
  /* is_specific_opcode implies no_transform.  */
  /* is_specific_opcode implies no_transform.  */
  unsigned is_no_transform : 1;
  unsigned is_no_transform : 1;
 
 
  struct
  struct
  {
  {
    unsigned is_loop_target : 1;
    unsigned is_loop_target : 1;
    unsigned is_branch_target : 1; /* Branch targets have a priority.  */
    unsigned is_branch_target : 1; /* Branch targets have a priority.  */
    unsigned bt_align_priority : 2;
    unsigned bt_align_priority : 2;
 
 
    unsigned is_no_density : 1;
    unsigned is_no_density : 1;
    /* no_longcalls flag does not need to be placed in the object file.  */
    /* no_longcalls flag does not need to be placed in the object file.  */
 
 
    unsigned is_no_reorder : 1;
    unsigned is_no_reorder : 1;
 
 
    /* Uses absolute literal addressing for l32r.  */
    /* Uses absolute literal addressing for l32r.  */
    unsigned is_abslit : 1;
    unsigned is_abslit : 1;
  } insn;
  } insn;
  unsigned is_align : 1;
  unsigned is_align : 1;
  unsigned alignment : 5;
  unsigned alignment : 5;
};
};
 
 
 
 
/* Structure for saving information about a block of property data
/* Structure for saving information about a block of property data
   for frags that have the same flags.  */
   for frags that have the same flags.  */
struct xtensa_block_info_struct
struct xtensa_block_info_struct
{
{
  segT sec;
  segT sec;
  bfd_vma offset;
  bfd_vma offset;
  size_t size;
  size_t size;
  frag_flags flags;
  frag_flags flags;
  struct xtensa_block_info_struct *next;
  struct xtensa_block_info_struct *next;
};
};
 
 
 
 
/* Structure for saving the current state before emitting literals.  */
/* Structure for saving the current state before emitting literals.  */
typedef struct emit_state_struct
typedef struct emit_state_struct
{
{
  const char *name;
  const char *name;
  segT now_seg;
  segT now_seg;
  subsegT now_subseg;
  subsegT now_subseg;
  int generating_literals;
  int generating_literals;
} emit_state;
} emit_state;
 
 
 
 
/* Opcode placement information */
/* Opcode placement information */
 
 
typedef unsigned long long bitfield;
typedef unsigned long long bitfield;
#define bit_is_set(bit, bf)     ((bf) & (0x01ll << (bit)))
#define bit_is_set(bit, bf)     ((bf) & (0x01ll << (bit)))
#define set_bit(bit, bf)        ((bf) |= (0x01ll << (bit)))
#define set_bit(bit, bf)        ((bf) |= (0x01ll << (bit)))
#define clear_bit(bit, bf)      ((bf) &= ~(0x01ll << (bit)))
#define clear_bit(bit, bf)      ((bf) &= ~(0x01ll << (bit)))
 
 
#define MAX_FORMATS 32
#define MAX_FORMATS 32
 
 
typedef struct op_placement_info_struct
typedef struct op_placement_info_struct
{
{
  int num_formats;
  int num_formats;
  /* A number describing how restrictive the issue is for this
  /* A number describing how restrictive the issue is for this
     opcode.  For example, an opcode that fits lots of different
     opcode.  For example, an opcode that fits lots of different
     formats has a high freedom, as does an opcode that fits
     formats has a high freedom, as does an opcode that fits
     only one format but many slots in that format.  The most
     only one format but many slots in that format.  The most
     restrictive is the opcode that fits only one slot in one
     restrictive is the opcode that fits only one slot in one
     format.  */
     format.  */
  int issuef;
  int issuef;
  xtensa_format narrowest;
  xtensa_format narrowest;
  char narrowest_size;
  char narrowest_size;
  char narrowest_slot;
  char narrowest_slot;
 
 
  /* formats is a bitfield with the Nth bit set
  /* formats is a bitfield with the Nth bit set
     if the opcode fits in the Nth xtensa_format.  */
     if the opcode fits in the Nth xtensa_format.  */
  bitfield formats;
  bitfield formats;
 
 
  /* slots[N]'s Mth bit is set if the op fits in the
  /* slots[N]'s Mth bit is set if the op fits in the
     Mth slot of the Nth xtensa_format.  */
     Mth slot of the Nth xtensa_format.  */
  bitfield slots[MAX_FORMATS];
  bitfield slots[MAX_FORMATS];
 
 
  /* A count of the number of slots in a given format
  /* A count of the number of slots in a given format
     an op can fit (i.e., the bitcount of the slot field above).  */
     an op can fit (i.e., the bitcount of the slot field above).  */
  char slots_in_format[MAX_FORMATS];
  char slots_in_format[MAX_FORMATS];
 
 
} op_placement_info, *op_placement_info_table;
} op_placement_info, *op_placement_info_table;
 
 
op_placement_info_table op_placement_table;
op_placement_info_table op_placement_table;
 
 
 
 
/* Extra expression types.  */
/* Extra expression types.  */
 
 
#define O_pltrel        O_md1   /* like O_symbol but use a PLT reloc */
#define O_pltrel        O_md1   /* like O_symbol but use a PLT reloc */
#define O_hi16          O_md2   /* use high 16 bits of symbolic value */
#define O_hi16          O_md2   /* use high 16 bits of symbolic value */
#define O_lo16          O_md3   /* use low 16 bits of symbolic value */
#define O_lo16          O_md3   /* use low 16 bits of symbolic value */
#define O_pcrel         O_md4   /* value is a PC-relative offset */
#define O_pcrel         O_md4   /* value is a PC-relative offset */
#define O_tlsfunc       O_md5   /* TLS_FUNC/TLSDESC_FN relocation */
#define O_tlsfunc       O_md5   /* TLS_FUNC/TLSDESC_FN relocation */
#define O_tlsarg        O_md6   /* TLS_ARG/TLSDESC_ARG relocation */
#define O_tlsarg        O_md6   /* TLS_ARG/TLSDESC_ARG relocation */
#define O_tlscall       O_md7   /* TLS_CALL relocation */
#define O_tlscall       O_md7   /* TLS_CALL relocation */
#define O_tpoff         O_md8   /* TPOFF relocation */
#define O_tpoff         O_md8   /* TPOFF relocation */
#define O_dtpoff        O_md9   /* DTPOFF relocation */
#define O_dtpoff        O_md9   /* DTPOFF relocation */
 
 
struct suffix_reloc_map
struct suffix_reloc_map
{
{
  char *suffix;
  char *suffix;
  int length;
  int length;
  bfd_reloc_code_real_type reloc;
  bfd_reloc_code_real_type reloc;
  unsigned char operator;
  unsigned char operator;
};
};
 
 
#define SUFFIX_MAP(str, reloc, op) { str, sizeof (str) - 1, reloc, op }
#define SUFFIX_MAP(str, reloc, op) { str, sizeof (str) - 1, reloc, op }
 
 
static struct suffix_reloc_map suffix_relocs[] =
static struct suffix_reloc_map suffix_relocs[] =
{
{
  SUFFIX_MAP ("l",      BFD_RELOC_LO16,                 O_lo16),
  SUFFIX_MAP ("l",      BFD_RELOC_LO16,                 O_lo16),
  SUFFIX_MAP ("h",      BFD_RELOC_HI16,                 O_hi16),
  SUFFIX_MAP ("h",      BFD_RELOC_HI16,                 O_hi16),
  SUFFIX_MAP ("plt",    BFD_RELOC_XTENSA_PLT,           O_pltrel),
  SUFFIX_MAP ("plt",    BFD_RELOC_XTENSA_PLT,           O_pltrel),
  SUFFIX_MAP ("pcrel",  BFD_RELOC_32_PCREL,             O_pcrel),
  SUFFIX_MAP ("pcrel",  BFD_RELOC_32_PCREL,             O_pcrel),
  SUFFIX_MAP ("tlsfunc", BFD_RELOC_XTENSA_TLS_FUNC,     O_tlsfunc),
  SUFFIX_MAP ("tlsfunc", BFD_RELOC_XTENSA_TLS_FUNC,     O_tlsfunc),
  SUFFIX_MAP ("tlsarg", BFD_RELOC_XTENSA_TLS_ARG,       O_tlsarg),
  SUFFIX_MAP ("tlsarg", BFD_RELOC_XTENSA_TLS_ARG,       O_tlsarg),
  SUFFIX_MAP ("tlscall", BFD_RELOC_XTENSA_TLS_CALL,     O_tlscall),
  SUFFIX_MAP ("tlscall", BFD_RELOC_XTENSA_TLS_CALL,     O_tlscall),
  SUFFIX_MAP ("tpoff",  BFD_RELOC_XTENSA_TLS_TPOFF,     O_tpoff),
  SUFFIX_MAP ("tpoff",  BFD_RELOC_XTENSA_TLS_TPOFF,     O_tpoff),
  SUFFIX_MAP ("dtpoff", BFD_RELOC_XTENSA_TLS_DTPOFF,    O_dtpoff),
  SUFFIX_MAP ("dtpoff", BFD_RELOC_XTENSA_TLS_DTPOFF,    O_dtpoff),
  { (char *) 0, 0,        BFD_RELOC_UNUSED,               0 }
  { (char *) 0, 0,        BFD_RELOC_UNUSED,               0 }
};
};
 
 
 
 
/* Directives.  */
/* Directives.  */
 
 
typedef enum
typedef enum
{
{
  directive_none = 0,
  directive_none = 0,
  directive_literal,
  directive_literal,
  directive_density,
  directive_density,
  directive_transform,
  directive_transform,
  directive_freeregs,
  directive_freeregs,
  directive_longcalls,
  directive_longcalls,
  directive_literal_prefix,
  directive_literal_prefix,
  directive_schedule,
  directive_schedule,
  directive_absolute_literals,
  directive_absolute_literals,
  directive_last_directive
  directive_last_directive
} directiveE;
} directiveE;
 
 
typedef struct
typedef struct
{
{
  const char *name;
  const char *name;
  bfd_boolean can_be_negated;
  bfd_boolean can_be_negated;
} directive_infoS;
} directive_infoS;
 
 
const directive_infoS directive_info[] =
const directive_infoS directive_info[] =
{
{
  { "none",             FALSE },
  { "none",             FALSE },
  { "literal",          FALSE },
  { "literal",          FALSE },
  { "density",          TRUE },
  { "density",          TRUE },
  { "transform",        TRUE },
  { "transform",        TRUE },
  { "freeregs",         FALSE },
  { "freeregs",         FALSE },
  { "longcalls",        TRUE },
  { "longcalls",        TRUE },
  { "literal_prefix",   FALSE },
  { "literal_prefix",   FALSE },
  { "schedule",         TRUE },
  { "schedule",         TRUE },
  { "absolute-literals", TRUE }
  { "absolute-literals", TRUE }
};
};
 
 
bfd_boolean directive_state[] =
bfd_boolean directive_state[] =
{
{
  FALSE,                        /* none */
  FALSE,                        /* none */
  FALSE,                        /* literal */
  FALSE,                        /* literal */
#if !XCHAL_HAVE_DENSITY
#if !XCHAL_HAVE_DENSITY
  FALSE,                        /* density */
  FALSE,                        /* density */
#else
#else
  TRUE,                         /* density */
  TRUE,                         /* density */
#endif
#endif
  TRUE,                         /* transform */
  TRUE,                         /* transform */
  FALSE,                        /* freeregs */
  FALSE,                        /* freeregs */
  FALSE,                        /* longcalls */
  FALSE,                        /* longcalls */
  FALSE,                        /* literal_prefix */
  FALSE,                        /* literal_prefix */
  FALSE,                        /* schedule */
  FALSE,                        /* schedule */
#if XSHAL_USE_ABSOLUTE_LITERALS
#if XSHAL_USE_ABSOLUTE_LITERALS
  TRUE                          /* absolute_literals */
  TRUE                          /* absolute_literals */
#else
#else
  FALSE                         /* absolute_literals */
  FALSE                         /* absolute_literals */
#endif
#endif
};
};
 
 
 
 
/* Directive functions.  */
/* Directive functions.  */
 
 
static void xtensa_begin_directive (int);
static void xtensa_begin_directive (int);
static void xtensa_end_directive (int);
static void xtensa_end_directive (int);
static void xtensa_literal_prefix (void);
static void xtensa_literal_prefix (void);
static void xtensa_literal_position (int);
static void xtensa_literal_position (int);
static void xtensa_literal_pseudo (int);
static void xtensa_literal_pseudo (int);
static void xtensa_frequency_pseudo (int);
static void xtensa_frequency_pseudo (int);
static void xtensa_elf_cons (int);
static void xtensa_elf_cons (int);
static void xtensa_leb128 (int);
static void xtensa_leb128 (int);
 
 
/* Parsing and Idiom Translation.  */
/* Parsing and Idiom Translation.  */
 
 
static bfd_reloc_code_real_type xtensa_elf_suffix (char **, expressionS *);
static bfd_reloc_code_real_type xtensa_elf_suffix (char **, expressionS *);
 
 
/* Various Other Internal Functions.  */
/* Various Other Internal Functions.  */
 
 
extern bfd_boolean xg_is_single_relaxable_insn (TInsn *, TInsn *, bfd_boolean);
extern bfd_boolean xg_is_single_relaxable_insn (TInsn *, TInsn *, bfd_boolean);
static bfd_boolean xg_build_to_insn (TInsn *, TInsn *, BuildInstr *);
static bfd_boolean xg_build_to_insn (TInsn *, TInsn *, BuildInstr *);
static void xtensa_mark_literal_pool_location (void);
static void xtensa_mark_literal_pool_location (void);
static addressT get_expanded_loop_offset (xtensa_opcode);
static addressT get_expanded_loop_offset (xtensa_opcode);
static fragS *get_literal_pool_location (segT);
static fragS *get_literal_pool_location (segT);
static void set_literal_pool_location (segT, fragS *);
static void set_literal_pool_location (segT, fragS *);
static void xtensa_set_frag_assembly_state (fragS *);
static void xtensa_set_frag_assembly_state (fragS *);
static void finish_vinsn (vliw_insn *);
static void finish_vinsn (vliw_insn *);
static bfd_boolean emit_single_op (TInsn *);
static bfd_boolean emit_single_op (TInsn *);
static int total_frag_text_expansion (fragS *);
static int total_frag_text_expansion (fragS *);
 
 
/* Alignment Functions.  */
/* Alignment Functions.  */
 
 
static int get_text_align_power (unsigned);
static int get_text_align_power (unsigned);
static int get_text_align_max_fill_size (int, bfd_boolean, bfd_boolean);
static int get_text_align_max_fill_size (int, bfd_boolean, bfd_boolean);
static int branch_align_power (segT);
static int branch_align_power (segT);
 
 
/* Helpers for xtensa_relax_frag().  */
/* Helpers for xtensa_relax_frag().  */
 
 
static long relax_frag_add_nop (fragS *);
static long relax_frag_add_nop (fragS *);
 
 
/* Accessors for additional per-subsegment information.  */
/* Accessors for additional per-subsegment information.  */
 
 
static unsigned get_last_insn_flags (segT, subsegT);
static unsigned get_last_insn_flags (segT, subsegT);
static void set_last_insn_flags (segT, subsegT, unsigned, bfd_boolean);
static void set_last_insn_flags (segT, subsegT, unsigned, bfd_boolean);
static float get_subseg_total_freq (segT, subsegT);
static float get_subseg_total_freq (segT, subsegT);
static float get_subseg_target_freq (segT, subsegT);
static float get_subseg_target_freq (segT, subsegT);
static void set_subseg_freq (segT, subsegT, float, float);
static void set_subseg_freq (segT, subsegT, float, float);
 
 
/* Segment list functions.  */
/* Segment list functions.  */
 
 
static void xtensa_move_literals (void);
static void xtensa_move_literals (void);
static void xtensa_reorder_segments (void);
static void xtensa_reorder_segments (void);
static void xtensa_switch_to_literal_fragment (emit_state *);
static void xtensa_switch_to_literal_fragment (emit_state *);
static void xtensa_switch_to_non_abs_literal_fragment (emit_state *);
static void xtensa_switch_to_non_abs_literal_fragment (emit_state *);
static void xtensa_switch_section_emit_state (emit_state *, segT, subsegT);
static void xtensa_switch_section_emit_state (emit_state *, segT, subsegT);
static void xtensa_restore_emit_state (emit_state *);
static void xtensa_restore_emit_state (emit_state *);
static segT cache_literal_section (bfd_boolean);
static segT cache_literal_section (bfd_boolean);
 
 
/* Import from elf32-xtensa.c in BFD library.  */
/* Import from elf32-xtensa.c in BFD library.  */
 
 
extern asection *xtensa_make_property_section (asection *, const char *);
extern asection *xtensa_make_property_section (asection *, const char *);
 
 
/* op_placement_info functions.  */
/* op_placement_info functions.  */
 
 
static void init_op_placement_info_table (void);
static void init_op_placement_info_table (void);
extern bfd_boolean opcode_fits_format_slot (xtensa_opcode, xtensa_format, int);
extern bfd_boolean opcode_fits_format_slot (xtensa_opcode, xtensa_format, int);
static int xg_get_single_size (xtensa_opcode);
static int xg_get_single_size (xtensa_opcode);
static xtensa_format xg_get_single_format (xtensa_opcode);
static xtensa_format xg_get_single_format (xtensa_opcode);
static int xg_get_single_slot (xtensa_opcode);
static int xg_get_single_slot (xtensa_opcode);
 
 
/* TInsn and IStack functions.  */
/* TInsn and IStack functions.  */
 
 
static bfd_boolean tinsn_has_symbolic_operands (const TInsn *);
static bfd_boolean tinsn_has_symbolic_operands (const TInsn *);
static bfd_boolean tinsn_has_invalid_symbolic_operands (const TInsn *);
static bfd_boolean tinsn_has_invalid_symbolic_operands (const TInsn *);
static bfd_boolean tinsn_has_complex_operands (const TInsn *);
static bfd_boolean tinsn_has_complex_operands (const TInsn *);
static bfd_boolean tinsn_to_insnbuf (TInsn *, xtensa_insnbuf);
static bfd_boolean tinsn_to_insnbuf (TInsn *, xtensa_insnbuf);
static bfd_boolean tinsn_check_arguments (const TInsn *);
static bfd_boolean tinsn_check_arguments (const TInsn *);
static void tinsn_from_chars (TInsn *, char *, int);
static void tinsn_from_chars (TInsn *, char *, int);
static void tinsn_immed_from_frag (TInsn *, fragS *, int);
static void tinsn_immed_from_frag (TInsn *, fragS *, int);
static int get_num_stack_text_bytes (IStack *);
static int get_num_stack_text_bytes (IStack *);
static int get_num_stack_literal_bytes (IStack *);
static int get_num_stack_literal_bytes (IStack *);
 
 
/* vliw_insn functions.  */
/* vliw_insn functions.  */
 
 
static void xg_init_vinsn (vliw_insn *);
static void xg_init_vinsn (vliw_insn *);
static void xg_copy_vinsn (vliw_insn *, vliw_insn *);
static void xg_copy_vinsn (vliw_insn *, vliw_insn *);
static void xg_clear_vinsn (vliw_insn *);
static void xg_clear_vinsn (vliw_insn *);
static bfd_boolean vinsn_has_specific_opcodes (vliw_insn *);
static bfd_boolean vinsn_has_specific_opcodes (vliw_insn *);
static void xg_free_vinsn (vliw_insn *);
static void xg_free_vinsn (vliw_insn *);
static bfd_boolean vinsn_to_insnbuf
static bfd_boolean vinsn_to_insnbuf
  (vliw_insn *, char *, fragS *, bfd_boolean);
  (vliw_insn *, char *, fragS *, bfd_boolean);
static void vinsn_from_chars (vliw_insn *, char *);
static void vinsn_from_chars (vliw_insn *, char *);
 
 
/* Expression Utilities.  */
/* Expression Utilities.  */
 
 
bfd_boolean expr_is_const (const expressionS *);
bfd_boolean expr_is_const (const expressionS *);
offsetT get_expr_const (const expressionS *);
offsetT get_expr_const (const expressionS *);
void set_expr_const (expressionS *, offsetT);
void set_expr_const (expressionS *, offsetT);
bfd_boolean expr_is_register (const expressionS *);
bfd_boolean expr_is_register (const expressionS *);
offsetT get_expr_register (const expressionS *);
offsetT get_expr_register (const expressionS *);
void set_expr_symbol_offset (expressionS *, symbolS *, offsetT);
void set_expr_symbol_offset (expressionS *, symbolS *, offsetT);
bfd_boolean expr_is_equal (expressionS *, expressionS *);
bfd_boolean expr_is_equal (expressionS *, expressionS *);
static void copy_expr (expressionS *, const expressionS *);
static void copy_expr (expressionS *, const expressionS *);
 
 
/* Section renaming.  */
/* Section renaming.  */
 
 
static void build_section_rename (const char *);
static void build_section_rename (const char *);
 
 
 
 
/* ISA imported from bfd.  */
/* ISA imported from bfd.  */
extern xtensa_isa xtensa_default_isa;
extern xtensa_isa xtensa_default_isa;
 
 
extern int target_big_endian;
extern int target_big_endian;
 
 
static xtensa_opcode xtensa_addi_opcode;
static xtensa_opcode xtensa_addi_opcode;
static xtensa_opcode xtensa_addmi_opcode;
static xtensa_opcode xtensa_addmi_opcode;
static xtensa_opcode xtensa_call0_opcode;
static xtensa_opcode xtensa_call0_opcode;
static xtensa_opcode xtensa_call4_opcode;
static xtensa_opcode xtensa_call4_opcode;
static xtensa_opcode xtensa_call8_opcode;
static xtensa_opcode xtensa_call8_opcode;
static xtensa_opcode xtensa_call12_opcode;
static xtensa_opcode xtensa_call12_opcode;
static xtensa_opcode xtensa_callx0_opcode;
static xtensa_opcode xtensa_callx0_opcode;
static xtensa_opcode xtensa_callx4_opcode;
static xtensa_opcode xtensa_callx4_opcode;
static xtensa_opcode xtensa_callx8_opcode;
static xtensa_opcode xtensa_callx8_opcode;
static xtensa_opcode xtensa_callx12_opcode;
static xtensa_opcode xtensa_callx12_opcode;
static xtensa_opcode xtensa_const16_opcode;
static xtensa_opcode xtensa_const16_opcode;
static xtensa_opcode xtensa_entry_opcode;
static xtensa_opcode xtensa_entry_opcode;
static xtensa_opcode xtensa_extui_opcode;
static xtensa_opcode xtensa_extui_opcode;
static xtensa_opcode xtensa_movi_opcode;
static xtensa_opcode xtensa_movi_opcode;
static xtensa_opcode xtensa_movi_n_opcode;
static xtensa_opcode xtensa_movi_n_opcode;
static xtensa_opcode xtensa_isync_opcode;
static xtensa_opcode xtensa_isync_opcode;
static xtensa_opcode xtensa_j_opcode;
static xtensa_opcode xtensa_j_opcode;
static xtensa_opcode xtensa_jx_opcode;
static xtensa_opcode xtensa_jx_opcode;
static xtensa_opcode xtensa_l32r_opcode;
static xtensa_opcode xtensa_l32r_opcode;
static xtensa_opcode xtensa_loop_opcode;
static xtensa_opcode xtensa_loop_opcode;
static xtensa_opcode xtensa_loopnez_opcode;
static xtensa_opcode xtensa_loopnez_opcode;
static xtensa_opcode xtensa_loopgtz_opcode;
static xtensa_opcode xtensa_loopgtz_opcode;
static xtensa_opcode xtensa_nop_opcode;
static xtensa_opcode xtensa_nop_opcode;
static xtensa_opcode xtensa_nop_n_opcode;
static xtensa_opcode xtensa_nop_n_opcode;
static xtensa_opcode xtensa_or_opcode;
static xtensa_opcode xtensa_or_opcode;
static xtensa_opcode xtensa_ret_opcode;
static xtensa_opcode xtensa_ret_opcode;
static xtensa_opcode xtensa_ret_n_opcode;
static xtensa_opcode xtensa_ret_n_opcode;
static xtensa_opcode xtensa_retw_opcode;
static xtensa_opcode xtensa_retw_opcode;
static xtensa_opcode xtensa_retw_n_opcode;
static xtensa_opcode xtensa_retw_n_opcode;
static xtensa_opcode xtensa_rsr_lcount_opcode;
static xtensa_opcode xtensa_rsr_lcount_opcode;
static xtensa_opcode xtensa_waiti_opcode;
static xtensa_opcode xtensa_waiti_opcode;
static int config_max_slots = 0;
static int config_max_slots = 0;
 
 


/* Command-line Options.  */
/* Command-line Options.  */
 
 
bfd_boolean use_literal_section = TRUE;
bfd_boolean use_literal_section = TRUE;
enum flix_level produce_flix = FLIX_ALL;
enum flix_level produce_flix = FLIX_ALL;
static bfd_boolean align_targets = TRUE;
static bfd_boolean align_targets = TRUE;
static bfd_boolean warn_unaligned_branch_targets = FALSE;
static bfd_boolean warn_unaligned_branch_targets = FALSE;
static bfd_boolean has_a0_b_retw = FALSE;
static bfd_boolean has_a0_b_retw = FALSE;
static bfd_boolean workaround_a0_b_retw = FALSE;
static bfd_boolean workaround_a0_b_retw = FALSE;
static bfd_boolean workaround_b_j_loop_end = FALSE;
static bfd_boolean workaround_b_j_loop_end = FALSE;
static bfd_boolean workaround_short_loop = FALSE;
static bfd_boolean workaround_short_loop = FALSE;
static bfd_boolean maybe_has_short_loop = FALSE;
static bfd_boolean maybe_has_short_loop = FALSE;
static bfd_boolean workaround_close_loop_end = FALSE;
static bfd_boolean workaround_close_loop_end = FALSE;
static bfd_boolean maybe_has_close_loop_end = FALSE;
static bfd_boolean maybe_has_close_loop_end = FALSE;
static bfd_boolean enforce_three_byte_loop_align = FALSE;
static bfd_boolean enforce_three_byte_loop_align = FALSE;
 
 
/* When workaround_short_loops is TRUE, all loops with early exits must
/* When workaround_short_loops is TRUE, all loops with early exits must
   have at least 3 instructions.  workaround_all_short_loops is a modifier
   have at least 3 instructions.  workaround_all_short_loops is a modifier
   to the workaround_short_loop flag.  In addition to the
   to the workaround_short_loop flag.  In addition to the
   workaround_short_loop actions, all straightline loopgtz and loopnez
   workaround_short_loop actions, all straightline loopgtz and loopnez
   must have at least 3 instructions.  */
   must have at least 3 instructions.  */
 
 
static bfd_boolean workaround_all_short_loops = FALSE;
static bfd_boolean workaround_all_short_loops = FALSE;
 
 
 
 
static void
static void
xtensa_setup_hw_workarounds (int earliest, int latest)
xtensa_setup_hw_workarounds (int earliest, int latest)
{
{
  if (earliest > latest)
  if (earliest > latest)
    as_fatal (_("illegal range of target hardware versions"));
    as_fatal (_("illegal range of target hardware versions"));
 
 
  /* Enable all workarounds for pre-T1050.0 hardware.  */
  /* Enable all workarounds for pre-T1050.0 hardware.  */
  if (earliest < 105000 || latest < 105000)
  if (earliest < 105000 || latest < 105000)
    {
    {
      workaround_a0_b_retw |= TRUE;
      workaround_a0_b_retw |= TRUE;
      workaround_b_j_loop_end |= TRUE;
      workaround_b_j_loop_end |= TRUE;
      workaround_short_loop |= TRUE;
      workaround_short_loop |= TRUE;
      workaround_close_loop_end |= TRUE;
      workaround_close_loop_end |= TRUE;
      workaround_all_short_loops |= TRUE;
      workaround_all_short_loops |= TRUE;
      enforce_three_byte_loop_align = TRUE;
      enforce_three_byte_loop_align = TRUE;
    }
    }
}
}
 
 
 
 
enum
enum
{
{
  option_density = OPTION_MD_BASE,
  option_density = OPTION_MD_BASE,
  option_no_density,
  option_no_density,
 
 
  option_flix,
  option_flix,
  option_no_generate_flix,
  option_no_generate_flix,
  option_no_flix,
  option_no_flix,
 
 
  option_relax,
  option_relax,
  option_no_relax,
  option_no_relax,
 
 
  option_link_relax,
  option_link_relax,
  option_no_link_relax,
  option_no_link_relax,
 
 
  option_generics,
  option_generics,
  option_no_generics,
  option_no_generics,
 
 
  option_transform,
  option_transform,
  option_no_transform,
  option_no_transform,
 
 
  option_text_section_literals,
  option_text_section_literals,
  option_no_text_section_literals,
  option_no_text_section_literals,
 
 
  option_absolute_literals,
  option_absolute_literals,
  option_no_absolute_literals,
  option_no_absolute_literals,
 
 
  option_align_targets,
  option_align_targets,
  option_no_align_targets,
  option_no_align_targets,
 
 
  option_warn_unaligned_targets,
  option_warn_unaligned_targets,
 
 
  option_longcalls,
  option_longcalls,
  option_no_longcalls,
  option_no_longcalls,
 
 
  option_workaround_a0_b_retw,
  option_workaround_a0_b_retw,
  option_no_workaround_a0_b_retw,
  option_no_workaround_a0_b_retw,
 
 
  option_workaround_b_j_loop_end,
  option_workaround_b_j_loop_end,
  option_no_workaround_b_j_loop_end,
  option_no_workaround_b_j_loop_end,
 
 
  option_workaround_short_loop,
  option_workaround_short_loop,
  option_no_workaround_short_loop,
  option_no_workaround_short_loop,
 
 
  option_workaround_all_short_loops,
  option_workaround_all_short_loops,
  option_no_workaround_all_short_loops,
  option_no_workaround_all_short_loops,
 
 
  option_workaround_close_loop_end,
  option_workaround_close_loop_end,
  option_no_workaround_close_loop_end,
  option_no_workaround_close_loop_end,
 
 
  option_no_workarounds,
  option_no_workarounds,
 
 
  option_rename_section_name,
  option_rename_section_name,
 
 
  option_prefer_l32r,
  option_prefer_l32r,
  option_prefer_const16,
  option_prefer_const16,
 
 
  option_target_hardware
  option_target_hardware
};
};
 
 
const char *md_shortopts = "";
const char *md_shortopts = "";
 
 
struct option md_longopts[] =
struct option md_longopts[] =
{
{
  { "density", no_argument, NULL, option_density },
  { "density", no_argument, NULL, option_density },
  { "no-density", no_argument, NULL, option_no_density },
  { "no-density", no_argument, NULL, option_no_density },
 
 
  { "flix", no_argument, NULL, option_flix },
  { "flix", no_argument, NULL, option_flix },
  { "no-generate-flix", no_argument, NULL, option_no_generate_flix },
  { "no-generate-flix", no_argument, NULL, option_no_generate_flix },
  { "no-allow-flix", no_argument, NULL, option_no_flix },
  { "no-allow-flix", no_argument, NULL, option_no_flix },
 
 
  /* Both "relax" and "generics" are deprecated and treated as equivalent
  /* Both "relax" and "generics" are deprecated and treated as equivalent
     to the "transform" option.  */
     to the "transform" option.  */
  { "relax", no_argument, NULL, option_relax },
  { "relax", no_argument, NULL, option_relax },
  { "no-relax", no_argument, NULL, option_no_relax },
  { "no-relax", no_argument, NULL, option_no_relax },
  { "generics", no_argument, NULL, option_generics },
  { "generics", no_argument, NULL, option_generics },
  { "no-generics", no_argument, NULL, option_no_generics },
  { "no-generics", no_argument, NULL, option_no_generics },
 
 
  { "transform", no_argument, NULL, option_transform },
  { "transform", no_argument, NULL, option_transform },
  { "no-transform", no_argument, NULL, option_no_transform },
  { "no-transform", no_argument, NULL, option_no_transform },
  { "text-section-literals", no_argument, NULL, option_text_section_literals },
  { "text-section-literals", no_argument, NULL, option_text_section_literals },
  { "no-text-section-literals", no_argument, NULL,
  { "no-text-section-literals", no_argument, NULL,
    option_no_text_section_literals },
    option_no_text_section_literals },
  { "absolute-literals", no_argument, NULL, option_absolute_literals },
  { "absolute-literals", no_argument, NULL, option_absolute_literals },
  { "no-absolute-literals", no_argument, NULL, option_no_absolute_literals },
  { "no-absolute-literals", no_argument, NULL, option_no_absolute_literals },
  /* This option was changed from -align-target to -target-align
  /* This option was changed from -align-target to -target-align
     because it conflicted with the "-al" option.  */
     because it conflicted with the "-al" option.  */
  { "target-align", no_argument, NULL, option_align_targets },
  { "target-align", no_argument, NULL, option_align_targets },
  { "no-target-align", no_argument, NULL, option_no_align_targets },
  { "no-target-align", no_argument, NULL, option_no_align_targets },
  { "warn-unaligned-targets", no_argument, NULL,
  { "warn-unaligned-targets", no_argument, NULL,
    option_warn_unaligned_targets },
    option_warn_unaligned_targets },
  { "longcalls", no_argument, NULL, option_longcalls },
  { "longcalls", no_argument, NULL, option_longcalls },
  { "no-longcalls", no_argument, NULL, option_no_longcalls },
  { "no-longcalls", no_argument, NULL, option_no_longcalls },
 
 
  { "no-workaround-a0-b-retw", no_argument, NULL,
  { "no-workaround-a0-b-retw", no_argument, NULL,
    option_no_workaround_a0_b_retw },
    option_no_workaround_a0_b_retw },
  { "workaround-a0-b-retw", no_argument, NULL, option_workaround_a0_b_retw },
  { "workaround-a0-b-retw", no_argument, NULL, option_workaround_a0_b_retw },
 
 
  { "no-workaround-b-j-loop-end", no_argument, NULL,
  { "no-workaround-b-j-loop-end", no_argument, NULL,
    option_no_workaround_b_j_loop_end },
    option_no_workaround_b_j_loop_end },
  { "workaround-b-j-loop-end", no_argument, NULL,
  { "workaround-b-j-loop-end", no_argument, NULL,
    option_workaround_b_j_loop_end },
    option_workaround_b_j_loop_end },
 
 
  { "no-workaround-short-loops", no_argument, NULL,
  { "no-workaround-short-loops", no_argument, NULL,
    option_no_workaround_short_loop },
    option_no_workaround_short_loop },
  { "workaround-short-loops", no_argument, NULL,
  { "workaround-short-loops", no_argument, NULL,
    option_workaround_short_loop },
    option_workaround_short_loop },
 
 
  { "no-workaround-all-short-loops", no_argument, NULL,
  { "no-workaround-all-short-loops", no_argument, NULL,
    option_no_workaround_all_short_loops },
    option_no_workaround_all_short_loops },
  { "workaround-all-short-loop", no_argument, NULL,
  { "workaround-all-short-loop", no_argument, NULL,
    option_workaround_all_short_loops },
    option_workaround_all_short_loops },
 
 
  { "prefer-l32r", no_argument, NULL, option_prefer_l32r },
  { "prefer-l32r", no_argument, NULL, option_prefer_l32r },
  { "prefer-const16", no_argument, NULL, option_prefer_const16 },
  { "prefer-const16", no_argument, NULL, option_prefer_const16 },
 
 
  { "no-workarounds", no_argument, NULL, option_no_workarounds },
  { "no-workarounds", no_argument, NULL, option_no_workarounds },
 
 
  { "no-workaround-close-loop-end", no_argument, NULL,
  { "no-workaround-close-loop-end", no_argument, NULL,
    option_no_workaround_close_loop_end },
    option_no_workaround_close_loop_end },
  { "workaround-close-loop-end", no_argument, NULL,
  { "workaround-close-loop-end", no_argument, NULL,
    option_workaround_close_loop_end },
    option_workaround_close_loop_end },
 
 
  { "rename-section", required_argument, NULL, option_rename_section_name },
  { "rename-section", required_argument, NULL, option_rename_section_name },
 
 
  { "link-relax", no_argument, NULL, option_link_relax },
  { "link-relax", no_argument, NULL, option_link_relax },
  { "no-link-relax", no_argument, NULL, option_no_link_relax },
  { "no-link-relax", no_argument, NULL, option_no_link_relax },
 
 
  { "target-hardware", required_argument, NULL, option_target_hardware },
  { "target-hardware", required_argument, NULL, option_target_hardware },
 
 
  { NULL, no_argument, NULL, 0 }
  { NULL, no_argument, NULL, 0 }
};
};
 
 
size_t md_longopts_size = sizeof md_longopts;
size_t md_longopts_size = sizeof md_longopts;
 
 
 
 
int
int
md_parse_option (int c, char *arg)
md_parse_option (int c, char *arg)
{
{
  switch (c)
  switch (c)
    {
    {
    case option_density:
    case option_density:
      as_warn (_("--density option is ignored"));
      as_warn (_("--density option is ignored"));
      return 1;
      return 1;
    case option_no_density:
    case option_no_density:
      as_warn (_("--no-density option is ignored"));
      as_warn (_("--no-density option is ignored"));
      return 1;
      return 1;
    case option_link_relax:
    case option_link_relax:
      linkrelax = 1;
      linkrelax = 1;
      return 1;
      return 1;
    case option_no_link_relax:
    case option_no_link_relax:
      linkrelax = 0;
      linkrelax = 0;
      return 1;
      return 1;
    case option_flix:
    case option_flix:
      produce_flix = FLIX_ALL;
      produce_flix = FLIX_ALL;
      return 1;
      return 1;
    case option_no_generate_flix:
    case option_no_generate_flix:
      produce_flix = FLIX_NO_GENERATE;
      produce_flix = FLIX_NO_GENERATE;
      return 1;
      return 1;
    case option_no_flix:
    case option_no_flix:
      produce_flix = FLIX_NONE;
      produce_flix = FLIX_NONE;
      return 1;
      return 1;
    case option_generics:
    case option_generics:
      as_warn (_("--generics is deprecated; use --transform instead"));
      as_warn (_("--generics is deprecated; use --transform instead"));
      return md_parse_option (option_transform, arg);
      return md_parse_option (option_transform, arg);
    case option_no_generics:
    case option_no_generics:
      as_warn (_("--no-generics is deprecated; use --no-transform instead"));
      as_warn (_("--no-generics is deprecated; use --no-transform instead"));
      return md_parse_option (option_no_transform, arg);
      return md_parse_option (option_no_transform, arg);
    case option_relax:
    case option_relax:
      as_warn (_("--relax is deprecated; use --transform instead"));
      as_warn (_("--relax is deprecated; use --transform instead"));
      return md_parse_option (option_transform, arg);
      return md_parse_option (option_transform, arg);
    case option_no_relax:
    case option_no_relax:
      as_warn (_("--no-relax is deprecated; use --no-transform instead"));
      as_warn (_("--no-relax is deprecated; use --no-transform instead"));
      return md_parse_option (option_no_transform, arg);
      return md_parse_option (option_no_transform, arg);
    case option_longcalls:
    case option_longcalls:
      directive_state[directive_longcalls] = TRUE;
      directive_state[directive_longcalls] = TRUE;
      return 1;
      return 1;
    case option_no_longcalls:
    case option_no_longcalls:
      directive_state[directive_longcalls] = FALSE;
      directive_state[directive_longcalls] = FALSE;
      return 1;
      return 1;
    case option_text_section_literals:
    case option_text_section_literals:
      use_literal_section = FALSE;
      use_literal_section = FALSE;
      return 1;
      return 1;
    case option_no_text_section_literals:
    case option_no_text_section_literals:
      use_literal_section = TRUE;
      use_literal_section = TRUE;
      return 1;
      return 1;
    case option_absolute_literals:
    case option_absolute_literals:
      if (!absolute_literals_supported)
      if (!absolute_literals_supported)
        {
        {
          as_fatal (_("--absolute-literals option not supported in this Xtensa configuration"));
          as_fatal (_("--absolute-literals option not supported in this Xtensa configuration"));
          return 0;
          return 0;
        }
        }
      directive_state[directive_absolute_literals] = TRUE;
      directive_state[directive_absolute_literals] = TRUE;
      return 1;
      return 1;
    case option_no_absolute_literals:
    case option_no_absolute_literals:
      directive_state[directive_absolute_literals] = FALSE;
      directive_state[directive_absolute_literals] = FALSE;
      return 1;
      return 1;
 
 
    case option_workaround_a0_b_retw:
    case option_workaround_a0_b_retw:
      workaround_a0_b_retw = TRUE;
      workaround_a0_b_retw = TRUE;
      return 1;
      return 1;
    case option_no_workaround_a0_b_retw:
    case option_no_workaround_a0_b_retw:
      workaround_a0_b_retw = FALSE;
      workaround_a0_b_retw = FALSE;
      return 1;
      return 1;
    case option_workaround_b_j_loop_end:
    case option_workaround_b_j_loop_end:
      workaround_b_j_loop_end = TRUE;
      workaround_b_j_loop_end = TRUE;
      return 1;
      return 1;
    case option_no_workaround_b_j_loop_end:
    case option_no_workaround_b_j_loop_end:
      workaround_b_j_loop_end = FALSE;
      workaround_b_j_loop_end = FALSE;
      return 1;
      return 1;
 
 
    case option_workaround_short_loop:
    case option_workaround_short_loop:
      workaround_short_loop = TRUE;
      workaround_short_loop = TRUE;
      return 1;
      return 1;
    case option_no_workaround_short_loop:
    case option_no_workaround_short_loop:
      workaround_short_loop = FALSE;
      workaround_short_loop = FALSE;
      return 1;
      return 1;
 
 
    case option_workaround_all_short_loops:
    case option_workaround_all_short_loops:
      workaround_all_short_loops = TRUE;
      workaround_all_short_loops = TRUE;
      return 1;
      return 1;
    case option_no_workaround_all_short_loops:
    case option_no_workaround_all_short_loops:
      workaround_all_short_loops = FALSE;
      workaround_all_short_loops = FALSE;
      return 1;
      return 1;
 
 
    case option_workaround_close_loop_end:
    case option_workaround_close_loop_end:
      workaround_close_loop_end = TRUE;
      workaround_close_loop_end = TRUE;
      return 1;
      return 1;
    case option_no_workaround_close_loop_end:
    case option_no_workaround_close_loop_end:
      workaround_close_loop_end = FALSE;
      workaround_close_loop_end = FALSE;
      return 1;
      return 1;
 
 
    case option_no_workarounds:
    case option_no_workarounds:
      workaround_a0_b_retw = FALSE;
      workaround_a0_b_retw = FALSE;
      workaround_b_j_loop_end = FALSE;
      workaround_b_j_loop_end = FALSE;
      workaround_short_loop = FALSE;
      workaround_short_loop = FALSE;
      workaround_all_short_loops = FALSE;
      workaround_all_short_loops = FALSE;
      workaround_close_loop_end = FALSE;
      workaround_close_loop_end = FALSE;
      return 1;
      return 1;
 
 
    case option_align_targets:
    case option_align_targets:
      align_targets = TRUE;
      align_targets = TRUE;
      return 1;
      return 1;
    case option_no_align_targets:
    case option_no_align_targets:
      align_targets = FALSE;
      align_targets = FALSE;
      return 1;
      return 1;
 
 
    case option_warn_unaligned_targets:
    case option_warn_unaligned_targets:
      warn_unaligned_branch_targets = TRUE;
      warn_unaligned_branch_targets = TRUE;
      return 1;
      return 1;
 
 
    case option_rename_section_name:
    case option_rename_section_name:
      build_section_rename (arg);
      build_section_rename (arg);
      return 1;
      return 1;
 
 
    case 'Q':
    case 'Q':
      /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
      /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
         should be emitted or not.  FIXME: Not implemented.  */
         should be emitted or not.  FIXME: Not implemented.  */
      return 1;
      return 1;
 
 
    case option_prefer_l32r:
    case option_prefer_l32r:
      if (prefer_const16)
      if (prefer_const16)
        as_fatal (_("prefer-l32r conflicts with prefer-const16"));
        as_fatal (_("prefer-l32r conflicts with prefer-const16"));
      prefer_l32r = 1;
      prefer_l32r = 1;
      return 1;
      return 1;
 
 
    case option_prefer_const16:
    case option_prefer_const16:
      if (prefer_l32r)
      if (prefer_l32r)
        as_fatal (_("prefer-const16 conflicts with prefer-l32r"));
        as_fatal (_("prefer-const16 conflicts with prefer-l32r"));
      prefer_const16 = 1;
      prefer_const16 = 1;
      return 1;
      return 1;
 
 
    case option_target_hardware:
    case option_target_hardware:
      {
      {
        int earliest, latest = 0;
        int earliest, latest = 0;
        if (*arg == 0 || *arg == '-')
        if (*arg == 0 || *arg == '-')
          as_fatal (_("invalid target hardware version"));
          as_fatal (_("invalid target hardware version"));
 
 
        earliest = strtol (arg, &arg, 0);
        earliest = strtol (arg, &arg, 0);
 
 
        if (*arg == 0)
        if (*arg == 0)
          latest = earliest;
          latest = earliest;
        else if (*arg == '-')
        else if (*arg == '-')
          {
          {
            if (*++arg == 0)
            if (*++arg == 0)
              as_fatal (_("invalid target hardware version"));
              as_fatal (_("invalid target hardware version"));
            latest = strtol (arg, &arg, 0);
            latest = strtol (arg, &arg, 0);
          }
          }
        if (*arg != 0)
        if (*arg != 0)
          as_fatal (_("invalid target hardware version"));
          as_fatal (_("invalid target hardware version"));
 
 
        xtensa_setup_hw_workarounds (earliest, latest);
        xtensa_setup_hw_workarounds (earliest, latest);
        return 1;
        return 1;
      }
      }
 
 
    case option_transform:
    case option_transform:
      /* This option has no affect other than to use the defaults,
      /* This option has no affect other than to use the defaults,
         which are already set.  */
         which are already set.  */
      return 1;
      return 1;
 
 
    case option_no_transform:
    case option_no_transform:
      /* This option turns off all transformations of any kind.
      /* This option turns off all transformations of any kind.
         However, because we want to preserve the state of other
         However, because we want to preserve the state of other
         directives, we only change its own field.  Thus, before
         directives, we only change its own field.  Thus, before
         you perform any transformation, always check if transform
         you perform any transformation, always check if transform
         is available.  If you use the functions we provide for this
         is available.  If you use the functions we provide for this
         purpose, you will be ok.  */
         purpose, you will be ok.  */
      directive_state[directive_transform] = FALSE;
      directive_state[directive_transform] = FALSE;
      return 1;
      return 1;
 
 
    default:
    default:
      return 0;
      return 0;
    }
    }
}
}
 
 
 
 
void
void
md_show_usage (FILE *stream)
md_show_usage (FILE *stream)
{
{
  fputs ("\n\
  fputs ("\n\
Xtensa options:\n\
Xtensa options:\n\
  --[no-]text-section-literals\n\
  --[no-]text-section-literals\n\
                          [Do not] put literals in the text section\n\
                          [Do not] put literals in the text section\n\
  --[no-]absolute-literals\n\
  --[no-]absolute-literals\n\
                          [Do not] default to use non-PC-relative literals\n\
                          [Do not] default to use non-PC-relative literals\n\
  --[no-]target-align     [Do not] try to align branch targets\n\
  --[no-]target-align     [Do not] try to align branch targets\n\
  --[no-]longcalls        [Do not] emit 32-bit call sequences\n\
  --[no-]longcalls        [Do not] emit 32-bit call sequences\n\
  --[no-]transform        [Do not] transform instructions\n\
  --[no-]transform        [Do not] transform instructions\n\
  --flix                  both allow hand-written and generate flix bundles\n\
  --flix                  both allow hand-written and generate flix bundles\n\
  --no-generate-flix      allow hand-written but do not generate\n\
  --no-generate-flix      allow hand-written but do not generate\n\
                          flix bundles\n\
                          flix bundles\n\
  --no-allow-flix         neither allow hand-written nor generate\n\
  --no-allow-flix         neither allow hand-written nor generate\n\
                          flix bundles\n\
                          flix bundles\n\
  --rename-section old=new Rename section 'old' to 'new'\n", stream);
  --rename-section old=new Rename section 'old' to 'new'\n", stream);
}
}
 
 


/* Functions related to the list of current label symbols.  */
/* Functions related to the list of current label symbols.  */
 
 
static void
static void
xtensa_add_insn_label (symbolS *sym)
xtensa_add_insn_label (symbolS *sym)
{
{
  sym_list *l;
  sym_list *l;
 
 
  if (!free_insn_labels)
  if (!free_insn_labels)
    l = (sym_list *) xmalloc (sizeof (sym_list));
    l = (sym_list *) xmalloc (sizeof (sym_list));
  else
  else
    {
    {
      l = free_insn_labels;
      l = free_insn_labels;
      free_insn_labels = l->next;
      free_insn_labels = l->next;
    }
    }
 
 
  l->sym = sym;
  l->sym = sym;
  l->next = insn_labels;
  l->next = insn_labels;
  insn_labels = l;
  insn_labels = l;
}
}
 
 
 
 
static void
static void
xtensa_clear_insn_labels (void)
xtensa_clear_insn_labels (void)
{
{
  sym_list **pl;
  sym_list **pl;
 
 
  for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
  for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
    ;
    ;
  *pl = insn_labels;
  *pl = insn_labels;
  insn_labels = NULL;
  insn_labels = NULL;
}
}
 
 
 
 
static void
static void
xtensa_move_labels (fragS *new_frag, valueT new_offset)
xtensa_move_labels (fragS *new_frag, valueT new_offset)
{
{
  sym_list *lit;
  sym_list *lit;
 
 
  for (lit = insn_labels; lit; lit = lit->next)
  for (lit = insn_labels; lit; lit = lit->next)
    {
    {
      symbolS *lit_sym = lit->sym;
      symbolS *lit_sym = lit->sym;
      S_SET_VALUE (lit_sym, new_offset);
      S_SET_VALUE (lit_sym, new_offset);
      symbol_set_frag (lit_sym, new_frag);
      symbol_set_frag (lit_sym, new_frag);
    }
    }
}
}
 
 


/* Directive data and functions.  */
/* Directive data and functions.  */
 
 
typedef struct state_stackS_struct
typedef struct state_stackS_struct
{
{
  directiveE directive;
  directiveE directive;
  bfd_boolean negated;
  bfd_boolean negated;
  bfd_boolean old_state;
  bfd_boolean old_state;
  const char *file;
  const char *file;
  unsigned int line;
  unsigned int line;
  const void *datum;
  const void *datum;
  struct state_stackS_struct *prev;
  struct state_stackS_struct *prev;
} state_stackS;
} state_stackS;
 
 
state_stackS *directive_state_stack;
state_stackS *directive_state_stack;
 
 
const pseudo_typeS md_pseudo_table[] =
const pseudo_typeS md_pseudo_table[] =
{
{
  { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0).  */
  { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0).  */
  { "literal_position", xtensa_literal_position, 0 },
  { "literal_position", xtensa_literal_position, 0 },
  { "frame", s_ignore, 0 },      /* Formerly used for STABS debugging.  */
  { "frame", s_ignore, 0 },      /* Formerly used for STABS debugging.  */
  { "long", xtensa_elf_cons, 4 },
  { "long", xtensa_elf_cons, 4 },
  { "word", xtensa_elf_cons, 4 },
  { "word", xtensa_elf_cons, 4 },
  { "4byte", xtensa_elf_cons, 4 },
  { "4byte", xtensa_elf_cons, 4 },
  { "short", xtensa_elf_cons, 2 },
  { "short", xtensa_elf_cons, 2 },
  { "2byte", xtensa_elf_cons, 2 },
  { "2byte", xtensa_elf_cons, 2 },
  { "sleb128", xtensa_leb128, 1},
  { "sleb128", xtensa_leb128, 1},
  { "uleb128", xtensa_leb128, 0},
  { "uleb128", xtensa_leb128, 0},
  { "begin", xtensa_begin_directive, 0 },
  { "begin", xtensa_begin_directive, 0 },
  { "end", xtensa_end_directive, 0 },
  { "end", xtensa_end_directive, 0 },
  { "literal", xtensa_literal_pseudo, 0 },
  { "literal", xtensa_literal_pseudo, 0 },
  { "frequency", xtensa_frequency_pseudo, 0 },
  { "frequency", xtensa_frequency_pseudo, 0 },
  { NULL, 0, 0 },
  { NULL, 0, 0 },
};
};
 
 
 
 
static bfd_boolean
static bfd_boolean
use_transform (void)
use_transform (void)
{
{
  /* After md_end, you should be checking frag by frag, rather
  /* After md_end, you should be checking frag by frag, rather
     than state directives.  */
     than state directives.  */
  gas_assert (!past_xtensa_end);
  gas_assert (!past_xtensa_end);
  return directive_state[directive_transform];
  return directive_state[directive_transform];
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
do_align_targets (void)
do_align_targets (void)
{
{
  /* Do not use this function after md_end; just look at align_targets
  /* Do not use this function after md_end; just look at align_targets
     instead.  There is no target-align directive, so alignment is either
     instead.  There is no target-align directive, so alignment is either
     enabled for all frags or not done at all.  */
     enabled for all frags or not done at all.  */
  gas_assert (!past_xtensa_end);
  gas_assert (!past_xtensa_end);
  return align_targets && use_transform ();
  return align_targets && use_transform ();
}
}
 
 
 
 
static void
static void
directive_push (directiveE directive, bfd_boolean negated, const void *datum)
directive_push (directiveE directive, bfd_boolean negated, const void *datum)
{
{
  char *file;
  char *file;
  unsigned int line;
  unsigned int line;
  state_stackS *stack = (state_stackS *) xmalloc (sizeof (state_stackS));
  state_stackS *stack = (state_stackS *) xmalloc (sizeof (state_stackS));
 
 
  as_where (&file, &line);
  as_where (&file, &line);
 
 
  stack->directive = directive;
  stack->directive = directive;
  stack->negated = negated;
  stack->negated = negated;
  stack->old_state = directive_state[directive];
  stack->old_state = directive_state[directive];
  stack->file = file;
  stack->file = file;
  stack->line = line;
  stack->line = line;
  stack->datum = datum;
  stack->datum = datum;
  stack->prev = directive_state_stack;
  stack->prev = directive_state_stack;
  directive_state_stack = stack;
  directive_state_stack = stack;
 
 
  directive_state[directive] = !negated;
  directive_state[directive] = !negated;
}
}
 
 
 
 
static void
static void
directive_pop (directiveE *directive,
directive_pop (directiveE *directive,
               bfd_boolean *negated,
               bfd_boolean *negated,
               const char **file,
               const char **file,
               unsigned int *line,
               unsigned int *line,
               const void **datum)
               const void **datum)
{
{
  state_stackS *top = directive_state_stack;
  state_stackS *top = directive_state_stack;
 
 
  if (!directive_state_stack)
  if (!directive_state_stack)
    {
    {
      as_bad (_("unmatched end directive"));
      as_bad (_("unmatched end directive"));
      *directive = directive_none;
      *directive = directive_none;
      return;
      return;
    }
    }
 
 
  directive_state[directive_state_stack->directive] = top->old_state;
  directive_state[directive_state_stack->directive] = top->old_state;
  *directive = top->directive;
  *directive = top->directive;
  *negated = top->negated;
  *negated = top->negated;
  *file = top->file;
  *file = top->file;
  *line = top->line;
  *line = top->line;
  *datum = top->datum;
  *datum = top->datum;
  directive_state_stack = top->prev;
  directive_state_stack = top->prev;
  free (top);
  free (top);
}
}
 
 
 
 
static void
static void
directive_balance (void)
directive_balance (void)
{
{
  while (directive_state_stack)
  while (directive_state_stack)
    {
    {
      directiveE directive;
      directiveE directive;
      bfd_boolean negated;
      bfd_boolean negated;
      const char *file;
      const char *file;
      unsigned int line;
      unsigned int line;
      const void *datum;
      const void *datum;
 
 
      directive_pop (&directive, &negated, &file, &line, &datum);
      directive_pop (&directive, &negated, &file, &line, &datum);
      as_warn_where ((char *) file, line,
      as_warn_where ((char *) file, line,
                     _(".begin directive with no matching .end directive"));
                     _(".begin directive with no matching .end directive"));
    }
    }
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
inside_directive (directiveE dir)
inside_directive (directiveE dir)
{
{
  state_stackS *top = directive_state_stack;
  state_stackS *top = directive_state_stack;
 
 
  while (top && top->directive != dir)
  while (top && top->directive != dir)
    top = top->prev;
    top = top->prev;
 
 
  return (top != NULL);
  return (top != NULL);
}
}
 
 
 
 
static void
static void
get_directive (directiveE *directive, bfd_boolean *negated)
get_directive (directiveE *directive, bfd_boolean *negated)
{
{
  int len;
  int len;
  unsigned i;
  unsigned i;
  char *directive_string;
  char *directive_string;
 
 
  if (strncmp (input_line_pointer, "no-", 3) != 0)
  if (strncmp (input_line_pointer, "no-", 3) != 0)
    *negated = FALSE;
    *negated = FALSE;
  else
  else
    {
    {
      *negated = TRUE;
      *negated = TRUE;
      input_line_pointer += 3;
      input_line_pointer += 3;
    }
    }
 
 
  len = strspn (input_line_pointer,
  len = strspn (input_line_pointer,
                "abcdefghijklmnopqrstuvwxyz_-/0123456789.");
                "abcdefghijklmnopqrstuvwxyz_-/0123456789.");
 
 
  /* This code is a hack to make .begin [no-][generics|relax] exactly
  /* This code is a hack to make .begin [no-][generics|relax] exactly
     equivalent to .begin [no-]transform.  We should remove it when
     equivalent to .begin [no-]transform.  We should remove it when
     we stop accepting those options.  */
     we stop accepting those options.  */
 
 
  if (strncmp (input_line_pointer, "generics", strlen ("generics")) == 0)
  if (strncmp (input_line_pointer, "generics", strlen ("generics")) == 0)
    {
    {
      as_warn (_("[no-]generics is deprecated; use [no-]transform instead"));
      as_warn (_("[no-]generics is deprecated; use [no-]transform instead"));
      directive_string = "transform";
      directive_string = "transform";
    }
    }
  else if (strncmp (input_line_pointer, "relax", strlen ("relax")) == 0)
  else if (strncmp (input_line_pointer, "relax", strlen ("relax")) == 0)
    {
    {
      as_warn (_("[no-]relax is deprecated; use [no-]transform instead"));
      as_warn (_("[no-]relax is deprecated; use [no-]transform instead"));
      directive_string = "transform";
      directive_string = "transform";
    }
    }
  else
  else
    directive_string = input_line_pointer;
    directive_string = input_line_pointer;
 
 
  for (i = 0; i < sizeof (directive_info) / sizeof (*directive_info); ++i)
  for (i = 0; i < sizeof (directive_info) / sizeof (*directive_info); ++i)
    {
    {
      if (strncmp (directive_string, directive_info[i].name, len) == 0)
      if (strncmp (directive_string, directive_info[i].name, len) == 0)
        {
        {
          input_line_pointer += len;
          input_line_pointer += len;
          *directive = (directiveE) i;
          *directive = (directiveE) i;
          if (*negated && !directive_info[i].can_be_negated)
          if (*negated && !directive_info[i].can_be_negated)
            as_bad (_("directive %s cannot be negated"),
            as_bad (_("directive %s cannot be negated"),
                    directive_info[i].name);
                    directive_info[i].name);
          return;
          return;
        }
        }
    }
    }
 
 
  as_bad (_("unknown directive"));
  as_bad (_("unknown directive"));
  *directive = (directiveE) XTENSA_UNDEFINED;
  *directive = (directiveE) XTENSA_UNDEFINED;
}
}
 
 
 
 
static void
static void
xtensa_begin_directive (int ignore ATTRIBUTE_UNUSED)
xtensa_begin_directive (int ignore ATTRIBUTE_UNUSED)
{
{
  directiveE directive;
  directiveE directive;
  bfd_boolean negated;
  bfd_boolean negated;
  emit_state *state;
  emit_state *state;
  lit_state *ls;
  lit_state *ls;
 
 
  get_directive (&directive, &negated);
  get_directive (&directive, &negated);
  if (directive == (directiveE) XTENSA_UNDEFINED)
  if (directive == (directiveE) XTENSA_UNDEFINED)
    {
    {
      discard_rest_of_line ();
      discard_rest_of_line ();
      return;
      return;
    }
    }
 
 
  if (cur_vinsn.inside_bundle)
  if (cur_vinsn.inside_bundle)
    as_bad (_("directives are not valid inside bundles"));
    as_bad (_("directives are not valid inside bundles"));
 
 
  switch (directive)
  switch (directive)
    {
    {
    case directive_literal:
    case directive_literal:
      if (!inside_directive (directive_literal))
      if (!inside_directive (directive_literal))
        {
        {
          /* Previous labels go with whatever follows this directive, not with
          /* Previous labels go with whatever follows this directive, not with
             the literal, so save them now.  */
             the literal, so save them now.  */
          saved_insn_labels = insn_labels;
          saved_insn_labels = insn_labels;
          insn_labels = NULL;
          insn_labels = NULL;
        }
        }
      as_warn (_(".begin literal is deprecated; use .literal instead"));
      as_warn (_(".begin literal is deprecated; use .literal instead"));
      state = (emit_state *) xmalloc (sizeof (emit_state));
      state = (emit_state *) xmalloc (sizeof (emit_state));
      xtensa_switch_to_literal_fragment (state);
      xtensa_switch_to_literal_fragment (state);
      directive_push (directive_literal, negated, state);
      directive_push (directive_literal, negated, state);
      break;
      break;
 
 
    case directive_literal_prefix:
    case directive_literal_prefix:
      /* Have to flush pending output because a movi relaxed to an l32r
      /* Have to flush pending output because a movi relaxed to an l32r
         might produce a literal.  */
         might produce a literal.  */
      md_flush_pending_output ();
      md_flush_pending_output ();
      /* Check to see if the current fragment is a literal
      /* Check to see if the current fragment is a literal
         fragment.  If it is, then this operation is not allowed.  */
         fragment.  If it is, then this operation is not allowed.  */
      if (generating_literals)
      if (generating_literals)
        {
        {
          as_bad (_("cannot set literal_prefix inside literal fragment"));
          as_bad (_("cannot set literal_prefix inside literal fragment"));
          return;
          return;
        }
        }
 
 
      /* Allocate the literal state for this section and push
      /* Allocate the literal state for this section and push
         onto the directive stack.  */
         onto the directive stack.  */
      ls = xmalloc (sizeof (lit_state));
      ls = xmalloc (sizeof (lit_state));
      gas_assert (ls);
      gas_assert (ls);
 
 
      *ls = default_lit_sections;
      *ls = default_lit_sections;
      directive_push (directive_literal_prefix, negated, ls);
      directive_push (directive_literal_prefix, negated, ls);
 
 
      /* Process the new prefix.  */
      /* Process the new prefix.  */
      xtensa_literal_prefix ();
      xtensa_literal_prefix ();
      break;
      break;
 
 
    case directive_freeregs:
    case directive_freeregs:
      /* This information is currently unused, but we'll accept the statement
      /* This information is currently unused, but we'll accept the statement
         and just discard the rest of the line.  This won't check the syntax,
         and just discard the rest of the line.  This won't check the syntax,
         but it will accept every correct freeregs directive.  */
         but it will accept every correct freeregs directive.  */
      input_line_pointer += strcspn (input_line_pointer, "\n");
      input_line_pointer += strcspn (input_line_pointer, "\n");
      directive_push (directive_freeregs, negated, 0);
      directive_push (directive_freeregs, negated, 0);
      break;
      break;
 
 
    case directive_schedule:
    case directive_schedule:
      md_flush_pending_output ();
      md_flush_pending_output ();
      frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
      frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
                frag_now->fr_symbol, frag_now->fr_offset, NULL);
                frag_now->fr_symbol, frag_now->fr_offset, NULL);
      directive_push (directive_schedule, negated, 0);
      directive_push (directive_schedule, negated, 0);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      break;
      break;
 
 
    case directive_density:
    case directive_density:
      as_warn (_(".begin [no-]density is ignored"));
      as_warn (_(".begin [no-]density is ignored"));
      break;
      break;
 
 
    case directive_absolute_literals:
    case directive_absolute_literals:
      md_flush_pending_output ();
      md_flush_pending_output ();
      if (!absolute_literals_supported && !negated)
      if (!absolute_literals_supported && !negated)
        {
        {
          as_warn (_("Xtensa absolute literals option not supported; ignored"));
          as_warn (_("Xtensa absolute literals option not supported; ignored"));
          break;
          break;
        }
        }
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      directive_push (directive, negated, 0);
      directive_push (directive, negated, 0);
      break;
      break;
 
 
    default:
    default:
      md_flush_pending_output ();
      md_flush_pending_output ();
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      directive_push (directive, negated, 0);
      directive_push (directive, negated, 0);
      break;
      break;
    }
    }
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
 
 
static void
static void
xtensa_end_directive (int ignore ATTRIBUTE_UNUSED)
xtensa_end_directive (int ignore ATTRIBUTE_UNUSED)
{
{
  directiveE begin_directive, end_directive;
  directiveE begin_directive, end_directive;
  bfd_boolean begin_negated, end_negated;
  bfd_boolean begin_negated, end_negated;
  const char *file;
  const char *file;
  unsigned int line;
  unsigned int line;
  emit_state *state;
  emit_state *state;
  emit_state **state_ptr;
  emit_state **state_ptr;
  lit_state *s;
  lit_state *s;
 
 
  if (cur_vinsn.inside_bundle)
  if (cur_vinsn.inside_bundle)
    as_bad (_("directives are not valid inside bundles"));
    as_bad (_("directives are not valid inside bundles"));
 
 
  get_directive (&end_directive, &end_negated);
  get_directive (&end_directive, &end_negated);
 
 
  md_flush_pending_output ();
  md_flush_pending_output ();
 
 
  switch (end_directive)
  switch (end_directive)
    {
    {
    case (directiveE) XTENSA_UNDEFINED:
    case (directiveE) XTENSA_UNDEFINED:
      discard_rest_of_line ();
      discard_rest_of_line ();
      return;
      return;
 
 
    case directive_density:
    case directive_density:
      as_warn (_(".end [no-]density is ignored"));
      as_warn (_(".end [no-]density is ignored"));
      demand_empty_rest_of_line ();
      demand_empty_rest_of_line ();
      break;
      break;
 
 
    case directive_absolute_literals:
    case directive_absolute_literals:
      if (!absolute_literals_supported && !end_negated)
      if (!absolute_literals_supported && !end_negated)
        {
        {
          as_warn (_("Xtensa absolute literals option not supported; ignored"));
          as_warn (_("Xtensa absolute literals option not supported; ignored"));
          demand_empty_rest_of_line ();
          demand_empty_rest_of_line ();
          return;
          return;
        }
        }
      break;
      break;
 
 
    default:
    default:
      break;
      break;
    }
    }
 
 
  state_ptr = &state; /* use state_ptr to avoid type-punning warning */
  state_ptr = &state; /* use state_ptr to avoid type-punning warning */
  directive_pop (&begin_directive, &begin_negated, &file, &line,
  directive_pop (&begin_directive, &begin_negated, &file, &line,
                 (const void **) state_ptr);
                 (const void **) state_ptr);
 
 
  if (begin_directive != directive_none)
  if (begin_directive != directive_none)
    {
    {
      if (begin_directive != end_directive || begin_negated != end_negated)
      if (begin_directive != end_directive || begin_negated != end_negated)
        {
        {
          as_bad (_("does not match begin %s%s at %s:%d"),
          as_bad (_("does not match begin %s%s at %s:%d"),
                  begin_negated ? "no-" : "",
                  begin_negated ? "no-" : "",
                  directive_info[begin_directive].name, file, line);
                  directive_info[begin_directive].name, file, line);
        }
        }
      else
      else
        {
        {
          switch (end_directive)
          switch (end_directive)
            {
            {
            case directive_literal:
            case directive_literal:
              frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL);
              frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL);
              xtensa_restore_emit_state (state);
              xtensa_restore_emit_state (state);
              xtensa_set_frag_assembly_state (frag_now);
              xtensa_set_frag_assembly_state (frag_now);
              free (state);
              free (state);
              if (!inside_directive (directive_literal))
              if (!inside_directive (directive_literal))
                {
                {
                  /* Restore the list of current labels.  */
                  /* Restore the list of current labels.  */
                  xtensa_clear_insn_labels ();
                  xtensa_clear_insn_labels ();
                  insn_labels = saved_insn_labels;
                  insn_labels = saved_insn_labels;
                }
                }
              break;
              break;
 
 
            case directive_literal_prefix:
            case directive_literal_prefix:
              /* Restore the default collection sections from saved state.  */
              /* Restore the default collection sections from saved state.  */
              s = (lit_state *) state;
              s = (lit_state *) state;
              gas_assert (s);
              gas_assert (s);
              default_lit_sections = *s;
              default_lit_sections = *s;
 
 
              /* Free the state storage.  */
              /* Free the state storage.  */
              free (s->lit_prefix);
              free (s->lit_prefix);
              free (s);
              free (s);
              break;
              break;
 
 
            case directive_schedule:
            case directive_schedule:
            case directive_freeregs:
            case directive_freeregs:
              break;
              break;
 
 
            default:
            default:
              xtensa_set_frag_assembly_state (frag_now);
              xtensa_set_frag_assembly_state (frag_now);
              break;
              break;
            }
            }
        }
        }
    }
    }
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
 
 
/* Place an aligned literal fragment at the current location.  */
/* Place an aligned literal fragment at the current location.  */
 
 
static void
static void
xtensa_literal_position (int ignore ATTRIBUTE_UNUSED)
xtensa_literal_position (int ignore ATTRIBUTE_UNUSED)
{
{
  md_flush_pending_output ();
  md_flush_pending_output ();
 
 
  if (inside_directive (directive_literal))
  if (inside_directive (directive_literal))
    as_warn (_(".literal_position inside literal directive; ignoring"));
    as_warn (_(".literal_position inside literal directive; ignoring"));
  xtensa_mark_literal_pool_location ();
  xtensa_mark_literal_pool_location ();
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
  xtensa_clear_insn_labels ();
  xtensa_clear_insn_labels ();
}
}
 
 
 
 
/* Support .literal label, expr, ...  */
/* Support .literal label, expr, ...  */
 
 
static void
static void
xtensa_literal_pseudo (int ignored ATTRIBUTE_UNUSED)
xtensa_literal_pseudo (int ignored ATTRIBUTE_UNUSED)
{
{
  emit_state state;
  emit_state state;
  char *p, *base_name;
  char *p, *base_name;
  char c;
  char c;
  segT dest_seg;
  segT dest_seg;
 
 
  if (inside_directive (directive_literal))
  if (inside_directive (directive_literal))
    {
    {
      as_bad (_(".literal not allowed inside .begin literal region"));
      as_bad (_(".literal not allowed inside .begin literal region"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  md_flush_pending_output ();
  md_flush_pending_output ();
 
 
  /* Previous labels go with whatever follows this directive, not with
  /* Previous labels go with whatever follows this directive, not with
     the literal, so save them now.  */
     the literal, so save them now.  */
  saved_insn_labels = insn_labels;
  saved_insn_labels = insn_labels;
  insn_labels = NULL;
  insn_labels = NULL;
 
 
  /* If we are using text-section literals, then this is the right value... */
  /* If we are using text-section literals, then this is the right value... */
  dest_seg = now_seg;
  dest_seg = now_seg;
 
 
  base_name = input_line_pointer;
  base_name = input_line_pointer;
 
 
  xtensa_switch_to_literal_fragment (&state);
  xtensa_switch_to_literal_fragment (&state);
 
 
  /* ...but if we aren't using text-section-literals, then we
  /* ...but if we aren't using text-section-literals, then we
     need to put them in the section we just switched to.  */
     need to put them in the section we just switched to.  */
  if (use_literal_section || directive_state[directive_absolute_literals])
  if (use_literal_section || directive_state[directive_absolute_literals])
    dest_seg = now_seg;
    dest_seg = now_seg;
 
 
  /* All literals are aligned to four-byte boundaries.  */
  /* All literals are aligned to four-byte boundaries.  */
  frag_align (2, 0, 0);
  frag_align (2, 0, 0);
  record_alignment (now_seg, 2);
  record_alignment (now_seg, 2);
 
 
  c = get_symbol_end ();
  c = get_symbol_end ();
  /* Just after name is now '\0'.  */
  /* Just after name is now '\0'.  */
  p = input_line_pointer;
  p = input_line_pointer;
  *p = c;
  *p = c;
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  if (*input_line_pointer != ',' && *input_line_pointer != ':')
  if (*input_line_pointer != ',' && *input_line_pointer != ':')
    {
    {
      as_bad (_("expected comma or colon after symbol name; "
      as_bad (_("expected comma or colon after symbol name; "
                "rest of line ignored"));
                "rest of line ignored"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      xtensa_restore_emit_state (&state);
      xtensa_restore_emit_state (&state);
      return;
      return;
    }
    }
  *p = 0;
  *p = 0;
 
 
  colon (base_name);
  colon (base_name);
 
 
  *p = c;
  *p = c;
  input_line_pointer++;         /* skip ',' or ':' */
  input_line_pointer++;         /* skip ',' or ':' */
 
 
  xtensa_elf_cons (4);
  xtensa_elf_cons (4);
 
 
  xtensa_restore_emit_state (&state);
  xtensa_restore_emit_state (&state);
 
 
  /* Restore the list of current labels.  */
  /* Restore the list of current labels.  */
  xtensa_clear_insn_labels ();
  xtensa_clear_insn_labels ();
  insn_labels = saved_insn_labels;
  insn_labels = saved_insn_labels;
}
}
 
 
 
 
static void
static void
xtensa_literal_prefix (void)
xtensa_literal_prefix (void)
{
{
  char *name;
  char *name;
  int len;
  int len;
 
 
  /* Parse the new prefix from the input_line_pointer.  */
  /* Parse the new prefix from the input_line_pointer.  */
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
  len = strspn (input_line_pointer,
  len = strspn (input_line_pointer,
                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                "abcdefghijklmnopqrstuvwxyz_/0123456789.$");
                "abcdefghijklmnopqrstuvwxyz_/0123456789.$");
 
 
  /* Get a null-terminated copy of the name.  */
  /* Get a null-terminated copy of the name.  */
  name = xmalloc (len + 1);
  name = xmalloc (len + 1);
  gas_assert (name);
  gas_assert (name);
  strncpy (name, input_line_pointer, len);
  strncpy (name, input_line_pointer, len);
  name[len] = 0;
  name[len] = 0;
 
 
  /* Skip the name in the input line.  */
  /* Skip the name in the input line.  */
  input_line_pointer += len;
  input_line_pointer += len;
 
 
  default_lit_sections.lit_prefix = name;
  default_lit_sections.lit_prefix = name;
 
 
  /* Clear cached literal sections, since the prefix has changed.  */
  /* Clear cached literal sections, since the prefix has changed.  */
  default_lit_sections.lit_seg = NULL;
  default_lit_sections.lit_seg = NULL;
  default_lit_sections.lit4_seg = NULL;
  default_lit_sections.lit4_seg = NULL;
}
}
 
 
 
 
/* Support ".frequency branch_target_frequency fall_through_frequency".  */
/* Support ".frequency branch_target_frequency fall_through_frequency".  */
 
 
static void
static void
xtensa_frequency_pseudo (int ignored ATTRIBUTE_UNUSED)
xtensa_frequency_pseudo (int ignored ATTRIBUTE_UNUSED)
{
{
  float fall_through_f, target_f;
  float fall_through_f, target_f;
 
 
  fall_through_f = (float) strtod (input_line_pointer, &input_line_pointer);
  fall_through_f = (float) strtod (input_line_pointer, &input_line_pointer);
  if (fall_through_f < 0)
  if (fall_through_f < 0)
    {
    {
      as_bad (_("fall through frequency must be greater than 0"));
      as_bad (_("fall through frequency must be greater than 0"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  target_f = (float) strtod (input_line_pointer, &input_line_pointer);
  target_f = (float) strtod (input_line_pointer, &input_line_pointer);
  if (target_f < 0)
  if (target_f < 0)
    {
    {
      as_bad (_("branch target frequency must be greater than 0"));
      as_bad (_("branch target frequency must be greater than 0"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  set_subseg_freq (now_seg, now_subseg, target_f + fall_through_f, target_f);
  set_subseg_freq (now_seg, now_subseg, target_f + fall_through_f, target_f);
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
 
 
/* Like normal .long/.short/.word, except support @plt, etc.
/* Like normal .long/.short/.word, except support @plt, etc.
   Clobbers input_line_pointer, checks end-of-line.  */
   Clobbers input_line_pointer, checks end-of-line.  */
 
 
static void
static void
xtensa_elf_cons (int nbytes)
xtensa_elf_cons (int nbytes)
{
{
  expressionS exp;
  expressionS exp;
  bfd_reloc_code_real_type reloc;
  bfd_reloc_code_real_type reloc;
 
 
  md_flush_pending_output ();
  md_flush_pending_output ();
 
 
  if (cur_vinsn.inside_bundle)
  if (cur_vinsn.inside_bundle)
    as_bad (_("directives are not valid inside bundles"));
    as_bad (_("directives are not valid inside bundles"));
 
 
  if (is_it_end_of_statement ())
  if (is_it_end_of_statement ())
    {
    {
      demand_empty_rest_of_line ();
      demand_empty_rest_of_line ();
      return;
      return;
    }
    }
 
 
  do
  do
    {
    {
      expression (&exp);
      expression (&exp);
      if (exp.X_op == O_symbol
      if (exp.X_op == O_symbol
          && *input_line_pointer == '@'
          && *input_line_pointer == '@'
          && ((reloc = xtensa_elf_suffix (&input_line_pointer, &exp))
          && ((reloc = xtensa_elf_suffix (&input_line_pointer, &exp))
              != BFD_RELOC_NONE))
              != BFD_RELOC_NONE))
        {
        {
          reloc_howto_type *reloc_howto =
          reloc_howto_type *reloc_howto =
            bfd_reloc_type_lookup (stdoutput, reloc);
            bfd_reloc_type_lookup (stdoutput, reloc);
 
 
          if (reloc == BFD_RELOC_UNUSED || !reloc_howto)
          if (reloc == BFD_RELOC_UNUSED || !reloc_howto)
            as_bad (_("unsupported relocation"));
            as_bad (_("unsupported relocation"));
          else if ((reloc >= BFD_RELOC_XTENSA_SLOT0_OP
          else if ((reloc >= BFD_RELOC_XTENSA_SLOT0_OP
                    && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
                    && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
                   || (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
                   || (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
                       && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT))
                       && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT))
            as_bad (_("opcode-specific %s relocation used outside "
            as_bad (_("opcode-specific %s relocation used outside "
                      "an instruction"), reloc_howto->name);
                      "an instruction"), reloc_howto->name);
          else if (nbytes != (int) bfd_get_reloc_size (reloc_howto))
          else if (nbytes != (int) bfd_get_reloc_size (reloc_howto))
            as_bad (_("%s relocations do not fit in %d bytes"),
            as_bad (_("%s relocations do not fit in %d bytes"),
                    reloc_howto->name, nbytes);
                    reloc_howto->name, nbytes);
          else if (reloc == BFD_RELOC_XTENSA_TLS_FUNC
          else if (reloc == BFD_RELOC_XTENSA_TLS_FUNC
                   || reloc == BFD_RELOC_XTENSA_TLS_ARG
                   || reloc == BFD_RELOC_XTENSA_TLS_ARG
                   || reloc == BFD_RELOC_XTENSA_TLS_CALL)
                   || reloc == BFD_RELOC_XTENSA_TLS_CALL)
            as_bad (_("invalid use of %s relocation"), reloc_howto->name);
            as_bad (_("invalid use of %s relocation"), reloc_howto->name);
          else
          else
            {
            {
              char *p = frag_more ((int) nbytes);
              char *p = frag_more ((int) nbytes);
              xtensa_set_frag_assembly_state (frag_now);
              xtensa_set_frag_assembly_state (frag_now);
              fix_new_exp (frag_now, p - frag_now->fr_literal,
              fix_new_exp (frag_now, p - frag_now->fr_literal,
                           nbytes, &exp, reloc_howto->pc_relative, reloc);
                           nbytes, &exp, reloc_howto->pc_relative, reloc);
            }
            }
        }
        }
      else
      else
        {
        {
          xtensa_set_frag_assembly_state (frag_now);
          xtensa_set_frag_assembly_state (frag_now);
          emit_expr (&exp, (unsigned int) nbytes);
          emit_expr (&exp, (unsigned int) nbytes);
        }
        }
    }
    }
  while (*input_line_pointer++ == ',');
  while (*input_line_pointer++ == ',');
 
 
  input_line_pointer--;         /* Put terminator back into stream.  */
  input_line_pointer--;         /* Put terminator back into stream.  */
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
static bfd_boolean is_leb128_expr;
static bfd_boolean is_leb128_expr;
 
 
static void
static void
xtensa_leb128 (int sign)
xtensa_leb128 (int sign)
{
{
  is_leb128_expr = TRUE;
  is_leb128_expr = TRUE;
  s_leb128 (sign);
  s_leb128 (sign);
  is_leb128_expr = FALSE;
  is_leb128_expr = FALSE;
}
}
 
 


/* Parsing and Idiom Translation.  */
/* Parsing and Idiom Translation.  */
 
 
/* Parse @plt, etc. and return the desired relocation.  */
/* Parse @plt, etc. and return the desired relocation.  */
static bfd_reloc_code_real_type
static bfd_reloc_code_real_type
xtensa_elf_suffix (char **str_p, expressionS *exp_p)
xtensa_elf_suffix (char **str_p, expressionS *exp_p)
{
{
  char ident[20];
  char ident[20];
  char *str = *str_p;
  char *str = *str_p;
  char *str2;
  char *str2;
  int ch;
  int ch;
  int len;
  int len;
  struct suffix_reloc_map *ptr;
  struct suffix_reloc_map *ptr;
 
 
  if (*str++ != '@')
  if (*str++ != '@')
    return BFD_RELOC_NONE;
    return BFD_RELOC_NONE;
 
 
  for (ch = *str, str2 = ident;
  for (ch = *str, str2 = ident;
       (str2 < ident + sizeof (ident) - 1
       (str2 < ident + sizeof (ident) - 1
        && (ISALNUM (ch) || ch == '@'));
        && (ISALNUM (ch) || ch == '@'));
       ch = *++str)
       ch = *++str)
    {
    {
      *str2++ = (ISLOWER (ch)) ? ch : TOLOWER (ch);
      *str2++ = (ISLOWER (ch)) ? ch : TOLOWER (ch);
    }
    }
 
 
  *str2 = '\0';
  *str2 = '\0';
  len = str2 - ident;
  len = str2 - ident;
 
 
  ch = ident[0];
  ch = ident[0];
  for (ptr = &suffix_relocs[0]; ptr->length > 0; ptr++)
  for (ptr = &suffix_relocs[0]; ptr->length > 0; ptr++)
    if (ch == ptr->suffix[0]
    if (ch == ptr->suffix[0]
        && len == ptr->length
        && len == ptr->length
        && memcmp (ident, ptr->suffix, ptr->length) == 0)
        && memcmp (ident, ptr->suffix, ptr->length) == 0)
      {
      {
        /* Now check for "identifier@suffix+constant".  */
        /* Now check for "identifier@suffix+constant".  */
        if (*str == '-' || *str == '+')
        if (*str == '-' || *str == '+')
          {
          {
            char *orig_line = input_line_pointer;
            char *orig_line = input_line_pointer;
            expressionS new_exp;
            expressionS new_exp;
 
 
            input_line_pointer = str;
            input_line_pointer = str;
            expression (&new_exp);
            expression (&new_exp);
            if (new_exp.X_op == O_constant)
            if (new_exp.X_op == O_constant)
              {
              {
                exp_p->X_add_number += new_exp.X_add_number;
                exp_p->X_add_number += new_exp.X_add_number;
                str = input_line_pointer;
                str = input_line_pointer;
              }
              }
 
 
            if (&input_line_pointer != str_p)
            if (&input_line_pointer != str_p)
              input_line_pointer = orig_line;
              input_line_pointer = orig_line;
          }
          }
 
 
        *str_p = str;
        *str_p = str;
        return ptr->reloc;
        return ptr->reloc;
      }
      }
 
 
  return BFD_RELOC_UNUSED;
  return BFD_RELOC_UNUSED;
}
}
 
 
 
 
/* Find the matching operator type.  */
/* Find the matching operator type.  */
static unsigned char
static unsigned char
map_suffix_reloc_to_operator (bfd_reloc_code_real_type reloc)
map_suffix_reloc_to_operator (bfd_reloc_code_real_type reloc)
{
{
  struct suffix_reloc_map *sfx;
  struct suffix_reloc_map *sfx;
  unsigned char operator = (unsigned char) -1;
  unsigned char operator = (unsigned char) -1;
 
 
  for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
  for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
    {
    {
      if (sfx->reloc == reloc)
      if (sfx->reloc == reloc)
        {
        {
          operator = sfx->operator;
          operator = sfx->operator;
          break;
          break;
        }
        }
    }
    }
  gas_assert (operator != (unsigned char) -1);
  gas_assert (operator != (unsigned char) -1);
  return operator;
  return operator;
}
}
 
 
 
 
/* Find the matching reloc type.  */
/* Find the matching reloc type.  */
static bfd_reloc_code_real_type
static bfd_reloc_code_real_type
map_operator_to_reloc (unsigned char operator, bfd_boolean is_literal)
map_operator_to_reloc (unsigned char operator, bfd_boolean is_literal)
{
{
  struct suffix_reloc_map *sfx;
  struct suffix_reloc_map *sfx;
  bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
  bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
 
 
  for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
  for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
    {
    {
      if (sfx->operator == operator)
      if (sfx->operator == operator)
        {
        {
          reloc = sfx->reloc;
          reloc = sfx->reloc;
          break;
          break;
        }
        }
    }
    }
 
 
  if (is_literal)
  if (is_literal)
    {
    {
      if (reloc == BFD_RELOC_XTENSA_TLS_FUNC)
      if (reloc == BFD_RELOC_XTENSA_TLS_FUNC)
        return BFD_RELOC_XTENSA_TLSDESC_FN;
        return BFD_RELOC_XTENSA_TLSDESC_FN;
      else if (reloc == BFD_RELOC_XTENSA_TLS_ARG)
      else if (reloc == BFD_RELOC_XTENSA_TLS_ARG)
        return BFD_RELOC_XTENSA_TLSDESC_ARG;
        return BFD_RELOC_XTENSA_TLSDESC_ARG;
    }
    }
 
 
  if (reloc == BFD_RELOC_UNUSED)
  if (reloc == BFD_RELOC_UNUSED)
    return BFD_RELOC_32;
    return BFD_RELOC_32;
 
 
  return reloc;
  return reloc;
}
}
 
 
 
 
static const char *
static const char *
expression_end (const char *name)
expression_end (const char *name)
{
{
  while (1)
  while (1)
    {
    {
      switch (*name)
      switch (*name)
        {
        {
        case '}':
        case '}':
        case ';':
        case ';':
        case '\0':
        case '\0':
        case ',':
        case ',':
        case ':':
        case ':':
          return name;
          return name;
        case ' ':
        case ' ':
        case '\t':
        case '\t':
          ++name;
          ++name;
          continue;
          continue;
        default:
        default:
          return 0;
          return 0;
        }
        }
    }
    }
}
}
 
 
 
 
#define ERROR_REG_NUM ((unsigned) -1)
#define ERROR_REG_NUM ((unsigned) -1)
 
 
static unsigned
static unsigned
tc_get_register (const char *prefix)
tc_get_register (const char *prefix)
{
{
  unsigned reg;
  unsigned reg;
  const char *next_expr;
  const char *next_expr;
  const char *old_line_pointer;
  const char *old_line_pointer;
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
  old_line_pointer = input_line_pointer;
  old_line_pointer = input_line_pointer;
 
 
  if (*input_line_pointer == '$')
  if (*input_line_pointer == '$')
    ++input_line_pointer;
    ++input_line_pointer;
 
 
  /* Accept "sp" as a synonym for "a1".  */
  /* Accept "sp" as a synonym for "a1".  */
  if (input_line_pointer[0] == 's' && input_line_pointer[1] == 'p'
  if (input_line_pointer[0] == 's' && input_line_pointer[1] == 'p'
      && expression_end (input_line_pointer + 2))
      && expression_end (input_line_pointer + 2))
    {
    {
      input_line_pointer += 2;
      input_line_pointer += 2;
      return 1;  /* AR[1] */
      return 1;  /* AR[1] */
    }
    }
 
 
  while (*input_line_pointer++ == *prefix++)
  while (*input_line_pointer++ == *prefix++)
    ;
    ;
  --input_line_pointer;
  --input_line_pointer;
  --prefix;
  --prefix;
 
 
  if (*prefix)
  if (*prefix)
    {
    {
      as_bad (_("bad register name: %s"), old_line_pointer);
      as_bad (_("bad register name: %s"), old_line_pointer);
      return ERROR_REG_NUM;
      return ERROR_REG_NUM;
    }
    }
 
 
  if (!ISDIGIT ((unsigned char) *input_line_pointer))
  if (!ISDIGIT ((unsigned char) *input_line_pointer))
    {
    {
      as_bad (_("bad register number: %s"), input_line_pointer);
      as_bad (_("bad register number: %s"), input_line_pointer);
      return ERROR_REG_NUM;
      return ERROR_REG_NUM;
    }
    }
 
 
  reg = 0;
  reg = 0;
 
 
  while (ISDIGIT ((int) *input_line_pointer))
  while (ISDIGIT ((int) *input_line_pointer))
    reg = reg * 10 + *input_line_pointer++ - '0';
    reg = reg * 10 + *input_line_pointer++ - '0';
 
 
  if (!(next_expr = expression_end (input_line_pointer)))
  if (!(next_expr = expression_end (input_line_pointer)))
    {
    {
      as_bad (_("bad register name: %s"), old_line_pointer);
      as_bad (_("bad register name: %s"), old_line_pointer);
      return ERROR_REG_NUM;
      return ERROR_REG_NUM;
    }
    }
 
 
  input_line_pointer = (char *) next_expr;
  input_line_pointer = (char *) next_expr;
 
 
  return reg;
  return reg;
}
}
 
 
 
 
static void
static void
expression_maybe_register (xtensa_opcode opc, int opnd, expressionS *tok)
expression_maybe_register (xtensa_opcode opc, int opnd, expressionS *tok)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
 
 
  /* Check if this is an immediate operand.  */
  /* Check if this is an immediate operand.  */
  if (xtensa_operand_is_register (isa, opc, opnd) == 0)
  if (xtensa_operand_is_register (isa, opc, opnd) == 0)
    {
    {
      bfd_reloc_code_real_type reloc;
      bfd_reloc_code_real_type reloc;
      segT t = expression (tok);
      segT t = expression (tok);
      if (t == absolute_section
      if (t == absolute_section
          && xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
          && xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
        {
        {
          gas_assert (tok->X_op == O_constant);
          gas_assert (tok->X_op == O_constant);
          tok->X_op = O_symbol;
          tok->X_op = O_symbol;
          tok->X_add_symbol = &abs_symbol;
          tok->X_add_symbol = &abs_symbol;
        }
        }
 
 
      if ((tok->X_op == O_constant || tok->X_op == O_symbol)
      if ((tok->X_op == O_constant || tok->X_op == O_symbol)
          && ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
          && ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
              != BFD_RELOC_NONE))
              != BFD_RELOC_NONE))
        {
        {
          switch (reloc)
          switch (reloc)
            {
            {
            case BFD_RELOC_LO16:
            case BFD_RELOC_LO16:
              if (tok->X_op == O_constant)
              if (tok->X_op == O_constant)
                {
                {
                  tok->X_add_number &= 0xffff;
                  tok->X_add_number &= 0xffff;
                  return;
                  return;
                }
                }
              break;
              break;
            case BFD_RELOC_HI16:
            case BFD_RELOC_HI16:
              if (tok->X_op == O_constant)
              if (tok->X_op == O_constant)
                {
                {
                  tok->X_add_number = ((unsigned) tok->X_add_number) >> 16;
                  tok->X_add_number = ((unsigned) tok->X_add_number) >> 16;
                  return;
                  return;
                }
                }
              break;
              break;
            case BFD_RELOC_UNUSED:
            case BFD_RELOC_UNUSED:
              as_bad (_("unsupported relocation"));
              as_bad (_("unsupported relocation"));
              return;
              return;
            case BFD_RELOC_32_PCREL:
            case BFD_RELOC_32_PCREL:
              as_bad (_("pcrel relocation not allowed in an instruction"));
              as_bad (_("pcrel relocation not allowed in an instruction"));
              return;
              return;
            default:
            default:
              break;
              break;
            }
            }
          tok->X_op = map_suffix_reloc_to_operator (reloc);
          tok->X_op = map_suffix_reloc_to_operator (reloc);
        }
        }
    }
    }
  else
  else
    {
    {
      xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
      xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
      unsigned reg = tc_get_register (xtensa_regfile_shortname (isa, opnd_rf));
      unsigned reg = tc_get_register (xtensa_regfile_shortname (isa, opnd_rf));
 
 
      if (reg != ERROR_REG_NUM) /* Already errored */
      if (reg != ERROR_REG_NUM) /* Already errored */
        {
        {
          uint32 buf = reg;
          uint32 buf = reg;
          if (xtensa_operand_encode (isa, opc, opnd, &buf))
          if (xtensa_operand_encode (isa, opc, opnd, &buf))
            as_bad (_("register number out of range"));
            as_bad (_("register number out of range"));
        }
        }
 
 
      tok->X_op = O_register;
      tok->X_op = O_register;
      tok->X_add_symbol = 0;
      tok->X_add_symbol = 0;
      tok->X_add_number = reg;
      tok->X_add_number = reg;
    }
    }
}
}
 
 
 
 
/* Split up the arguments for an opcode or pseudo-op.  */
/* Split up the arguments for an opcode or pseudo-op.  */
 
 
static int
static int
tokenize_arguments (char **args, char *str)
tokenize_arguments (char **args, char *str)
{
{
  char *old_input_line_pointer;
  char *old_input_line_pointer;
  bfd_boolean saw_comma = FALSE;
  bfd_boolean saw_comma = FALSE;
  bfd_boolean saw_arg = FALSE;
  bfd_boolean saw_arg = FALSE;
  bfd_boolean saw_colon = FALSE;
  bfd_boolean saw_colon = FALSE;
  int num_args = 0;
  int num_args = 0;
  char *arg_end, *arg;
  char *arg_end, *arg;
  int arg_len;
  int arg_len;
 
 
  /* Save and restore input_line_pointer around this function.  */
  /* Save and restore input_line_pointer around this function.  */
  old_input_line_pointer = input_line_pointer;
  old_input_line_pointer = input_line_pointer;
  input_line_pointer = str;
  input_line_pointer = str;
 
 
  while (*input_line_pointer)
  while (*input_line_pointer)
    {
    {
      SKIP_WHITESPACE ();
      SKIP_WHITESPACE ();
      switch (*input_line_pointer)
      switch (*input_line_pointer)
        {
        {
        case '\0':
        case '\0':
        case '}':
        case '}':
          goto fini;
          goto fini;
 
 
        case ':':
        case ':':
          input_line_pointer++;
          input_line_pointer++;
          if (saw_comma || saw_colon || !saw_arg)
          if (saw_comma || saw_colon || !saw_arg)
            goto err;
            goto err;
          saw_colon = TRUE;
          saw_colon = TRUE;
          break;
          break;
 
 
        case ',':
        case ',':
          input_line_pointer++;
          input_line_pointer++;
          if (saw_comma || saw_colon || !saw_arg)
          if (saw_comma || saw_colon || !saw_arg)
            goto err;
            goto err;
          saw_comma = TRUE;
          saw_comma = TRUE;
          break;
          break;
 
 
        default:
        default:
          if (!saw_comma && !saw_colon && saw_arg)
          if (!saw_comma && !saw_colon && saw_arg)
            goto err;
            goto err;
 
 
          arg_end = input_line_pointer + 1;
          arg_end = input_line_pointer + 1;
          while (!expression_end (arg_end))
          while (!expression_end (arg_end))
            arg_end += 1;
            arg_end += 1;
 
 
          arg_len = arg_end - input_line_pointer;
          arg_len = arg_end - input_line_pointer;
          arg = (char *) xmalloc ((saw_colon ? 1 : 0) + arg_len + 1);
          arg = (char *) xmalloc ((saw_colon ? 1 : 0) + arg_len + 1);
          args[num_args] = arg;
          args[num_args] = arg;
 
 
          if (saw_colon)
          if (saw_colon)
            *arg++ = ':';
            *arg++ = ':';
          strncpy (arg, input_line_pointer, arg_len);
          strncpy (arg, input_line_pointer, arg_len);
          arg[arg_len] = '\0';
          arg[arg_len] = '\0';
 
 
          input_line_pointer = arg_end;
          input_line_pointer = arg_end;
          num_args += 1;
          num_args += 1;
          saw_comma = FALSE;
          saw_comma = FALSE;
          saw_colon = FALSE;
          saw_colon = FALSE;
          saw_arg = TRUE;
          saw_arg = TRUE;
          break;
          break;
        }
        }
    }
    }
 
 
fini:
fini:
  if (saw_comma || saw_colon)
  if (saw_comma || saw_colon)
    goto err;
    goto err;
  input_line_pointer = old_input_line_pointer;
  input_line_pointer = old_input_line_pointer;
  return num_args;
  return num_args;
 
 
err:
err:
  if (saw_comma)
  if (saw_comma)
    as_bad (_("extra comma"));
    as_bad (_("extra comma"));
  else if (saw_colon)
  else if (saw_colon)
    as_bad (_("extra colon"));
    as_bad (_("extra colon"));
  else if (!saw_arg)
  else if (!saw_arg)
    as_bad (_("missing argument"));
    as_bad (_("missing argument"));
  else
  else
    as_bad (_("missing comma or colon"));
    as_bad (_("missing comma or colon"));
  input_line_pointer = old_input_line_pointer;
  input_line_pointer = old_input_line_pointer;
  return -1;
  return -1;
}
}
 
 
 
 
/* Parse the arguments to an opcode.  Return TRUE on error.  */
/* Parse the arguments to an opcode.  Return TRUE on error.  */
 
 
static bfd_boolean
static bfd_boolean
parse_arguments (TInsn *insn, int num_args, char **arg_strings)
parse_arguments (TInsn *insn, int num_args, char **arg_strings)
{
{
  expressionS *tok, *last_tok;
  expressionS *tok, *last_tok;
  xtensa_opcode opcode = insn->opcode;
  xtensa_opcode opcode = insn->opcode;
  bfd_boolean had_error = TRUE;
  bfd_boolean had_error = TRUE;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int n, num_regs = 0;
  int n, num_regs = 0;
  int opcode_operand_count;
  int opcode_operand_count;
  int opnd_cnt, last_opnd_cnt;
  int opnd_cnt, last_opnd_cnt;
  unsigned int next_reg = 0;
  unsigned int next_reg = 0;
  char *old_input_line_pointer;
  char *old_input_line_pointer;
 
 
  if (insn->insn_type == ITYPE_LITERAL)
  if (insn->insn_type == ITYPE_LITERAL)
    opcode_operand_count = 1;
    opcode_operand_count = 1;
  else
  else
    opcode_operand_count = xtensa_opcode_num_operands (isa, opcode);
    opcode_operand_count = xtensa_opcode_num_operands (isa, opcode);
 
 
  tok = insn->tok;
  tok = insn->tok;
  memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
  memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
 
 
  /* Save and restore input_line_pointer around this function.  */
  /* Save and restore input_line_pointer around this function.  */
  old_input_line_pointer = input_line_pointer;
  old_input_line_pointer = input_line_pointer;
 
 
  last_tok = 0;
  last_tok = 0;
  last_opnd_cnt = -1;
  last_opnd_cnt = -1;
  opnd_cnt = 0;
  opnd_cnt = 0;
 
 
  /* Skip invisible operands.  */
  /* Skip invisible operands.  */
  while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0)
  while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0)
    {
    {
      opnd_cnt += 1;
      opnd_cnt += 1;
      tok++;
      tok++;
    }
    }
 
 
  for (n = 0; n < num_args; n++)
  for (n = 0; n < num_args; n++)
    {
    {
      input_line_pointer = arg_strings[n];
      input_line_pointer = arg_strings[n];
      if (*input_line_pointer == ':')
      if (*input_line_pointer == ':')
        {
        {
          xtensa_regfile opnd_rf;
          xtensa_regfile opnd_rf;
          input_line_pointer++;
          input_line_pointer++;
          if (num_regs == 0)
          if (num_regs == 0)
            goto err;
            goto err;
          gas_assert (opnd_cnt > 0);
          gas_assert (opnd_cnt > 0);
          num_regs--;
          num_regs--;
          opnd_rf = xtensa_operand_regfile (isa, opcode, last_opnd_cnt);
          opnd_rf = xtensa_operand_regfile (isa, opcode, last_opnd_cnt);
          if (next_reg
          if (next_reg
              != tc_get_register (xtensa_regfile_shortname (isa, opnd_rf)))
              != tc_get_register (xtensa_regfile_shortname (isa, opnd_rf)))
            as_warn (_("incorrect register number, ignoring"));
            as_warn (_("incorrect register number, ignoring"));
          next_reg++;
          next_reg++;
        }
        }
      else
      else
        {
        {
          if (opnd_cnt >= opcode_operand_count)
          if (opnd_cnt >= opcode_operand_count)
            {
            {
              as_warn (_("too many arguments"));
              as_warn (_("too many arguments"));
              goto err;
              goto err;
            }
            }
          gas_assert (opnd_cnt < MAX_INSN_ARGS);
          gas_assert (opnd_cnt < MAX_INSN_ARGS);
 
 
          expression_maybe_register (opcode, opnd_cnt, tok);
          expression_maybe_register (opcode, opnd_cnt, tok);
          next_reg = tok->X_add_number + 1;
          next_reg = tok->X_add_number + 1;
 
 
          if (tok->X_op == O_illegal || tok->X_op == O_absent)
          if (tok->X_op == O_illegal || tok->X_op == O_absent)
            goto err;
            goto err;
          if (xtensa_operand_is_register (isa, opcode, opnd_cnt) == 1)
          if (xtensa_operand_is_register (isa, opcode, opnd_cnt) == 1)
            {
            {
              num_regs = xtensa_operand_num_regs (isa, opcode, opnd_cnt) - 1;
              num_regs = xtensa_operand_num_regs (isa, opcode, opnd_cnt) - 1;
              /* minus 1 because we are seeing one right now */
              /* minus 1 because we are seeing one right now */
            }
            }
          else
          else
            num_regs = 0;
            num_regs = 0;
 
 
          last_tok = tok;
          last_tok = tok;
          last_opnd_cnt = opnd_cnt;
          last_opnd_cnt = opnd_cnt;
          demand_empty_rest_of_line ();
          demand_empty_rest_of_line ();
 
 
          do
          do
            {
            {
              opnd_cnt += 1;
              opnd_cnt += 1;
              tok++;
              tok++;
            }
            }
          while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0);
          while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0);
        }
        }
    }
    }
 
 
  if (num_regs > 0 && ((int) next_reg != last_tok->X_add_number + 1))
  if (num_regs > 0 && ((int) next_reg != last_tok->X_add_number + 1))
    goto err;
    goto err;
 
 
  insn->ntok = tok - insn->tok;
  insn->ntok = tok - insn->tok;
  had_error = FALSE;
  had_error = FALSE;
 
 
 err:
 err:
  input_line_pointer = old_input_line_pointer;
  input_line_pointer = old_input_line_pointer;
  return had_error;
  return had_error;
}
}
 
 
 
 
static int
static int
get_invisible_operands (TInsn *insn)
get_invisible_operands (TInsn *insn)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  static xtensa_insnbuf slotbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  xtensa_format fmt;
  xtensa_format fmt;
  xtensa_opcode opc = insn->opcode;
  xtensa_opcode opc = insn->opcode;
  int slot, opnd, fmt_found;
  int slot, opnd, fmt_found;
  unsigned val;
  unsigned val;
 
 
  if (!slotbuf)
  if (!slotbuf)
    slotbuf = xtensa_insnbuf_alloc (isa);
    slotbuf = xtensa_insnbuf_alloc (isa);
 
 
  /* Find format/slot where this can be encoded.  */
  /* Find format/slot where this can be encoded.  */
  fmt_found = 0;
  fmt_found = 0;
  slot = 0;
  slot = 0;
  for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
  for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
    {
    {
      for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
      for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
        {
        {
          if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opc) == 0)
          if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opc) == 0)
            {
            {
              fmt_found = 1;
              fmt_found = 1;
              break;
              break;
            }
            }
        }
        }
      if (fmt_found) break;
      if (fmt_found) break;
    }
    }
 
 
  if (!fmt_found)
  if (!fmt_found)
    {
    {
      as_bad (_("cannot encode opcode \"%s\""), xtensa_opcode_name (isa, opc));
      as_bad (_("cannot encode opcode \"%s\""), xtensa_opcode_name (isa, opc));
      return -1;
      return -1;
    }
    }
 
 
  /* First encode all the visible operands
  /* First encode all the visible operands
     (to deal with shared field operands).  */
     (to deal with shared field operands).  */
  for (opnd = 0; opnd < insn->ntok; opnd++)
  for (opnd = 0; opnd < insn->ntok; opnd++)
    {
    {
      if (xtensa_operand_is_visible (isa, opc, opnd) == 1
      if (xtensa_operand_is_visible (isa, opc, opnd) == 1
          && (insn->tok[opnd].X_op == O_register
          && (insn->tok[opnd].X_op == O_register
              || insn->tok[opnd].X_op == O_constant))
              || insn->tok[opnd].X_op == O_constant))
        {
        {
          val = insn->tok[opnd].X_add_number;
          val = insn->tok[opnd].X_add_number;
          xtensa_operand_encode (isa, opc, opnd, &val);
          xtensa_operand_encode (isa, opc, opnd, &val);
          xtensa_operand_set_field (isa, opc, opnd, fmt, slot, slotbuf, val);
          xtensa_operand_set_field (isa, opc, opnd, fmt, slot, slotbuf, val);
        }
        }
    }
    }
 
 
  /* Then pull out the values for the invisible ones.  */
  /* Then pull out the values for the invisible ones.  */
  for (opnd = 0; opnd < insn->ntok; opnd++)
  for (opnd = 0; opnd < insn->ntok; opnd++)
    {
    {
      if (xtensa_operand_is_visible (isa, opc, opnd) == 0)
      if (xtensa_operand_is_visible (isa, opc, opnd) == 0)
        {
        {
          xtensa_operand_get_field (isa, opc, opnd, fmt, slot, slotbuf, &val);
          xtensa_operand_get_field (isa, opc, opnd, fmt, slot, slotbuf, &val);
          xtensa_operand_decode (isa, opc, opnd, &val);
          xtensa_operand_decode (isa, opc, opnd, &val);
          insn->tok[opnd].X_add_number = val;
          insn->tok[opnd].X_add_number = val;
          if (xtensa_operand_is_register (isa, opc, opnd) == 1)
          if (xtensa_operand_is_register (isa, opc, opnd) == 1)
            insn->tok[opnd].X_op = O_register;
            insn->tok[opnd].X_op = O_register;
          else
          else
            insn->tok[opnd].X_op = O_constant;
            insn->tok[opnd].X_op = O_constant;
        }
        }
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
 
 
static void
static void
xg_reverse_shift_count (char **cnt_argp)
xg_reverse_shift_count (char **cnt_argp)
{
{
  char *cnt_arg, *new_arg;
  char *cnt_arg, *new_arg;
  cnt_arg = *cnt_argp;
  cnt_arg = *cnt_argp;
 
 
  /* replace the argument with "31-(argument)" */
  /* replace the argument with "31-(argument)" */
  new_arg = (char *) xmalloc (strlen (cnt_arg) + 6);
  new_arg = (char *) xmalloc (strlen (cnt_arg) + 6);
  sprintf (new_arg, "31-(%s)", cnt_arg);
  sprintf (new_arg, "31-(%s)", cnt_arg);
 
 
  free (cnt_arg);
  free (cnt_arg);
  *cnt_argp = new_arg;
  *cnt_argp = new_arg;
}
}
 
 
 
 
/* If "arg" is a constant expression, return non-zero with the value
/* If "arg" is a constant expression, return non-zero with the value
   in *valp.  */
   in *valp.  */
 
 
static int
static int
xg_arg_is_constant (char *arg, offsetT *valp)
xg_arg_is_constant (char *arg, offsetT *valp)
{
{
  expressionS exp;
  expressionS exp;
  char *save_ptr = input_line_pointer;
  char *save_ptr = input_line_pointer;
 
 
  input_line_pointer = arg;
  input_line_pointer = arg;
  expression (&exp);
  expression (&exp);
  input_line_pointer = save_ptr;
  input_line_pointer = save_ptr;
 
 
  if (exp.X_op == O_constant)
  if (exp.X_op == O_constant)
    {
    {
      *valp = exp.X_add_number;
      *valp = exp.X_add_number;
      return 1;
      return 1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
 
 
static void
static void
xg_replace_opname (char **popname, char *newop)
xg_replace_opname (char **popname, char *newop)
{
{
  free (*popname);
  free (*popname);
  *popname = (char *) xmalloc (strlen (newop) + 1);
  *popname = (char *) xmalloc (strlen (newop) + 1);
  strcpy (*popname, newop);
  strcpy (*popname, newop);
}
}
 
 
 
 
static int
static int
xg_check_num_args (int *pnum_args,
xg_check_num_args (int *pnum_args,
                   int expected_num,
                   int expected_num,
                   char *opname,
                   char *opname,
                   char **arg_strings)
                   char **arg_strings)
{
{
  int num_args = *pnum_args;
  int num_args = *pnum_args;
 
 
  if (num_args < expected_num)
  if (num_args < expected_num)
    {
    {
      as_bad (_("not enough operands (%d) for '%s'; expected %d"),
      as_bad (_("not enough operands (%d) for '%s'; expected %d"),
              num_args, opname, expected_num);
              num_args, opname, expected_num);
      return -1;
      return -1;
    }
    }
 
 
  if (num_args > expected_num)
  if (num_args > expected_num)
    {
    {
      as_warn (_("too many operands (%d) for '%s'; expected %d"),
      as_warn (_("too many operands (%d) for '%s'; expected %d"),
               num_args, opname, expected_num);
               num_args, opname, expected_num);
      while (num_args-- > expected_num)
      while (num_args-- > expected_num)
        {
        {
          free (arg_strings[num_args]);
          free (arg_strings[num_args]);
          arg_strings[num_args] = 0;
          arg_strings[num_args] = 0;
        }
        }
      *pnum_args = expected_num;
      *pnum_args = expected_num;
      return -1;
      return -1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
 
 
/* If the register is not specified as part of the opcode,
/* If the register is not specified as part of the opcode,
   then get it from the operand and move it to the opcode.  */
   then get it from the operand and move it to the opcode.  */
 
 
static int
static int
xg_translate_sysreg_op (char **popname, int *pnum_args, char **arg_strings)
xg_translate_sysreg_op (char **popname, int *pnum_args, char **arg_strings)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_sysreg sr;
  xtensa_sysreg sr;
  char *opname, *new_opname;
  char *opname, *new_opname;
  const char *sr_name;
  const char *sr_name;
  int is_user, is_write;
  int is_user, is_write;
 
 
  opname = *popname;
  opname = *popname;
  if (*opname == '_')
  if (*opname == '_')
    opname += 1;
    opname += 1;
  is_user = (opname[1] == 'u');
  is_user = (opname[1] == 'u');
  is_write = (opname[0] == 'w');
  is_write = (opname[0] == 'w');
 
 
  /* Opname == [rw]ur or [rwx]sr... */
  /* Opname == [rw]ur or [rwx]sr... */
 
 
  if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
  if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
    return -1;
    return -1;
 
 
  /* Check if the argument is a symbolic register name.  */
  /* Check if the argument is a symbolic register name.  */
  sr = xtensa_sysreg_lookup_name (isa, arg_strings[1]);
  sr = xtensa_sysreg_lookup_name (isa, arg_strings[1]);
  /* Handle WSR to "INTSET" as a special case.  */
  /* Handle WSR to "INTSET" as a special case.  */
  if (sr == XTENSA_UNDEFINED && is_write && !is_user
  if (sr == XTENSA_UNDEFINED && is_write && !is_user
      && !strcasecmp (arg_strings[1], "intset"))
      && !strcasecmp (arg_strings[1], "intset"))
    sr = xtensa_sysreg_lookup_name (isa, "interrupt");
    sr = xtensa_sysreg_lookup_name (isa, "interrupt");
  if (sr == XTENSA_UNDEFINED
  if (sr == XTENSA_UNDEFINED
      || (xtensa_sysreg_is_user (isa, sr) == 1) != is_user)
      || (xtensa_sysreg_is_user (isa, sr) == 1) != is_user)
    {
    {
      /* Maybe it's a register number.... */
      /* Maybe it's a register number.... */
      offsetT val;
      offsetT val;
      if (!xg_arg_is_constant (arg_strings[1], &val))
      if (!xg_arg_is_constant (arg_strings[1], &val))
        {
        {
          as_bad (_("invalid register '%s' for '%s' instruction"),
          as_bad (_("invalid register '%s' for '%s' instruction"),
                  arg_strings[1], opname);
                  arg_strings[1], opname);
          return -1;
          return -1;
        }
        }
      sr = xtensa_sysreg_lookup (isa, val, is_user);
      sr = xtensa_sysreg_lookup (isa, val, is_user);
      if (sr == XTENSA_UNDEFINED)
      if (sr == XTENSA_UNDEFINED)
        {
        {
          as_bad (_("invalid register number (%ld) for '%s' instruction"),
          as_bad (_("invalid register number (%ld) for '%s' instruction"),
                  (long) val, opname);
                  (long) val, opname);
          return -1;
          return -1;
        }
        }
    }
    }
 
 
  /* Remove the last argument, which is now part of the opcode.  */
  /* Remove the last argument, which is now part of the opcode.  */
  free (arg_strings[1]);
  free (arg_strings[1]);
  arg_strings[1] = 0;
  arg_strings[1] = 0;
  *pnum_args = 1;
  *pnum_args = 1;
 
 
  /* Translate the opcode.  */
  /* Translate the opcode.  */
  sr_name = xtensa_sysreg_name (isa, sr);
  sr_name = xtensa_sysreg_name (isa, sr);
  /* Another special case for "WSR.INTSET"....  */
  /* Another special case for "WSR.INTSET"....  */
  if (is_write && !is_user && !strcasecmp ("interrupt", sr_name))
  if (is_write && !is_user && !strcasecmp ("interrupt", sr_name))
    sr_name = "intset";
    sr_name = "intset";
  new_opname = (char *) xmalloc (strlen (sr_name) + 6);
  new_opname = (char *) xmalloc (strlen (sr_name) + 6);
  sprintf (new_opname, "%s.%s", *popname, sr_name);
  sprintf (new_opname, "%s.%s", *popname, sr_name);
  free (*popname);
  free (*popname);
  *popname = new_opname;
  *popname = new_opname;
 
 
  return 0;
  return 0;
}
}
 
 
 
 
static int
static int
xtensa_translate_old_userreg_ops (char **popname)
xtensa_translate_old_userreg_ops (char **popname)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_sysreg sr;
  xtensa_sysreg sr;
  char *opname, *new_opname;
  char *opname, *new_opname;
  const char *sr_name;
  const char *sr_name;
  bfd_boolean has_underbar = FALSE;
  bfd_boolean has_underbar = FALSE;
 
 
  opname = *popname;
  opname = *popname;
  if (opname[0] == '_')
  if (opname[0] == '_')
    {
    {
      has_underbar = TRUE;
      has_underbar = TRUE;
      opname += 1;
      opname += 1;
    }
    }
 
 
  sr = xtensa_sysreg_lookup_name (isa, opname + 1);
  sr = xtensa_sysreg_lookup_name (isa, opname + 1);
  if (sr != XTENSA_UNDEFINED)
  if (sr != XTENSA_UNDEFINED)
    {
    {
      /* The new default name ("nnn") is different from the old default
      /* The new default name ("nnn") is different from the old default
         name ("URnnn").  The old default is handled below, and we don't
         name ("URnnn").  The old default is handled below, and we don't
         want to recognize [RW]nnn, so do nothing if the name is the (new)
         want to recognize [RW]nnn, so do nothing if the name is the (new)
         default.  */
         default.  */
      static char namebuf[10];
      static char namebuf[10];
      sprintf (namebuf, "%d", xtensa_sysreg_number (isa, sr));
      sprintf (namebuf, "%d", xtensa_sysreg_number (isa, sr));
      if (strcmp (namebuf, opname + 1) == 0)
      if (strcmp (namebuf, opname + 1) == 0)
        return 0;
        return 0;
    }
    }
  else
  else
    {
    {
      offsetT val;
      offsetT val;
      char *end;
      char *end;
 
 
      /* Only continue if the reg name is "URnnn".  */
      /* Only continue if the reg name is "URnnn".  */
      if (opname[1] != 'u' || opname[2] != 'r')
      if (opname[1] != 'u' || opname[2] != 'r')
        return 0;
        return 0;
      val = strtoul (opname + 3, &end, 10);
      val = strtoul (opname + 3, &end, 10);
      if (*end != '\0')
      if (*end != '\0')
        return 0;
        return 0;
 
 
      sr = xtensa_sysreg_lookup (isa, val, 1);
      sr = xtensa_sysreg_lookup (isa, val, 1);
      if (sr == XTENSA_UNDEFINED)
      if (sr == XTENSA_UNDEFINED)
        {
        {
          as_bad (_("invalid register number (%ld) for '%s'"),
          as_bad (_("invalid register number (%ld) for '%s'"),
                  (long) val, opname);
                  (long) val, opname);
          return -1;
          return -1;
        }
        }
    }
    }
 
 
  /* Translate the opcode.  */
  /* Translate the opcode.  */
  sr_name = xtensa_sysreg_name (isa, sr);
  sr_name = xtensa_sysreg_name (isa, sr);
  new_opname = (char *) xmalloc (strlen (sr_name) + 6);
  new_opname = (char *) xmalloc (strlen (sr_name) + 6);
  sprintf (new_opname, "%s%cur.%s", (has_underbar ? "_" : ""),
  sprintf (new_opname, "%s%cur.%s", (has_underbar ? "_" : ""),
           opname[0], sr_name);
           opname[0], sr_name);
  free (*popname);
  free (*popname);
  *popname = new_opname;
  *popname = new_opname;
 
 
  return 0;
  return 0;
}
}
 
 
 
 
static int
static int
xtensa_translate_zero_immed (char *old_op,
xtensa_translate_zero_immed (char *old_op,
                             char *new_op,
                             char *new_op,
                             char **popname,
                             char **popname,
                             int *pnum_args,
                             int *pnum_args,
                             char **arg_strings)
                             char **arg_strings)
{
{
  char *opname;
  char *opname;
  offsetT val;
  offsetT val;
 
 
  opname = *popname;
  opname = *popname;
  gas_assert (opname[0] != '_');
  gas_assert (opname[0] != '_');
 
 
  if (strcmp (opname, old_op) != 0)
  if (strcmp (opname, old_op) != 0)
    return 0;
    return 0;
 
 
  if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
  if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
    return -1;
    return -1;
  if (xg_arg_is_constant (arg_strings[1], &val) && val == 0)
  if (xg_arg_is_constant (arg_strings[1], &val) && val == 0)
    {
    {
      xg_replace_opname (popname, new_op);
      xg_replace_opname (popname, new_op);
      free (arg_strings[1]);
      free (arg_strings[1]);
      arg_strings[1] = arg_strings[2];
      arg_strings[1] = arg_strings[2];
      arg_strings[2] = 0;
      arg_strings[2] = 0;
      *pnum_args = 2;
      *pnum_args = 2;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
 
 
/* If the instruction is an idiom (i.e., a built-in macro), translate it.
/* If the instruction is an idiom (i.e., a built-in macro), translate it.
   Returns non-zero if an error was found.  */
   Returns non-zero if an error was found.  */
 
 
static int
static int
xg_translate_idioms (char **popname, int *pnum_args, char **arg_strings)
xg_translate_idioms (char **popname, int *pnum_args, char **arg_strings)
{
{
  char *opname = *popname;
  char *opname = *popname;
  bfd_boolean has_underbar = FALSE;
  bfd_boolean has_underbar = FALSE;
 
 
  if (*opname == '_')
  if (*opname == '_')
    {
    {
      has_underbar = TRUE;
      has_underbar = TRUE;
      opname += 1;
      opname += 1;
    }
    }
 
 
  if (strcmp (opname, "mov") == 0)
  if (strcmp (opname, "mov") == 0)
    {
    {
      if (use_transform () && !has_underbar && density_supported)
      if (use_transform () && !has_underbar && density_supported)
        xg_replace_opname (popname, "mov.n");
        xg_replace_opname (popname, "mov.n");
      else
      else
        {
        {
          if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
          if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
            return -1;
            return -1;
          xg_replace_opname (popname, (has_underbar ? "_or" : "or"));
          xg_replace_opname (popname, (has_underbar ? "_or" : "or"));
          arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1);
          arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1);
          strcpy (arg_strings[2], arg_strings[1]);
          strcpy (arg_strings[2], arg_strings[1]);
          *pnum_args = 3;
          *pnum_args = 3;
        }
        }
      return 0;
      return 0;
    }
    }
 
 
  if (strcmp (opname, "bbsi.l") == 0)
  if (strcmp (opname, "bbsi.l") == 0)
    {
    {
      if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
      if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
        return -1;
        return -1;
      xg_replace_opname (popname, (has_underbar ? "_bbsi" : "bbsi"));
      xg_replace_opname (popname, (has_underbar ? "_bbsi" : "bbsi"));
      if (target_big_endian)
      if (target_big_endian)
        xg_reverse_shift_count (&arg_strings[1]);
        xg_reverse_shift_count (&arg_strings[1]);
      return 0;
      return 0;
    }
    }
 
 
  if (strcmp (opname, "bbci.l") == 0)
  if (strcmp (opname, "bbci.l") == 0)
    {
    {
      if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
      if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
        return -1;
        return -1;
      xg_replace_opname (popname, (has_underbar ? "_bbci" : "bbci"));
      xg_replace_opname (popname, (has_underbar ? "_bbci" : "bbci"));
      if (target_big_endian)
      if (target_big_endian)
        xg_reverse_shift_count (&arg_strings[1]);
        xg_reverse_shift_count (&arg_strings[1]);
      return 0;
      return 0;
    }
    }
 
 
  /* Don't do anything special with NOPs inside FLIX instructions.  They
  /* Don't do anything special with NOPs inside FLIX instructions.  They
     are handled elsewhere.  Real NOP instructions are always available
     are handled elsewhere.  Real NOP instructions are always available
     in configurations with FLIX, so this should never be an issue but
     in configurations with FLIX, so this should never be an issue but
     check for it anyway.  */
     check for it anyway.  */
  if (!cur_vinsn.inside_bundle && xtensa_nop_opcode == XTENSA_UNDEFINED
  if (!cur_vinsn.inside_bundle && xtensa_nop_opcode == XTENSA_UNDEFINED
      && strcmp (opname, "nop") == 0)
      && strcmp (opname, "nop") == 0)
    {
    {
      if (use_transform () && !has_underbar && density_supported)
      if (use_transform () && !has_underbar && density_supported)
        xg_replace_opname (popname, "nop.n");
        xg_replace_opname (popname, "nop.n");
      else
      else
        {
        {
          if (xg_check_num_args (pnum_args, 0, opname, arg_strings))
          if (xg_check_num_args (pnum_args, 0, opname, arg_strings))
            return -1;
            return -1;
          xg_replace_opname (popname, (has_underbar ? "_or" : "or"));
          xg_replace_opname (popname, (has_underbar ? "_or" : "or"));
          arg_strings[0] = (char *) xmalloc (3);
          arg_strings[0] = (char *) xmalloc (3);
          arg_strings[1] = (char *) xmalloc (3);
          arg_strings[1] = (char *) xmalloc (3);
          arg_strings[2] = (char *) xmalloc (3);
          arg_strings[2] = (char *) xmalloc (3);
          strcpy (arg_strings[0], "a1");
          strcpy (arg_strings[0], "a1");
          strcpy (arg_strings[1], "a1");
          strcpy (arg_strings[1], "a1");
          strcpy (arg_strings[2], "a1");
          strcpy (arg_strings[2], "a1");
          *pnum_args = 3;
          *pnum_args = 3;
        }
        }
      return 0;
      return 0;
    }
    }
 
 
  /* Recognize [RW]UR and [RWX]SR.  */
  /* Recognize [RW]UR and [RWX]SR.  */
  if ((((opname[0] == 'r' || opname[0] == 'w')
  if ((((opname[0] == 'r' || opname[0] == 'w')
        && (opname[1] == 'u' || opname[1] == 's'))
        && (opname[1] == 'u' || opname[1] == 's'))
       || (opname[0] == 'x' && opname[1] == 's'))
       || (opname[0] == 'x' && opname[1] == 's'))
      && opname[2] == 'r'
      && opname[2] == 'r'
      && opname[3] == '\0')
      && opname[3] == '\0')
    return xg_translate_sysreg_op (popname, pnum_args, arg_strings);
    return xg_translate_sysreg_op (popname, pnum_args, arg_strings);
 
 
  /* Backward compatibility for RUR and WUR: Recognize [RW]UR<nnn> and
  /* Backward compatibility for RUR and WUR: Recognize [RW]UR<nnn> and
     [RW]<name> if <name> is the non-default name of a user register.  */
     [RW]<name> if <name> is the non-default name of a user register.  */
  if ((opname[0] == 'r' || opname[0] == 'w')
  if ((opname[0] == 'r' || opname[0] == 'w')
      && xtensa_opcode_lookup (xtensa_default_isa, opname) == XTENSA_UNDEFINED)
      && xtensa_opcode_lookup (xtensa_default_isa, opname) == XTENSA_UNDEFINED)
    return xtensa_translate_old_userreg_ops (popname);
    return xtensa_translate_old_userreg_ops (popname);
 
 
  /* Relax branches that don't allow comparisons against an immediate value
  /* Relax branches that don't allow comparisons against an immediate value
     of zero to the corresponding branches with implicit zero immediates.  */
     of zero to the corresponding branches with implicit zero immediates.  */
  if (!has_underbar && use_transform ())
  if (!has_underbar && use_transform ())
    {
    {
      if (xtensa_translate_zero_immed ("bnei", "bnez", popname,
      if (xtensa_translate_zero_immed ("bnei", "bnez", popname,
                                       pnum_args, arg_strings))
                                       pnum_args, arg_strings))
        return -1;
        return -1;
 
 
      if (xtensa_translate_zero_immed ("beqi", "beqz", popname,
      if (xtensa_translate_zero_immed ("beqi", "beqz", popname,
                                       pnum_args, arg_strings))
                                       pnum_args, arg_strings))
        return -1;
        return -1;
 
 
      if (xtensa_translate_zero_immed ("bgei", "bgez", popname,
      if (xtensa_translate_zero_immed ("bgei", "bgez", popname,
                                       pnum_args, arg_strings))
                                       pnum_args, arg_strings))
        return -1;
        return -1;
 
 
      if (xtensa_translate_zero_immed ("blti", "bltz", popname,
      if (xtensa_translate_zero_immed ("blti", "bltz", popname,
                                       pnum_args, arg_strings))
                                       pnum_args, arg_strings))
        return -1;
        return -1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 


/* Functions for dealing with the Xtensa ISA.  */
/* Functions for dealing with the Xtensa ISA.  */
 
 
/* Currently the assembler only allows us to use a single target per
/* Currently the assembler only allows us to use a single target per
   fragment.  Because of this, only one operand for a given
   fragment.  Because of this, only one operand for a given
   instruction may be symbolic.  If there is a PC-relative operand,
   instruction may be symbolic.  If there is a PC-relative operand,
   the last one is chosen.  Otherwise, the result is the number of the
   the last one is chosen.  Otherwise, the result is the number of the
   last immediate operand, and if there are none of those, we fail and
   last immediate operand, and if there are none of those, we fail and
   return -1.  */
   return -1.  */
 
 
static int
static int
get_relaxable_immed (xtensa_opcode opcode)
get_relaxable_immed (xtensa_opcode opcode)
{
{
  int last_immed = -1;
  int last_immed = -1;
  int noperands, opi;
  int noperands, opi;
 
 
  if (opcode == XTENSA_UNDEFINED)
  if (opcode == XTENSA_UNDEFINED)
    return -1;
    return -1;
 
 
  noperands = xtensa_opcode_num_operands (xtensa_default_isa, opcode);
  noperands = xtensa_opcode_num_operands (xtensa_default_isa, opcode);
  for (opi = noperands - 1; opi >= 0; opi--)
  for (opi = noperands - 1; opi >= 0; opi--)
    {
    {
      if (xtensa_operand_is_visible (xtensa_default_isa, opcode, opi) == 0)
      if (xtensa_operand_is_visible (xtensa_default_isa, opcode, opi) == 0)
        continue;
        continue;
      if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, opi) == 1)
      if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, opi) == 1)
        return opi;
        return opi;
      if (last_immed == -1
      if (last_immed == -1
          && xtensa_operand_is_register (xtensa_default_isa, opcode, opi) == 0)
          && xtensa_operand_is_register (xtensa_default_isa, opcode, opi) == 0)
        last_immed = opi;
        last_immed = opi;
    }
    }
  return last_immed;
  return last_immed;
}
}
 
 
 
 
static xtensa_opcode
static xtensa_opcode
get_opcode_from_buf (const char *buf, int slot)
get_opcode_from_buf (const char *buf, int slot)
{
{
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_format fmt;
  xtensa_format fmt;
 
 
  if (!insnbuf)
  if (!insnbuf)
    {
    {
      insnbuf = xtensa_insnbuf_alloc (isa);
      insnbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
    }
    }
 
 
  xtensa_insnbuf_from_chars (isa, insnbuf, (const unsigned char *) buf, 0);
  xtensa_insnbuf_from_chars (isa, insnbuf, (const unsigned char *) buf, 0);
  fmt = xtensa_format_decode (isa, insnbuf);
  fmt = xtensa_format_decode (isa, insnbuf);
  if (fmt == XTENSA_UNDEFINED)
  if (fmt == XTENSA_UNDEFINED)
    return XTENSA_UNDEFINED;
    return XTENSA_UNDEFINED;
 
 
  if (slot >= xtensa_format_num_slots (isa, fmt))
  if (slot >= xtensa_format_num_slots (isa, fmt))
    return XTENSA_UNDEFINED;
    return XTENSA_UNDEFINED;
 
 
  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
  return xtensa_opcode_decode (isa, fmt, slot, slotbuf);
  return xtensa_opcode_decode (isa, fmt, slot, slotbuf);
}
}
 
 
 
 
#ifdef TENSILICA_DEBUG
#ifdef TENSILICA_DEBUG
 
 
/* For debugging, print out the mapping of opcode numbers to opcodes.  */
/* For debugging, print out the mapping of opcode numbers to opcodes.  */
 
 
static void
static void
xtensa_print_insn_table (void)
xtensa_print_insn_table (void)
{
{
  int num_opcodes, num_operands;
  int num_opcodes, num_operands;
  xtensa_opcode opcode;
  xtensa_opcode opcode;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
 
 
  num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
  num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
  for (opcode = 0; opcode < num_opcodes; opcode++)
  for (opcode = 0; opcode < num_opcodes; opcode++)
    {
    {
      int opn;
      int opn;
      fprintf (stderr, "%d: %s: ", opcode, xtensa_opcode_name (isa, opcode));
      fprintf (stderr, "%d: %s: ", opcode, xtensa_opcode_name (isa, opcode));
      num_operands = xtensa_opcode_num_operands (isa, opcode);
      num_operands = xtensa_opcode_num_operands (isa, opcode);
      for (opn = 0; opn < num_operands; opn++)
      for (opn = 0; opn < num_operands; opn++)
        {
        {
          if (xtensa_operand_is_visible (isa, opcode, opn) == 0)
          if (xtensa_operand_is_visible (isa, opcode, opn) == 0)
            continue;
            continue;
          if (xtensa_operand_is_register (isa, opcode, opn) == 1)
          if (xtensa_operand_is_register (isa, opcode, opn) == 1)
            {
            {
              xtensa_regfile opnd_rf =
              xtensa_regfile opnd_rf =
                xtensa_operand_regfile (isa, opcode, opn);
                xtensa_operand_regfile (isa, opcode, opn);
              fprintf (stderr, "%s ", xtensa_regfile_shortname (isa, opnd_rf));
              fprintf (stderr, "%s ", xtensa_regfile_shortname (isa, opnd_rf));
            }
            }
          else if (xtensa_operand_is_PCrelative (isa, opcode, opn) == 1)
          else if (xtensa_operand_is_PCrelative (isa, opcode, opn) == 1)
            fputs ("[lLr] ", stderr);
            fputs ("[lLr] ", stderr);
          else
          else
            fputs ("i ", stderr);
            fputs ("i ", stderr);
        }
        }
      fprintf (stderr, "\n");
      fprintf (stderr, "\n");
    }
    }
}
}
 
 
 
 
static void
static void
print_vliw_insn (xtensa_insnbuf vbuf)
print_vliw_insn (xtensa_insnbuf vbuf)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_format f = xtensa_format_decode (isa, vbuf);
  xtensa_format f = xtensa_format_decode (isa, vbuf);
  xtensa_insnbuf sbuf = xtensa_insnbuf_alloc (isa);
  xtensa_insnbuf sbuf = xtensa_insnbuf_alloc (isa);
  int op;
  int op;
 
 
  fprintf (stderr, "format = %d\n", f);
  fprintf (stderr, "format = %d\n", f);
 
 
  for (op = 0; op < xtensa_format_num_slots (isa, f); op++)
  for (op = 0; op < xtensa_format_num_slots (isa, f); op++)
    {
    {
      xtensa_opcode opcode;
      xtensa_opcode opcode;
      const char *opname;
      const char *opname;
      int operands;
      int operands;
 
 
      xtensa_format_get_slot (isa, f, op, vbuf, sbuf);
      xtensa_format_get_slot (isa, f, op, vbuf, sbuf);
      opcode = xtensa_opcode_decode (isa, f, op, sbuf);
      opcode = xtensa_opcode_decode (isa, f, op, sbuf);
      opname = xtensa_opcode_name (isa, opcode);
      opname = xtensa_opcode_name (isa, opcode);
 
 
      fprintf (stderr, "op in slot %i is %s;\n", op, opname);
      fprintf (stderr, "op in slot %i is %s;\n", op, opname);
      fprintf (stderr, "   operands = ");
      fprintf (stderr, "   operands = ");
      for (operands = 0;
      for (operands = 0;
           operands < xtensa_opcode_num_operands (isa, opcode);
           operands < xtensa_opcode_num_operands (isa, opcode);
           operands++)
           operands++)
        {
        {
          unsigned int val;
          unsigned int val;
          if (xtensa_operand_is_visible (isa, opcode, operands) == 0)
          if (xtensa_operand_is_visible (isa, opcode, operands) == 0)
            continue;
            continue;
          xtensa_operand_get_field (isa, opcode, operands, f, op, sbuf, &val);
          xtensa_operand_get_field (isa, opcode, operands, f, op, sbuf, &val);
          xtensa_operand_decode (isa, opcode, operands, &val);
          xtensa_operand_decode (isa, opcode, operands, &val);
          fprintf (stderr, "%d ", val);
          fprintf (stderr, "%d ", val);
        }
        }
      fprintf (stderr, "\n");
      fprintf (stderr, "\n");
    }
    }
  xtensa_insnbuf_free (isa, sbuf);
  xtensa_insnbuf_free (isa, sbuf);
}
}
 
 
#endif /* TENSILICA_DEBUG */
#endif /* TENSILICA_DEBUG */
 
 
 
 
static bfd_boolean
static bfd_boolean
is_direct_call_opcode (xtensa_opcode opcode)
is_direct_call_opcode (xtensa_opcode opcode)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int n, num_operands;
  int n, num_operands;
 
 
  if (xtensa_opcode_is_call (isa, opcode) != 1)
  if (xtensa_opcode_is_call (isa, opcode) != 1)
    return FALSE;
    return FALSE;
 
 
  num_operands = xtensa_opcode_num_operands (isa, opcode);
  num_operands = xtensa_opcode_num_operands (isa, opcode);
  for (n = 0; n < num_operands; n++)
  for (n = 0; n < num_operands; n++)
    {
    {
      if (xtensa_operand_is_register (isa, opcode, n) == 0
      if (xtensa_operand_is_register (isa, opcode, n) == 0
          && xtensa_operand_is_PCrelative (isa, opcode, n) == 1)
          && xtensa_operand_is_PCrelative (isa, opcode, n) == 1)
        return TRUE;
        return TRUE;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Convert from BFD relocation type code to slot and operand number.
/* Convert from BFD relocation type code to slot and operand number.
   Returns non-zero on failure.  */
   Returns non-zero on failure.  */
 
 
static int
static int
decode_reloc (bfd_reloc_code_real_type reloc, int *slot, bfd_boolean *is_alt)
decode_reloc (bfd_reloc_code_real_type reloc, int *slot, bfd_boolean *is_alt)
{
{
  if (reloc >= BFD_RELOC_XTENSA_SLOT0_OP
  if (reloc >= BFD_RELOC_XTENSA_SLOT0_OP
      && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
      && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
    {
    {
      *slot = reloc - BFD_RELOC_XTENSA_SLOT0_OP;
      *slot = reloc - BFD_RELOC_XTENSA_SLOT0_OP;
      *is_alt = FALSE;
      *is_alt = FALSE;
    }
    }
  else if (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
  else if (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
      && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT)
      && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT)
    {
    {
      *slot = reloc - BFD_RELOC_XTENSA_SLOT0_ALT;
      *slot = reloc - BFD_RELOC_XTENSA_SLOT0_ALT;
      *is_alt = TRUE;
      *is_alt = TRUE;
    }
    }
  else
  else
    return -1;
    return -1;
 
 
  return 0;
  return 0;
}
}
 
 
 
 
/* Convert from slot number to BFD relocation type code for the
/* Convert from slot number to BFD relocation type code for the
   standard PC-relative relocations.  Return BFD_RELOC_NONE on
   standard PC-relative relocations.  Return BFD_RELOC_NONE on
   failure.  */
   failure.  */
 
 
static bfd_reloc_code_real_type
static bfd_reloc_code_real_type
encode_reloc (int slot)
encode_reloc (int slot)
{
{
  if (slot < 0 || slot > 14)
  if (slot < 0 || slot > 14)
    return BFD_RELOC_NONE;
    return BFD_RELOC_NONE;
 
 
  return BFD_RELOC_XTENSA_SLOT0_OP + slot;
  return BFD_RELOC_XTENSA_SLOT0_OP + slot;
}
}
 
 
 
 
/* Convert from slot numbers to BFD relocation type code for the
/* Convert from slot numbers to BFD relocation type code for the
   "alternate" relocations.  Return BFD_RELOC_NONE on failure.  */
   "alternate" relocations.  Return BFD_RELOC_NONE on failure.  */
 
 
static bfd_reloc_code_real_type
static bfd_reloc_code_real_type
encode_alt_reloc (int slot)
encode_alt_reloc (int slot)
{
{
  if (slot < 0 || slot > 14)
  if (slot < 0 || slot > 14)
    return BFD_RELOC_NONE;
    return BFD_RELOC_NONE;
 
 
  return BFD_RELOC_XTENSA_SLOT0_ALT + slot;
  return BFD_RELOC_XTENSA_SLOT0_ALT + slot;
}
}
 
 
 
 
static void
static void
xtensa_insnbuf_set_operand (xtensa_insnbuf slotbuf,
xtensa_insnbuf_set_operand (xtensa_insnbuf slotbuf,
                            xtensa_format fmt,
                            xtensa_format fmt,
                            int slot,
                            int slot,
                            xtensa_opcode opcode,
                            xtensa_opcode opcode,
                            int operand,
                            int operand,
                            uint32 value,
                            uint32 value,
                            const char *file,
                            const char *file,
                            unsigned int line)
                            unsigned int line)
{
{
  uint32 valbuf = value;
  uint32 valbuf = value;
 
 
  if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
  if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
    {
    {
      if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, operand)
      if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, operand)
          == 1)
          == 1)
        as_bad_where ((char *) file, line,
        as_bad_where ((char *) file, line,
                      _("operand %d of '%s' has out of range value '%u'"),
                      _("operand %d of '%s' has out of range value '%u'"),
                      operand + 1,
                      operand + 1,
                      xtensa_opcode_name (xtensa_default_isa, opcode),
                      xtensa_opcode_name (xtensa_default_isa, opcode),
                      value);
                      value);
      else
      else
        as_bad_where ((char *) file, line,
        as_bad_where ((char *) file, line,
                      _("operand %d of '%s' has invalid value '%u'"),
                      _("operand %d of '%s' has invalid value '%u'"),
                      operand + 1,
                      operand + 1,
                      xtensa_opcode_name (xtensa_default_isa, opcode),
                      xtensa_opcode_name (xtensa_default_isa, opcode),
                      value);
                      value);
      return;
      return;
    }
    }
 
 
  xtensa_operand_set_field (xtensa_default_isa, opcode, operand, fmt, slot,
  xtensa_operand_set_field (xtensa_default_isa, opcode, operand, fmt, slot,
                            slotbuf, valbuf);
                            slotbuf, valbuf);
}
}
 
 
 
 
static uint32
static uint32
xtensa_insnbuf_get_operand (xtensa_insnbuf slotbuf,
xtensa_insnbuf_get_operand (xtensa_insnbuf slotbuf,
                            xtensa_format fmt,
                            xtensa_format fmt,
                            int slot,
                            int slot,
                            xtensa_opcode opcode,
                            xtensa_opcode opcode,
                            int opnum)
                            int opnum)
{
{
  uint32 val = 0;
  uint32 val = 0;
  (void) xtensa_operand_get_field (xtensa_default_isa, opcode, opnum,
  (void) xtensa_operand_get_field (xtensa_default_isa, opcode, opnum,
                                   fmt, slot, slotbuf, &val);
                                   fmt, slot, slotbuf, &val);
  (void) xtensa_operand_decode (xtensa_default_isa, opcode, opnum, &val);
  (void) xtensa_operand_decode (xtensa_default_isa, opcode, opnum, &val);
  return val;
  return val;
}
}
 
 


/* Checks for rules from xtensa-relax tables.  */
/* Checks for rules from xtensa-relax tables.  */
 
 
/* The routine xg_instruction_matches_option_term must return TRUE
/* The routine xg_instruction_matches_option_term must return TRUE
   when a given option term is true.  The meaning of all of the option
   when a given option term is true.  The meaning of all of the option
   terms is given interpretation by this function.  */
   terms is given interpretation by this function.  */
 
 
static bfd_boolean
static bfd_boolean
xg_instruction_matches_option_term (TInsn *insn, const ReqOrOption *option)
xg_instruction_matches_option_term (TInsn *insn, const ReqOrOption *option)
{
{
  if (strcmp (option->option_name, "realnop") == 0
  if (strcmp (option->option_name, "realnop") == 0
      || strncmp (option->option_name, "IsaUse", 6) == 0)
      || strncmp (option->option_name, "IsaUse", 6) == 0)
    {
    {
      /* These conditions were evaluated statically when building the
      /* These conditions were evaluated statically when building the
         relaxation table.  There's no need to reevaluate them now.  */
         relaxation table.  There's no need to reevaluate them now.  */
      return TRUE;
      return TRUE;
    }
    }
  else if (strcmp (option->option_name, "FREEREG") == 0)
  else if (strcmp (option->option_name, "FREEREG") == 0)
    return insn->extra_arg.X_op == O_register;
    return insn->extra_arg.X_op == O_register;
  else
  else
    {
    {
      as_fatal (_("internal error: unknown option name '%s'"),
      as_fatal (_("internal error: unknown option name '%s'"),
                option->option_name);
                option->option_name);
    }
    }
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
xg_instruction_matches_or_options (TInsn *insn,
xg_instruction_matches_or_options (TInsn *insn,
                                   const ReqOrOptionList *or_option)
                                   const ReqOrOptionList *or_option)
{
{
  const ReqOrOption *option;
  const ReqOrOption *option;
  /* Must match each of the AND terms.  */
  /* Must match each of the AND terms.  */
  for (option = or_option; option != NULL; option = option->next)
  for (option = or_option; option != NULL; option = option->next)
    {
    {
      if (xg_instruction_matches_option_term (insn, option))
      if (xg_instruction_matches_option_term (insn, option))
        return TRUE;
        return TRUE;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
xg_instruction_matches_options (TInsn *insn, const ReqOptionList *options)
xg_instruction_matches_options (TInsn *insn, const ReqOptionList *options)
{
{
  const ReqOption *req_options;
  const ReqOption *req_options;
  /* Must match each of the AND terms.  */
  /* Must match each of the AND terms.  */
  for (req_options = options;
  for (req_options = options;
       req_options != NULL;
       req_options != NULL;
       req_options = req_options->next)
       req_options = req_options->next)
    {
    {
      /* Must match one of the OR clauses.  */
      /* Must match one of the OR clauses.  */
      if (!xg_instruction_matches_or_options (insn,
      if (!xg_instruction_matches_or_options (insn,
                                              req_options->or_option_terms))
                                              req_options->or_option_terms))
        return FALSE;
        return FALSE;
    }
    }
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* Return the transition rule that matches or NULL if none matches.  */
/* Return the transition rule that matches or NULL if none matches.  */
 
 
static bfd_boolean
static bfd_boolean
xg_instruction_matches_rule (TInsn *insn, TransitionRule *rule)
xg_instruction_matches_rule (TInsn *insn, TransitionRule *rule)
{
{
  PreconditionList *condition_l;
  PreconditionList *condition_l;
 
 
  if (rule->opcode != insn->opcode)
  if (rule->opcode != insn->opcode)
    return FALSE;
    return FALSE;
 
 
  for (condition_l = rule->conditions;
  for (condition_l = rule->conditions;
       condition_l != NULL;
       condition_l != NULL;
       condition_l = condition_l->next)
       condition_l = condition_l->next)
    {
    {
      expressionS *exp1;
      expressionS *exp1;
      expressionS *exp2;
      expressionS *exp2;
      Precondition *cond = condition_l->precond;
      Precondition *cond = condition_l->precond;
 
 
      switch (cond->typ)
      switch (cond->typ)
        {
        {
        case OP_CONSTANT:
        case OP_CONSTANT:
          /* The expression must be the constant.  */
          /* The expression must be the constant.  */
          gas_assert (cond->op_num < insn->ntok);
          gas_assert (cond->op_num < insn->ntok);
          exp1 = &insn->tok[cond->op_num];
          exp1 = &insn->tok[cond->op_num];
          if (expr_is_const (exp1))
          if (expr_is_const (exp1))
            {
            {
              switch (cond->cmp)
              switch (cond->cmp)
                {
                {
                case OP_EQUAL:
                case OP_EQUAL:
                  if (get_expr_const (exp1) != cond->op_data)
                  if (get_expr_const (exp1) != cond->op_data)
                    return FALSE;
                    return FALSE;
                  break;
                  break;
                case OP_NOTEQUAL:
                case OP_NOTEQUAL:
                  if (get_expr_const (exp1) == cond->op_data)
                  if (get_expr_const (exp1) == cond->op_data)
                    return FALSE;
                    return FALSE;
                  break;
                  break;
                default:
                default:
                  return FALSE;
                  return FALSE;
                }
                }
            }
            }
          else if (expr_is_register (exp1))
          else if (expr_is_register (exp1))
            {
            {
              switch (cond->cmp)
              switch (cond->cmp)
                {
                {
                case OP_EQUAL:
                case OP_EQUAL:
                  if (get_expr_register (exp1) != cond->op_data)
                  if (get_expr_register (exp1) != cond->op_data)
                    return FALSE;
                    return FALSE;
                  break;
                  break;
                case OP_NOTEQUAL:
                case OP_NOTEQUAL:
                  if (get_expr_register (exp1) == cond->op_data)
                  if (get_expr_register (exp1) == cond->op_data)
                    return FALSE;
                    return FALSE;
                  break;
                  break;
                default:
                default:
                  return FALSE;
                  return FALSE;
                }
                }
            }
            }
          else
          else
            return FALSE;
            return FALSE;
          break;
          break;
 
 
        case OP_OPERAND:
        case OP_OPERAND:
          gas_assert (cond->op_num < insn->ntok);
          gas_assert (cond->op_num < insn->ntok);
          gas_assert (cond->op_data < insn->ntok);
          gas_assert (cond->op_data < insn->ntok);
          exp1 = &insn->tok[cond->op_num];
          exp1 = &insn->tok[cond->op_num];
          exp2 = &insn->tok[cond->op_data];
          exp2 = &insn->tok[cond->op_data];
 
 
          switch (cond->cmp)
          switch (cond->cmp)
            {
            {
            case OP_EQUAL:
            case OP_EQUAL:
              if (!expr_is_equal (exp1, exp2))
              if (!expr_is_equal (exp1, exp2))
                return FALSE;
                return FALSE;
              break;
              break;
            case OP_NOTEQUAL:
            case OP_NOTEQUAL:
              if (expr_is_equal (exp1, exp2))
              if (expr_is_equal (exp1, exp2))
                return FALSE;
                return FALSE;
              break;
              break;
            }
            }
          break;
          break;
 
 
        case OP_LITERAL:
        case OP_LITERAL:
        case OP_LABEL:
        case OP_LABEL:
        default:
        default:
          return FALSE;
          return FALSE;
        }
        }
    }
    }
  if (!xg_instruction_matches_options (insn, rule->options))
  if (!xg_instruction_matches_options (insn, rule->options))
    return FALSE;
    return FALSE;
 
 
  return TRUE;
  return TRUE;
}
}
 
 
 
 
static int
static int
transition_rule_cmp (const TransitionRule *a, const TransitionRule *b)
transition_rule_cmp (const TransitionRule *a, const TransitionRule *b)
{
{
  bfd_boolean a_greater = FALSE;
  bfd_boolean a_greater = FALSE;
  bfd_boolean b_greater = FALSE;
  bfd_boolean b_greater = FALSE;
 
 
  ReqOptionList *l_a = a->options;
  ReqOptionList *l_a = a->options;
  ReqOptionList *l_b = b->options;
  ReqOptionList *l_b = b->options;
 
 
  /* We only care if they both are the same except for
  /* We only care if they both are the same except for
     a const16 vs. an l32r.  */
     a const16 vs. an l32r.  */
 
 
  while (l_a && l_b && ((l_a->next == NULL) == (l_b->next == NULL)))
  while (l_a && l_b && ((l_a->next == NULL) == (l_b->next == NULL)))
    {
    {
      ReqOrOptionList *l_or_a = l_a->or_option_terms;
      ReqOrOptionList *l_or_a = l_a->or_option_terms;
      ReqOrOptionList *l_or_b = l_b->or_option_terms;
      ReqOrOptionList *l_or_b = l_b->or_option_terms;
      while (l_or_a && l_or_b && ((l_a->next == NULL) == (l_b->next == NULL)))
      while (l_or_a && l_or_b && ((l_a->next == NULL) == (l_b->next == NULL)))
        {
        {
          if (l_or_a->is_true != l_or_b->is_true)
          if (l_or_a->is_true != l_or_b->is_true)
            return 0;
            return 0;
          if (strcmp (l_or_a->option_name, l_or_b->option_name) != 0)
          if (strcmp (l_or_a->option_name, l_or_b->option_name) != 0)
            {
            {
              /* This is the case we care about.  */
              /* This is the case we care about.  */
              if (strcmp (l_or_a->option_name, "IsaUseConst16") == 0
              if (strcmp (l_or_a->option_name, "IsaUseConst16") == 0
                  && strcmp (l_or_b->option_name, "IsaUseL32R") == 0)
                  && strcmp (l_or_b->option_name, "IsaUseL32R") == 0)
                {
                {
                  if (prefer_const16)
                  if (prefer_const16)
                    a_greater = TRUE;
                    a_greater = TRUE;
                  else
                  else
                    b_greater = TRUE;
                    b_greater = TRUE;
                }
                }
              else if (strcmp (l_or_a->option_name, "IsaUseL32R") == 0
              else if (strcmp (l_or_a->option_name, "IsaUseL32R") == 0
                       && strcmp (l_or_b->option_name, "IsaUseConst16") == 0)
                       && strcmp (l_or_b->option_name, "IsaUseConst16") == 0)
                {
                {
                  if (prefer_const16)
                  if (prefer_const16)
                    b_greater = TRUE;
                    b_greater = TRUE;
                  else
                  else
                    a_greater = TRUE;
                    a_greater = TRUE;
                }
                }
              else
              else
                return 0;
                return 0;
            }
            }
          l_or_a = l_or_a->next;
          l_or_a = l_or_a->next;
          l_or_b = l_or_b->next;
          l_or_b = l_or_b->next;
        }
        }
      if (l_or_a || l_or_b)
      if (l_or_a || l_or_b)
        return 0;
        return 0;
 
 
      l_a = l_a->next;
      l_a = l_a->next;
      l_b = l_b->next;
      l_b = l_b->next;
    }
    }
  if (l_a || l_b)
  if (l_a || l_b)
    return 0;
    return 0;
 
 
  /* Incomparable if the substitution was used differently in two cases.  */
  /* Incomparable if the substitution was used differently in two cases.  */
  if (a_greater && b_greater)
  if (a_greater && b_greater)
    return 0;
    return 0;
 
 
  if (b_greater)
  if (b_greater)
    return 1;
    return 1;
  if (a_greater)
  if (a_greater)
    return -1;
    return -1;
 
 
  return 0;
  return 0;
}
}
 
 
 
 
static TransitionRule *
static TransitionRule *
xg_instruction_match (TInsn *insn)
xg_instruction_match (TInsn *insn)
{
{
  TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
  TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
  TransitionList *l;
  TransitionList *l;
  gas_assert (insn->opcode < table->num_opcodes);
  gas_assert (insn->opcode < table->num_opcodes);
 
 
  /* Walk through all of the possible transitions.  */
  /* Walk through all of the possible transitions.  */
  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
    {
    {
      TransitionRule *rule = l->rule;
      TransitionRule *rule = l->rule;
      if (xg_instruction_matches_rule (insn, rule))
      if (xg_instruction_matches_rule (insn, rule))
        return rule;
        return rule;
    }
    }
  return NULL;
  return NULL;
}
}
 
 


/* Various Other Internal Functions.  */
/* Various Other Internal Functions.  */
 
 
static bfd_boolean
static bfd_boolean
is_unique_insn_expansion (TransitionRule *r)
is_unique_insn_expansion (TransitionRule *r)
{
{
  if (!r->to_instr || r->to_instr->next != NULL)
  if (!r->to_instr || r->to_instr->next != NULL)
    return FALSE;
    return FALSE;
  if (r->to_instr->typ != INSTR_INSTR)
  if (r->to_instr->typ != INSTR_INSTR)
    return FALSE;
    return FALSE;
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* Check if there is exactly one relaxation for INSN that converts it to
/* Check if there is exactly one relaxation for INSN that converts it to
   another instruction of equal or larger size.  If so, and if TARG is
   another instruction of equal or larger size.  If so, and if TARG is
   non-null, go ahead and generate the relaxed instruction into TARG.  If
   non-null, go ahead and generate the relaxed instruction into TARG.  If
   NARROW_ONLY is true, then only consider relaxations that widen a narrow
   NARROW_ONLY is true, then only consider relaxations that widen a narrow
   instruction, i.e., ignore relaxations that convert to an instruction of
   instruction, i.e., ignore relaxations that convert to an instruction of
   equal size.  In some contexts where this function is used, only
   equal size.  In some contexts where this function is used, only
   a single widening is allowed and the NARROW_ONLY argument is used to
   a single widening is allowed and the NARROW_ONLY argument is used to
   exclude cases like ADDI being "widened" to an ADDMI, which may
   exclude cases like ADDI being "widened" to an ADDMI, which may
   later be relaxed to an ADDMI/ADDI pair.  */
   later be relaxed to an ADDMI/ADDI pair.  */
 
 
bfd_boolean
bfd_boolean
xg_is_single_relaxable_insn (TInsn *insn, TInsn *targ, bfd_boolean narrow_only)
xg_is_single_relaxable_insn (TInsn *insn, TInsn *targ, bfd_boolean narrow_only)
{
{
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionList *l;
  TransitionList *l;
  TransitionRule *match = 0;
  TransitionRule *match = 0;
 
 
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->opcode < table->num_opcodes);
  gas_assert (insn->opcode < table->num_opcodes);
 
 
  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
    {
    {
      TransitionRule *rule = l->rule;
      TransitionRule *rule = l->rule;
 
 
      if (xg_instruction_matches_rule (insn, rule)
      if (xg_instruction_matches_rule (insn, rule)
          && is_unique_insn_expansion (rule)
          && is_unique_insn_expansion (rule)
          && (xg_get_single_size (insn->opcode) + (narrow_only ? 1 : 0)
          && (xg_get_single_size (insn->opcode) + (narrow_only ? 1 : 0)
              <= xg_get_single_size (rule->to_instr->opcode)))
              <= xg_get_single_size (rule->to_instr->opcode)))
        {
        {
          if (match)
          if (match)
            return FALSE;
            return FALSE;
          match = rule;
          match = rule;
        }
        }
    }
    }
  if (!match)
  if (!match)
    return FALSE;
    return FALSE;
 
 
  if (targ)
  if (targ)
    xg_build_to_insn (targ, insn, match->to_instr);
    xg_build_to_insn (targ, insn, match->to_instr);
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* Return the maximum number of bytes this opcode can expand to.  */
/* Return the maximum number of bytes this opcode can expand to.  */
 
 
static int
static int
xg_get_max_insn_widen_size (xtensa_opcode opcode)
xg_get_max_insn_widen_size (xtensa_opcode opcode)
{
{
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionList *l;
  TransitionList *l;
  int max_size = xg_get_single_size (opcode);
  int max_size = xg_get_single_size (opcode);
 
 
  gas_assert (opcode < table->num_opcodes);
  gas_assert (opcode < table->num_opcodes);
 
 
  for (l = table->table[opcode]; l != NULL; l = l->next)
  for (l = table->table[opcode]; l != NULL; l = l->next)
    {
    {
      TransitionRule *rule = l->rule;
      TransitionRule *rule = l->rule;
      BuildInstr *build_list;
      BuildInstr *build_list;
      int this_size = 0;
      int this_size = 0;
 
 
      if (!rule)
      if (!rule)
        continue;
        continue;
      build_list = rule->to_instr;
      build_list = rule->to_instr;
      if (is_unique_insn_expansion (rule))
      if (is_unique_insn_expansion (rule))
        {
        {
          gas_assert (build_list->typ == INSTR_INSTR);
          gas_assert (build_list->typ == INSTR_INSTR);
          this_size = xg_get_max_insn_widen_size (build_list->opcode);
          this_size = xg_get_max_insn_widen_size (build_list->opcode);
        }
        }
      else
      else
        for (; build_list != NULL; build_list = build_list->next)
        for (; build_list != NULL; build_list = build_list->next)
          {
          {
            switch (build_list->typ)
            switch (build_list->typ)
              {
              {
              case INSTR_INSTR:
              case INSTR_INSTR:
                this_size += xg_get_single_size (build_list->opcode);
                this_size += xg_get_single_size (build_list->opcode);
                break;
                break;
              case INSTR_LITERAL_DEF:
              case INSTR_LITERAL_DEF:
              case INSTR_LABEL_DEF:
              case INSTR_LABEL_DEF:
              default:
              default:
                break;
                break;
              }
              }
          }
          }
      if (this_size > max_size)
      if (this_size > max_size)
        max_size = this_size;
        max_size = this_size;
    }
    }
  return max_size;
  return max_size;
}
}
 
 
 
 
/* Return the maximum number of literal bytes this opcode can generate.  */
/* Return the maximum number of literal bytes this opcode can generate.  */
 
 
static int
static int
xg_get_max_insn_widen_literal_size (xtensa_opcode opcode)
xg_get_max_insn_widen_literal_size (xtensa_opcode opcode)
{
{
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionList *l;
  TransitionList *l;
  int max_size = 0;
  int max_size = 0;
 
 
  gas_assert (opcode < table->num_opcodes);
  gas_assert (opcode < table->num_opcodes);
 
 
  for (l = table->table[opcode]; l != NULL; l = l->next)
  for (l = table->table[opcode]; l != NULL; l = l->next)
    {
    {
      TransitionRule *rule = l->rule;
      TransitionRule *rule = l->rule;
      BuildInstr *build_list;
      BuildInstr *build_list;
      int this_size = 0;
      int this_size = 0;
 
 
      if (!rule)
      if (!rule)
        continue;
        continue;
      build_list = rule->to_instr;
      build_list = rule->to_instr;
      if (is_unique_insn_expansion (rule))
      if (is_unique_insn_expansion (rule))
        {
        {
          gas_assert (build_list->typ == INSTR_INSTR);
          gas_assert (build_list->typ == INSTR_INSTR);
          this_size = xg_get_max_insn_widen_literal_size (build_list->opcode);
          this_size = xg_get_max_insn_widen_literal_size (build_list->opcode);
        }
        }
      else
      else
        for (; build_list != NULL; build_list = build_list->next)
        for (; build_list != NULL; build_list = build_list->next)
          {
          {
            switch (build_list->typ)
            switch (build_list->typ)
              {
              {
              case INSTR_LITERAL_DEF:
              case INSTR_LITERAL_DEF:
                /* Hard-coded 4-byte literal.  */
                /* Hard-coded 4-byte literal.  */
                this_size += 4;
                this_size += 4;
                break;
                break;
              case INSTR_INSTR:
              case INSTR_INSTR:
              case INSTR_LABEL_DEF:
              case INSTR_LABEL_DEF:
              default:
              default:
                break;
                break;
              }
              }
          }
          }
      if (this_size > max_size)
      if (this_size > max_size)
        max_size = this_size;
        max_size = this_size;
    }
    }
  return max_size;
  return max_size;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
xg_is_relaxable_insn (TInsn *insn, int lateral_steps)
xg_is_relaxable_insn (TInsn *insn, int lateral_steps)
{
{
  int steps_taken = 0;
  int steps_taken = 0;
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionList *l;
  TransitionList *l;
 
 
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->opcode < table->num_opcodes);
  gas_assert (insn->opcode < table->num_opcodes);
 
 
  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
    {
    {
      TransitionRule *rule = l->rule;
      TransitionRule *rule = l->rule;
 
 
      if (xg_instruction_matches_rule (insn, rule))
      if (xg_instruction_matches_rule (insn, rule))
        {
        {
          if (steps_taken == lateral_steps)
          if (steps_taken == lateral_steps)
            return TRUE;
            return TRUE;
          steps_taken++;
          steps_taken++;
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static symbolS *
static symbolS *
get_special_literal_symbol (void)
get_special_literal_symbol (void)
{
{
  static symbolS *sym = NULL;
  static symbolS *sym = NULL;
 
 
  if (sym == NULL)
  if (sym == NULL)
    sym = symbol_find_or_make ("SPECIAL_LITERAL0\001");
    sym = symbol_find_or_make ("SPECIAL_LITERAL0\001");
  return sym;
  return sym;
}
}
 
 
 
 
static symbolS *
static symbolS *
get_special_label_symbol (void)
get_special_label_symbol (void)
{
{
  static symbolS *sym = NULL;
  static symbolS *sym = NULL;
 
 
  if (sym == NULL)
  if (sym == NULL)
    sym = symbol_find_or_make ("SPECIAL_LABEL0\001");
    sym = symbol_find_or_make ("SPECIAL_LABEL0\001");
  return sym;
  return sym;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
xg_valid_literal_expression (const expressionS *exp)
xg_valid_literal_expression (const expressionS *exp)
{
{
  switch (exp->X_op)
  switch (exp->X_op)
    {
    {
    case O_constant:
    case O_constant:
    case O_symbol:
    case O_symbol:
    case O_big:
    case O_big:
    case O_uminus:
    case O_uminus:
    case O_subtract:
    case O_subtract:
    case O_pltrel:
    case O_pltrel:
    case O_pcrel:
    case O_pcrel:
    case O_tlsfunc:
    case O_tlsfunc:
    case O_tlsarg:
    case O_tlsarg:
    case O_tpoff:
    case O_tpoff:
    case O_dtpoff:
    case O_dtpoff:
      return TRUE;
      return TRUE;
    default:
    default:
      return FALSE;
      return FALSE;
    }
    }
}
}
 
 
 
 
/* This will check to see if the value can be converted into the
/* This will check to see if the value can be converted into the
   operand type.  It will return TRUE if it does not fit.  */
   operand type.  It will return TRUE if it does not fit.  */
 
 
static bfd_boolean
static bfd_boolean
xg_check_operand (int32 value, xtensa_opcode opcode, int operand)
xg_check_operand (int32 value, xtensa_opcode opcode, int operand)
{
{
  uint32 valbuf = value;
  uint32 valbuf = value;
  if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
  if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
    return TRUE;
    return TRUE;
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Assumes: All immeds are constants.  Check that all constants fit
/* Assumes: All immeds are constants.  Check that all constants fit
   into their immeds; return FALSE if not.  */
   into their immeds; return FALSE if not.  */
 
 
static bfd_boolean
static bfd_boolean
xg_immeds_fit (const TInsn *insn)
xg_immeds_fit (const TInsn *insn)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int i;
  int i;
 
 
  int n = insn->ntok;
  int n = insn->ntok;
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
  for (i = 0; i < n; ++i)
  for (i = 0; i < n; ++i)
    {
    {
      const expressionS *expr = &insn->tok[i];
      const expressionS *expr = &insn->tok[i];
      if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
      if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
        continue;
        continue;
 
 
      switch (expr->X_op)
      switch (expr->X_op)
        {
        {
        case O_register:
        case O_register:
        case O_constant:
        case O_constant:
          if (xg_check_operand (expr->X_add_number, insn->opcode, i))
          if (xg_check_operand (expr->X_add_number, insn->opcode, i))
            return FALSE;
            return FALSE;
          break;
          break;
 
 
        default:
        default:
          /* The symbol should have a fixup associated with it.  */
          /* The symbol should have a fixup associated with it.  */
          gas_assert (FALSE);
          gas_assert (FALSE);
          break;
          break;
        }
        }
    }
    }
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* This should only be called after we have an initial
/* This should only be called after we have an initial
   estimate of the addresses.  */
   estimate of the addresses.  */
 
 
static bfd_boolean
static bfd_boolean
xg_symbolic_immeds_fit (const TInsn *insn,
xg_symbolic_immeds_fit (const TInsn *insn,
                        segT pc_seg,
                        segT pc_seg,
                        fragS *pc_frag,
                        fragS *pc_frag,
                        offsetT pc_offset,
                        offsetT pc_offset,
                        long stretch)
                        long stretch)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  symbolS *symbolP;
  symbolS *symbolP;
  fragS *sym_frag;
  fragS *sym_frag;
  offsetT target, pc;
  offsetT target, pc;
  uint32 new_offset;
  uint32 new_offset;
  int i;
  int i;
  int n = insn->ntok;
  int n = insn->ntok;
 
 
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
 
 
  for (i = 0; i < n; ++i)
  for (i = 0; i < n; ++i)
    {
    {
      const expressionS *expr = &insn->tok[i];
      const expressionS *expr = &insn->tok[i];
      if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
      if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
        continue;
        continue;
 
 
      switch (expr->X_op)
      switch (expr->X_op)
        {
        {
        case O_register:
        case O_register:
        case O_constant:
        case O_constant:
          if (xg_check_operand (expr->X_add_number, insn->opcode, i))
          if (xg_check_operand (expr->X_add_number, insn->opcode, i))
            return FALSE;
            return FALSE;
          break;
          break;
 
 
        case O_lo16:
        case O_lo16:
        case O_hi16:
        case O_hi16:
          /* Check for the worst case.  */
          /* Check for the worst case.  */
          if (xg_check_operand (0xffff, insn->opcode, i))
          if (xg_check_operand (0xffff, insn->opcode, i))
            return FALSE;
            return FALSE;
          break;
          break;
 
 
        case O_symbol:
        case O_symbol:
          /* We only allow symbols for PC-relative references.
          /* We only allow symbols for PC-relative references.
             If pc_frag == 0, then we don't have frag locations yet.  */
             If pc_frag == 0, then we don't have frag locations yet.  */
          if (pc_frag == 0
          if (pc_frag == 0
              || xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 0)
              || xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 0)
            return FALSE;
            return FALSE;
 
 
          /* If it is a weak symbol or a symbol in a different section,
          /* If it is a weak symbol or a symbol in a different section,
             it cannot be known to fit at assembly time.  */
             it cannot be known to fit at assembly time.  */
          if (S_IS_WEAK (expr->X_add_symbol)
          if (S_IS_WEAK (expr->X_add_symbol)
              || S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
              || S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
            {
            {
              /* For a direct call with --no-longcalls, be optimistic and
              /* For a direct call with --no-longcalls, be optimistic and
                 assume it will be in range.  If the symbol is weak and
                 assume it will be in range.  If the symbol is weak and
                 undefined, it may remain undefined at link-time, in which
                 undefined, it may remain undefined at link-time, in which
                 case it will have a zero value and almost certainly be out
                 case it will have a zero value and almost certainly be out
                 of range for a direct call; thus, relax for undefined weak
                 of range for a direct call; thus, relax for undefined weak
                 symbols even if longcalls is not enabled.  */
                 symbols even if longcalls is not enabled.  */
              if (is_direct_call_opcode (insn->opcode)
              if (is_direct_call_opcode (insn->opcode)
                  && ! pc_frag->tc_frag_data.use_longcalls
                  && ! pc_frag->tc_frag_data.use_longcalls
                  && (! S_IS_WEAK (expr->X_add_symbol)
                  && (! S_IS_WEAK (expr->X_add_symbol)
                      || S_IS_DEFINED (expr->X_add_symbol)))
                      || S_IS_DEFINED (expr->X_add_symbol)))
                return TRUE;
                return TRUE;
 
 
              return FALSE;
              return FALSE;
            }
            }
 
 
          symbolP = expr->X_add_symbol;
          symbolP = expr->X_add_symbol;
          sym_frag = symbol_get_frag (symbolP);
          sym_frag = symbol_get_frag (symbolP);
          target = S_GET_VALUE (symbolP) + expr->X_add_number;
          target = S_GET_VALUE (symbolP) + expr->X_add_number;
          pc = pc_frag->fr_address + pc_offset;
          pc = pc_frag->fr_address + pc_offset;
 
 
          /* If frag has yet to be reached on this pass, assume it
          /* If frag has yet to be reached on this pass, assume it
             will move by STRETCH just as we did.  If this is not so,
             will move by STRETCH just as we did.  If this is not so,
             it will be because some frag between grows, and that will
             it will be because some frag between grows, and that will
             force another pass.  Beware zero-length frags.  There
             force another pass.  Beware zero-length frags.  There
             should be a faster way to do this.  */
             should be a faster way to do this.  */
 
 
          if (stretch != 0
          if (stretch != 0
              && sym_frag->relax_marker != pc_frag->relax_marker
              && sym_frag->relax_marker != pc_frag->relax_marker
              && S_GET_SEGMENT (symbolP) == pc_seg)
              && S_GET_SEGMENT (symbolP) == pc_seg)
            {
            {
              target += stretch;
              target += stretch;
            }
            }
 
 
          new_offset = target;
          new_offset = target;
          xtensa_operand_do_reloc (isa, insn->opcode, i, &new_offset, pc);
          xtensa_operand_do_reloc (isa, insn->opcode, i, &new_offset, pc);
          if (xg_check_operand (new_offset, insn->opcode, i))
          if (xg_check_operand (new_offset, insn->opcode, i))
            return FALSE;
            return FALSE;
          break;
          break;
 
 
        default:
        default:
          /* The symbol should have a fixup associated with it.  */
          /* The symbol should have a fixup associated with it.  */
          return FALSE;
          return FALSE;
        }
        }
    }
    }
 
 
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* Return TRUE on success.  */
/* Return TRUE on success.  */
 
 
static bfd_boolean
static bfd_boolean
xg_build_to_insn (TInsn *targ, TInsn *insn, BuildInstr *bi)
xg_build_to_insn (TInsn *targ, TInsn *insn, BuildInstr *bi)
{
{
  BuildOp *op;
  BuildOp *op;
  symbolS *sym;
  symbolS *sym;
 
 
  tinsn_init (targ);
  tinsn_init (targ);
  targ->debug_line = insn->debug_line;
  targ->debug_line = insn->debug_line;
  targ->loc_directive_seen = insn->loc_directive_seen;
  targ->loc_directive_seen = insn->loc_directive_seen;
  switch (bi->typ)
  switch (bi->typ)
    {
    {
    case INSTR_INSTR:
    case INSTR_INSTR:
      op = bi->ops;
      op = bi->ops;
      targ->opcode = bi->opcode;
      targ->opcode = bi->opcode;
      targ->insn_type = ITYPE_INSN;
      targ->insn_type = ITYPE_INSN;
      targ->is_specific_opcode = FALSE;
      targ->is_specific_opcode = FALSE;
 
 
      for (; op != NULL; op = op->next)
      for (; op != NULL; op = op->next)
        {
        {
          int op_num = op->op_num;
          int op_num = op->op_num;
          int op_data = op->op_data;
          int op_data = op->op_data;
 
 
          gas_assert (op->op_num < MAX_INSN_ARGS);
          gas_assert (op->op_num < MAX_INSN_ARGS);
 
 
          if (targ->ntok <= op_num)
          if (targ->ntok <= op_num)
            targ->ntok = op_num + 1;
            targ->ntok = op_num + 1;
 
 
          switch (op->typ)
          switch (op->typ)
            {
            {
            case OP_CONSTANT:
            case OP_CONSTANT:
              set_expr_const (&targ->tok[op_num], op_data);
              set_expr_const (&targ->tok[op_num], op_data);
              break;
              break;
            case OP_OPERAND:
            case OP_OPERAND:
              gas_assert (op_data < insn->ntok);
              gas_assert (op_data < insn->ntok);
              copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
              copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
              break;
              break;
            case OP_FREEREG:
            case OP_FREEREG:
              if (insn->extra_arg.X_op != O_register)
              if (insn->extra_arg.X_op != O_register)
                return FALSE;
                return FALSE;
              copy_expr (&targ->tok[op_num], &insn->extra_arg);
              copy_expr (&targ->tok[op_num], &insn->extra_arg);
              break;
              break;
            case OP_LITERAL:
            case OP_LITERAL:
              sym = get_special_literal_symbol ();
              sym = get_special_literal_symbol ();
              set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
              set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
              if (insn->tok[op_data].X_op == O_tlsfunc
              if (insn->tok[op_data].X_op == O_tlsfunc
                  || insn->tok[op_data].X_op == O_tlsarg)
                  || insn->tok[op_data].X_op == O_tlsarg)
                copy_expr (&targ->extra_arg, &insn->tok[op_data]);
                copy_expr (&targ->extra_arg, &insn->tok[op_data]);
              break;
              break;
            case OP_LABEL:
            case OP_LABEL:
              sym = get_special_label_symbol ();
              sym = get_special_label_symbol ();
              set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
              set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
              break;
              break;
            case OP_OPERAND_HI16U:
            case OP_OPERAND_HI16U:
            case OP_OPERAND_LOW16U:
            case OP_OPERAND_LOW16U:
              gas_assert (op_data < insn->ntok);
              gas_assert (op_data < insn->ntok);
              if (expr_is_const (&insn->tok[op_data]))
              if (expr_is_const (&insn->tok[op_data]))
                {
                {
                  long val;
                  long val;
                  copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
                  copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
                  val = xg_apply_userdef_op_fn (op->typ,
                  val = xg_apply_userdef_op_fn (op->typ,
                                                targ->tok[op_num].
                                                targ->tok[op_num].
                                                X_add_number);
                                                X_add_number);
                  targ->tok[op_num].X_add_number = val;
                  targ->tok[op_num].X_add_number = val;
                }
                }
              else
              else
                {
                {
                  /* For const16 we can create relocations for these.  */
                  /* For const16 we can create relocations for these.  */
                  if (targ->opcode == XTENSA_UNDEFINED
                  if (targ->opcode == XTENSA_UNDEFINED
                      || (targ->opcode != xtensa_const16_opcode))
                      || (targ->opcode != xtensa_const16_opcode))
                    return FALSE;
                    return FALSE;
                  gas_assert (op_data < insn->ntok);
                  gas_assert (op_data < insn->ntok);
                  /* Need to build a O_lo16 or O_hi16.  */
                  /* Need to build a O_lo16 or O_hi16.  */
                  copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
                  copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
                  if (targ->tok[op_num].X_op == O_symbol)
                  if (targ->tok[op_num].X_op == O_symbol)
                    {
                    {
                      if (op->typ == OP_OPERAND_HI16U)
                      if (op->typ == OP_OPERAND_HI16U)
                        targ->tok[op_num].X_op = O_hi16;
                        targ->tok[op_num].X_op = O_hi16;
                      else if (op->typ == OP_OPERAND_LOW16U)
                      else if (op->typ == OP_OPERAND_LOW16U)
                        targ->tok[op_num].X_op = O_lo16;
                        targ->tok[op_num].X_op = O_lo16;
                      else
                      else
                        return FALSE;
                        return FALSE;
                    }
                    }
                }
                }
              break;
              break;
            default:
            default:
              /* currently handles:
              /* currently handles:
                 OP_OPERAND_LOW8
                 OP_OPERAND_LOW8
                 OP_OPERAND_HI24S
                 OP_OPERAND_HI24S
                 OP_OPERAND_F32MINUS */
                 OP_OPERAND_F32MINUS */
              if (xg_has_userdef_op_fn (op->typ))
              if (xg_has_userdef_op_fn (op->typ))
                {
                {
                  gas_assert (op_data < insn->ntok);
                  gas_assert (op_data < insn->ntok);
                  if (expr_is_const (&insn->tok[op_data]))
                  if (expr_is_const (&insn->tok[op_data]))
                    {
                    {
                      long val;
                      long val;
                      copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
                      copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
                      val = xg_apply_userdef_op_fn (op->typ,
                      val = xg_apply_userdef_op_fn (op->typ,
                                                    targ->tok[op_num].
                                                    targ->tok[op_num].
                                                    X_add_number);
                                                    X_add_number);
                      targ->tok[op_num].X_add_number = val;
                      targ->tok[op_num].X_add_number = val;
                    }
                    }
                  else
                  else
                    return FALSE; /* We cannot use a relocation for this.  */
                    return FALSE; /* We cannot use a relocation for this.  */
                  break;
                  break;
                }
                }
              gas_assert (0);
              gas_assert (0);
              break;
              break;
            }
            }
        }
        }
      break;
      break;
 
 
    case INSTR_LITERAL_DEF:
    case INSTR_LITERAL_DEF:
      op = bi->ops;
      op = bi->ops;
      targ->opcode = XTENSA_UNDEFINED;
      targ->opcode = XTENSA_UNDEFINED;
      targ->insn_type = ITYPE_LITERAL;
      targ->insn_type = ITYPE_LITERAL;
      targ->is_specific_opcode = FALSE;
      targ->is_specific_opcode = FALSE;
      for (; op != NULL; op = op->next)
      for (; op != NULL; op = op->next)
        {
        {
          int op_num = op->op_num;
          int op_num = op->op_num;
          int op_data = op->op_data;
          int op_data = op->op_data;
          gas_assert (op->op_num < MAX_INSN_ARGS);
          gas_assert (op->op_num < MAX_INSN_ARGS);
 
 
          if (targ->ntok <= op_num)
          if (targ->ntok <= op_num)
            targ->ntok = op_num + 1;
            targ->ntok = op_num + 1;
 
 
          switch (op->typ)
          switch (op->typ)
            {
            {
            case OP_OPERAND:
            case OP_OPERAND:
              gas_assert (op_data < insn->ntok);
              gas_assert (op_data < insn->ntok);
              /* We can only pass resolvable literals through.  */
              /* We can only pass resolvable literals through.  */
              if (!xg_valid_literal_expression (&insn->tok[op_data]))
              if (!xg_valid_literal_expression (&insn->tok[op_data]))
                return FALSE;
                return FALSE;
              copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
              copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
              break;
              break;
            case OP_LITERAL:
            case OP_LITERAL:
            case OP_CONSTANT:
            case OP_CONSTANT:
            case OP_LABEL:
            case OP_LABEL:
            default:
            default:
              gas_assert (0);
              gas_assert (0);
              break;
              break;
            }
            }
        }
        }
      break;
      break;
 
 
    case INSTR_LABEL_DEF:
    case INSTR_LABEL_DEF:
      op = bi->ops;
      op = bi->ops;
      targ->opcode = XTENSA_UNDEFINED;
      targ->opcode = XTENSA_UNDEFINED;
      targ->insn_type = ITYPE_LABEL;
      targ->insn_type = ITYPE_LABEL;
      targ->is_specific_opcode = FALSE;
      targ->is_specific_opcode = FALSE;
      /* Literal with no ops is a label?  */
      /* Literal with no ops is a label?  */
      gas_assert (op == NULL);
      gas_assert (op == NULL);
      break;
      break;
 
 
    default:
    default:
      gas_assert (0);
      gas_assert (0);
    }
    }
 
 
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* Return TRUE on success.  */
/* Return TRUE on success.  */
 
 
static bfd_boolean
static bfd_boolean
xg_build_to_stack (IStack *istack, TInsn *insn, BuildInstr *bi)
xg_build_to_stack (IStack *istack, TInsn *insn, BuildInstr *bi)
{
{
  for (; bi != NULL; bi = bi->next)
  for (; bi != NULL; bi = bi->next)
    {
    {
      TInsn *next_insn = istack_push_space (istack);
      TInsn *next_insn = istack_push_space (istack);
 
 
      if (!xg_build_to_insn (next_insn, insn, bi))
      if (!xg_build_to_insn (next_insn, insn, bi))
        return FALSE;
        return FALSE;
    }
    }
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* Return TRUE on valid expansion.  */
/* Return TRUE on valid expansion.  */
 
 
static bfd_boolean
static bfd_boolean
xg_expand_to_stack (IStack *istack, TInsn *insn, int lateral_steps)
xg_expand_to_stack (IStack *istack, TInsn *insn, int lateral_steps)
{
{
  int stack_size = istack->ninsn;
  int stack_size = istack->ninsn;
  int steps_taken = 0;
  int steps_taken = 0;
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
  TransitionList *l;
  TransitionList *l;
 
 
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->opcode < table->num_opcodes);
  gas_assert (insn->opcode < table->num_opcodes);
 
 
  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
    {
    {
      TransitionRule *rule = l->rule;
      TransitionRule *rule = l->rule;
 
 
      if (xg_instruction_matches_rule (insn, rule))
      if (xg_instruction_matches_rule (insn, rule))
        {
        {
          if (lateral_steps == steps_taken)
          if (lateral_steps == steps_taken)
            {
            {
              int i;
              int i;
 
 
              /* This is it.  Expand the rule to the stack.  */
              /* This is it.  Expand the rule to the stack.  */
              if (!xg_build_to_stack (istack, insn, rule->to_instr))
              if (!xg_build_to_stack (istack, insn, rule->to_instr))
                return FALSE;
                return FALSE;
 
 
              /* Check to see if it fits.  */
              /* Check to see if it fits.  */
              for (i = stack_size; i < istack->ninsn; i++)
              for (i = stack_size; i < istack->ninsn; i++)
                {
                {
                  TInsn *insn = &istack->insn[i];
                  TInsn *insn = &istack->insn[i];
 
 
                  if (insn->insn_type == ITYPE_INSN
                  if (insn->insn_type == ITYPE_INSN
                      && !tinsn_has_symbolic_operands (insn)
                      && !tinsn_has_symbolic_operands (insn)
                      && !xg_immeds_fit (insn))
                      && !xg_immeds_fit (insn))
                    {
                    {
                      istack->ninsn = stack_size;
                      istack->ninsn = stack_size;
                      return FALSE;
                      return FALSE;
                    }
                    }
                }
                }
              return TRUE;
              return TRUE;
            }
            }
          steps_taken++;
          steps_taken++;
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 


/* Relax the assembly instruction at least "min_steps".
/* Relax the assembly instruction at least "min_steps".
   Return the number of steps taken.
   Return the number of steps taken.
 
 
   For relaxation to correctly terminate, every relaxation chain must
   For relaxation to correctly terminate, every relaxation chain must
   terminate in one of two ways:
   terminate in one of two ways:
 
 
   1.  If the chain from one instruction to the next consists entirely of
   1.  If the chain from one instruction to the next consists entirely of
       single instructions, then the chain *must* handle all possible
       single instructions, then the chain *must* handle all possible
       immediates without failing.  It must not ever fail because an
       immediates without failing.  It must not ever fail because an
       immediate is out of range.  The MOVI.N -> MOVI -> L32R relaxation
       immediate is out of range.  The MOVI.N -> MOVI -> L32R relaxation
       chain is one example.  L32R loads 32 bits, and there cannot be an
       chain is one example.  L32R loads 32 bits, and there cannot be an
       immediate larger than 32 bits, so it satisfies this condition.
       immediate larger than 32 bits, so it satisfies this condition.
       Single instruction relaxation chains are as defined by
       Single instruction relaxation chains are as defined by
       xg_is_single_relaxable_instruction.
       xg_is_single_relaxable_instruction.
 
 
   2.  Otherwise, the chain must end in a multi-instruction expansion: e.g.,
   2.  Otherwise, the chain must end in a multi-instruction expansion: e.g.,
       BNEZ.N -> BNEZ -> BNEZ.W15 -> BENZ.N/J
       BNEZ.N -> BNEZ -> BNEZ.W15 -> BENZ.N/J
 
 
   Strictly speaking, in most cases you can violate condition 1 and be OK
   Strictly speaking, in most cases you can violate condition 1 and be OK
   -- in particular when the last two instructions have the same single
   -- in particular when the last two instructions have the same single
   size.  But nevertheless, you should guarantee the above two conditions.
   size.  But nevertheless, you should guarantee the above two conditions.
 
 
   We could fix this so that single-instruction expansions correctly
   We could fix this so that single-instruction expansions correctly
   terminate when they can't handle the range, but the error messages are
   terminate when they can't handle the range, but the error messages are
   worse, and it actually turns out that in every case but one (18-bit wide
   worse, and it actually turns out that in every case but one (18-bit wide
   branches), you need a multi-instruction expansion to get the full range
   branches), you need a multi-instruction expansion to get the full range
   anyway.  And because 18-bit branches are handled identically to 15-bit
   anyway.  And because 18-bit branches are handled identically to 15-bit
   branches, there isn't any point in changing it.  */
   branches, there isn't any point in changing it.  */
 
 
static int
static int
xg_assembly_relax (IStack *istack,
xg_assembly_relax (IStack *istack,
                   TInsn *insn,
                   TInsn *insn,
                   segT pc_seg,
                   segT pc_seg,
                   fragS *pc_frag,      /* if pc_frag == 0, not pc-relative */
                   fragS *pc_frag,      /* if pc_frag == 0, not pc-relative */
                   offsetT pc_offset,   /* offset in fragment */
                   offsetT pc_offset,   /* offset in fragment */
                   int min_steps,       /* minimum conversion steps */
                   int min_steps,       /* minimum conversion steps */
                   long stretch)        /* number of bytes stretched so far */
                   long stretch)        /* number of bytes stretched so far */
{
{
  int steps_taken = 0;
  int steps_taken = 0;
 
 
  /* Some of its immeds don't fit.  Try to build a relaxed version.
  /* Some of its immeds don't fit.  Try to build a relaxed version.
     This may go through a couple of stages of single instruction
     This may go through a couple of stages of single instruction
     transformations before we get there.  */
     transformations before we get there.  */
 
 
  TInsn single_target;
  TInsn single_target;
  TInsn current_insn;
  TInsn current_insn;
  int lateral_steps = 0;
  int lateral_steps = 0;
  int istack_size = istack->ninsn;
  int istack_size = istack->ninsn;
 
 
  if (xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
  if (xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
      && steps_taken >= min_steps)
      && steps_taken >= min_steps)
    {
    {
      istack_push (istack, insn);
      istack_push (istack, insn);
      return steps_taken;
      return steps_taken;
    }
    }
  current_insn = *insn;
  current_insn = *insn;
 
 
  /* Walk through all of the single instruction expansions.  */
  /* Walk through all of the single instruction expansions.  */
  while (xg_is_single_relaxable_insn (&current_insn, &single_target, FALSE))
  while (xg_is_single_relaxable_insn (&current_insn, &single_target, FALSE))
    {
    {
      steps_taken++;
      steps_taken++;
      if (xg_symbolic_immeds_fit (&single_target, pc_seg, pc_frag, pc_offset,
      if (xg_symbolic_immeds_fit (&single_target, pc_seg, pc_frag, pc_offset,
                                  stretch))
                                  stretch))
        {
        {
          if (steps_taken >= min_steps)
          if (steps_taken >= min_steps)
            {
            {
              istack_push (istack, &single_target);
              istack_push (istack, &single_target);
              return steps_taken;
              return steps_taken;
            }
            }
        }
        }
      current_insn = single_target;
      current_insn = single_target;
    }
    }
 
 
  /* Now check for a multi-instruction expansion.  */
  /* Now check for a multi-instruction expansion.  */
  while (xg_is_relaxable_insn (&current_insn, lateral_steps))
  while (xg_is_relaxable_insn (&current_insn, lateral_steps))
    {
    {
      if (xg_symbolic_immeds_fit (&current_insn, pc_seg, pc_frag, pc_offset,
      if (xg_symbolic_immeds_fit (&current_insn, pc_seg, pc_frag, pc_offset,
                                  stretch))
                                  stretch))
        {
        {
          if (steps_taken >= min_steps)
          if (steps_taken >= min_steps)
            {
            {
              istack_push (istack, &current_insn);
              istack_push (istack, &current_insn);
              return steps_taken;
              return steps_taken;
            }
            }
        }
        }
      steps_taken++;
      steps_taken++;
      if (xg_expand_to_stack (istack, &current_insn, lateral_steps))
      if (xg_expand_to_stack (istack, &current_insn, lateral_steps))
        {
        {
          if (steps_taken >= min_steps)
          if (steps_taken >= min_steps)
            return steps_taken;
            return steps_taken;
        }
        }
      lateral_steps++;
      lateral_steps++;
      istack->ninsn = istack_size;
      istack->ninsn = istack_size;
    }
    }
 
 
  /* It's not going to work -- use the original.  */
  /* It's not going to work -- use the original.  */
  istack_push (istack, insn);
  istack_push (istack, insn);
  return steps_taken;
  return steps_taken;
}
}
 
 
 
 
static void
static void
xg_finish_frag (char *last_insn,
xg_finish_frag (char *last_insn,
                enum xtensa_relax_statesE frag_state,
                enum xtensa_relax_statesE frag_state,
                enum xtensa_relax_statesE slot0_state,
                enum xtensa_relax_statesE slot0_state,
                int max_growth,
                int max_growth,
                bfd_boolean is_insn)
                bfd_boolean is_insn)
{
{
  /* Finish off this fragment so that it has at LEAST the desired
  /* Finish off this fragment so that it has at LEAST the desired
     max_growth.  If it doesn't fit in this fragment, close this one
     max_growth.  If it doesn't fit in this fragment, close this one
     and start a new one.  In either case, return a pointer to the
     and start a new one.  In either case, return a pointer to the
     beginning of the growth area.  */
     beginning of the growth area.  */
 
 
  fragS *old_frag;
  fragS *old_frag;
 
 
  frag_grow (max_growth);
  frag_grow (max_growth);
  old_frag = frag_now;
  old_frag = frag_now;
 
 
  frag_now->fr_opcode = last_insn;
  frag_now->fr_opcode = last_insn;
  if (is_insn)
  if (is_insn)
    frag_now->tc_frag_data.is_insn = TRUE;
    frag_now->tc_frag_data.is_insn = TRUE;
 
 
  frag_var (rs_machine_dependent, max_growth, max_growth,
  frag_var (rs_machine_dependent, max_growth, max_growth,
            frag_state, frag_now->fr_symbol, frag_now->fr_offset, last_insn);
            frag_state, frag_now->fr_symbol, frag_now->fr_offset, last_insn);
 
 
  old_frag->tc_frag_data.slot_subtypes[0] = slot0_state;
  old_frag->tc_frag_data.slot_subtypes[0] = slot0_state;
  xtensa_set_frag_assembly_state (frag_now);
  xtensa_set_frag_assembly_state (frag_now);
 
 
  /* Just to make sure that we did not split it up.  */
  /* Just to make sure that we did not split it up.  */
  gas_assert (old_frag->fr_next == frag_now);
  gas_assert (old_frag->fr_next == frag_now);
}
}
 
 
 
 
/* Return TRUE if the target frag is one of the next non-empty frags.  */
/* Return TRUE if the target frag is one of the next non-empty frags.  */
 
 
static bfd_boolean
static bfd_boolean
is_next_frag_target (const fragS *fragP, const fragS *target)
is_next_frag_target (const fragS *fragP, const fragS *target)
{
{
  if (fragP == NULL)
  if (fragP == NULL)
    return FALSE;
    return FALSE;
 
 
  for (; fragP; fragP = fragP->fr_next)
  for (; fragP; fragP = fragP->fr_next)
    {
    {
      if (fragP == target)
      if (fragP == target)
        return TRUE;
        return TRUE;
      if (fragP->fr_fix != 0)
      if (fragP->fr_fix != 0)
        return FALSE;
        return FALSE;
      if (fragP->fr_type == rs_fill && fragP->fr_offset != 0)
      if (fragP->fr_type == rs_fill && fragP->fr_offset != 0)
        return FALSE;
        return FALSE;
      if ((fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
      if ((fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
          && ((fragP->fr_address % (1 << fragP->fr_offset)) != 0))
          && ((fragP->fr_address % (1 << fragP->fr_offset)) != 0))
        return FALSE;
        return FALSE;
      if (fragP->fr_type == rs_space)
      if (fragP->fr_type == rs_space)
        return FALSE;
        return FALSE;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
is_branch_jmp_to_next (TInsn *insn, fragS *fragP)
is_branch_jmp_to_next (TInsn *insn, fragS *fragP)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int i;
  int i;
  int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
  int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
  int target_op = -1;
  int target_op = -1;
  symbolS *sym;
  symbolS *sym;
  fragS *target_frag;
  fragS *target_frag;
 
 
  if (xtensa_opcode_is_branch (isa, insn->opcode) != 1
  if (xtensa_opcode_is_branch (isa, insn->opcode) != 1
      && xtensa_opcode_is_jump (isa, insn->opcode) != 1)
      && xtensa_opcode_is_jump (isa, insn->opcode) != 1)
    return FALSE;
    return FALSE;
 
 
  for (i = 0; i < num_ops; i++)
  for (i = 0; i < num_ops; i++)
    {
    {
      if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1)
      if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1)
        {
        {
          target_op = i;
          target_op = i;
          break;
          break;
        }
        }
    }
    }
  if (target_op == -1)
  if (target_op == -1)
    return FALSE;
    return FALSE;
 
 
  if (insn->ntok <= target_op)
  if (insn->ntok <= target_op)
    return FALSE;
    return FALSE;
 
 
  if (insn->tok[target_op].X_op != O_symbol)
  if (insn->tok[target_op].X_op != O_symbol)
    return FALSE;
    return FALSE;
 
 
  sym = insn->tok[target_op].X_add_symbol;
  sym = insn->tok[target_op].X_add_symbol;
  if (sym == NULL)
  if (sym == NULL)
    return FALSE;
    return FALSE;
 
 
  if (insn->tok[target_op].X_add_number != 0)
  if (insn->tok[target_op].X_add_number != 0)
    return FALSE;
    return FALSE;
 
 
  target_frag = symbol_get_frag (sym);
  target_frag = symbol_get_frag (sym);
  if (target_frag == NULL)
  if (target_frag == NULL)
    return FALSE;
    return FALSE;
 
 
  if (is_next_frag_target (fragP->fr_next, target_frag)
  if (is_next_frag_target (fragP->fr_next, target_frag)
      && S_GET_VALUE (sym) == target_frag->fr_address)
      && S_GET_VALUE (sym) == target_frag->fr_address)
    return TRUE;
    return TRUE;
 
 
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static void
static void
xg_add_branch_and_loop_targets (TInsn *insn)
xg_add_branch_and_loop_targets (TInsn *insn)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
  int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
 
 
  if (xtensa_opcode_is_loop (isa, insn->opcode) == 1)
  if (xtensa_opcode_is_loop (isa, insn->opcode) == 1)
    {
    {
      int i = 1;
      int i = 1;
      if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
      if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
          && insn->tok[i].X_op == O_symbol)
          && insn->tok[i].X_op == O_symbol)
        symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
        symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
      return;
      return;
    }
    }
 
 
  if (xtensa_opcode_is_branch (isa, insn->opcode) == 1
  if (xtensa_opcode_is_branch (isa, insn->opcode) == 1
      || xtensa_opcode_is_loop (isa, insn->opcode) == 1)
      || xtensa_opcode_is_loop (isa, insn->opcode) == 1)
    {
    {
      int i;
      int i;
 
 
      for (i = 0; i < insn->ntok && i < num_ops; i++)
      for (i = 0; i < insn->ntok && i < num_ops; i++)
        {
        {
          if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
          if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
              && insn->tok[i].X_op == O_symbol)
              && insn->tok[i].X_op == O_symbol)
            {
            {
              symbolS *sym = insn->tok[i].X_add_symbol;
              symbolS *sym = insn->tok[i].X_add_symbol;
              symbol_get_tc (sym)->is_branch_target = TRUE;
              symbol_get_tc (sym)->is_branch_target = TRUE;
              if (S_IS_DEFINED (sym))
              if (S_IS_DEFINED (sym))
                symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
                symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
            }
            }
        }
        }
    }
    }
}
}
 
 
 
 
/* Return FALSE if no error.  */
/* Return FALSE if no error.  */
 
 
static bfd_boolean
static bfd_boolean
xg_build_token_insn (BuildInstr *instr_spec, TInsn *old_insn, TInsn *new_insn)
xg_build_token_insn (BuildInstr *instr_spec, TInsn *old_insn, TInsn *new_insn)
{
{
  int num_ops = 0;
  int num_ops = 0;
  BuildOp *b_op;
  BuildOp *b_op;
 
 
  switch (instr_spec->typ)
  switch (instr_spec->typ)
    {
    {
    case INSTR_INSTR:
    case INSTR_INSTR:
      new_insn->insn_type = ITYPE_INSN;
      new_insn->insn_type = ITYPE_INSN;
      new_insn->opcode = instr_spec->opcode;
      new_insn->opcode = instr_spec->opcode;
      break;
      break;
    case INSTR_LITERAL_DEF:
    case INSTR_LITERAL_DEF:
      new_insn->insn_type = ITYPE_LITERAL;
      new_insn->insn_type = ITYPE_LITERAL;
      new_insn->opcode = XTENSA_UNDEFINED;
      new_insn->opcode = XTENSA_UNDEFINED;
      break;
      break;
    case INSTR_LABEL_DEF:
    case INSTR_LABEL_DEF:
      abort ();
      abort ();
    }
    }
  new_insn->is_specific_opcode = FALSE;
  new_insn->is_specific_opcode = FALSE;
  new_insn->debug_line = old_insn->debug_line;
  new_insn->debug_line = old_insn->debug_line;
  new_insn->loc_directive_seen = old_insn->loc_directive_seen;
  new_insn->loc_directive_seen = old_insn->loc_directive_seen;
 
 
  for (b_op = instr_spec->ops; b_op != NULL; b_op = b_op->next)
  for (b_op = instr_spec->ops; b_op != NULL; b_op = b_op->next)
    {
    {
      expressionS *exp;
      expressionS *exp;
      const expressionS *src_exp;
      const expressionS *src_exp;
 
 
      num_ops++;
      num_ops++;
      switch (b_op->typ)
      switch (b_op->typ)
        {
        {
        case OP_CONSTANT:
        case OP_CONSTANT:
          /* The expression must be the constant.  */
          /* The expression must be the constant.  */
          gas_assert (b_op->op_num < MAX_INSN_ARGS);
          gas_assert (b_op->op_num < MAX_INSN_ARGS);
          exp = &new_insn->tok[b_op->op_num];
          exp = &new_insn->tok[b_op->op_num];
          set_expr_const (exp, b_op->op_data);
          set_expr_const (exp, b_op->op_data);
          break;
          break;
 
 
        case OP_OPERAND:
        case OP_OPERAND:
          gas_assert (b_op->op_num < MAX_INSN_ARGS);
          gas_assert (b_op->op_num < MAX_INSN_ARGS);
          gas_assert (b_op->op_data < (unsigned) old_insn->ntok);
          gas_assert (b_op->op_data < (unsigned) old_insn->ntok);
          src_exp = &old_insn->tok[b_op->op_data];
          src_exp = &old_insn->tok[b_op->op_data];
          exp = &new_insn->tok[b_op->op_num];
          exp = &new_insn->tok[b_op->op_num];
          copy_expr (exp, src_exp);
          copy_expr (exp, src_exp);
          break;
          break;
 
 
        case OP_LITERAL:
        case OP_LITERAL:
        case OP_LABEL:
        case OP_LABEL:
          as_bad (_("can't handle generation of literal/labels yet"));
          as_bad (_("can't handle generation of literal/labels yet"));
          gas_assert (0);
          gas_assert (0);
 
 
        default:
        default:
          as_bad (_("can't handle undefined OP TYPE"));
          as_bad (_("can't handle undefined OP TYPE"));
          gas_assert (0);
          gas_assert (0);
        }
        }
    }
    }
 
 
  new_insn->ntok = num_ops;
  new_insn->ntok = num_ops;
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Return TRUE if it was simplified.  */
/* Return TRUE if it was simplified.  */
 
 
static bfd_boolean
static bfd_boolean
xg_simplify_insn (TInsn *old_insn, TInsn *new_insn)
xg_simplify_insn (TInsn *old_insn, TInsn *new_insn)
{
{
  TransitionRule *rule;
  TransitionRule *rule;
  BuildInstr *insn_spec;
  BuildInstr *insn_spec;
 
 
  if (old_insn->is_specific_opcode || !density_supported)
  if (old_insn->is_specific_opcode || !density_supported)
    return FALSE;
    return FALSE;
 
 
  rule = xg_instruction_match (old_insn);
  rule = xg_instruction_match (old_insn);
  if (rule == NULL)
  if (rule == NULL)
    return FALSE;
    return FALSE;
 
 
  insn_spec = rule->to_instr;
  insn_spec = rule->to_instr;
  /* There should only be one.  */
  /* There should only be one.  */
  gas_assert (insn_spec != NULL);
  gas_assert (insn_spec != NULL);
  gas_assert (insn_spec->next == NULL);
  gas_assert (insn_spec->next == NULL);
  if (insn_spec->next != NULL)
  if (insn_spec->next != NULL)
    return FALSE;
    return FALSE;
 
 
  xg_build_token_insn (insn_spec, old_insn, new_insn);
  xg_build_token_insn (insn_spec, old_insn, new_insn);
 
 
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* xg_expand_assembly_insn: (1) Simplify the instruction, i.e., l32i ->
/* xg_expand_assembly_insn: (1) Simplify the instruction, i.e., l32i ->
   l32i.n. (2) Check the number of operands.  (3) Place the instruction
   l32i.n. (2) Check the number of operands.  (3) Place the instruction
   tokens into the stack or relax it and place multiple
   tokens into the stack or relax it and place multiple
   instructions/literals onto the stack.  Return FALSE if no error.  */
   instructions/literals onto the stack.  Return FALSE if no error.  */
 
 
static bfd_boolean
static bfd_boolean
xg_expand_assembly_insn (IStack *istack, TInsn *orig_insn)
xg_expand_assembly_insn (IStack *istack, TInsn *orig_insn)
{
{
  int noperands;
  int noperands;
  TInsn new_insn;
  TInsn new_insn;
  bfd_boolean do_expand;
  bfd_boolean do_expand;
 
 
  tinsn_init (&new_insn);
  tinsn_init (&new_insn);
 
 
  /* Narrow it if we can.  xg_simplify_insn now does all the
  /* Narrow it if we can.  xg_simplify_insn now does all the
     appropriate checking (e.g., for the density option).  */
     appropriate checking (e.g., for the density option).  */
  if (xg_simplify_insn (orig_insn, &new_insn))
  if (xg_simplify_insn (orig_insn, &new_insn))
    orig_insn = &new_insn;
    orig_insn = &new_insn;
 
 
  noperands = xtensa_opcode_num_operands (xtensa_default_isa,
  noperands = xtensa_opcode_num_operands (xtensa_default_isa,
                                          orig_insn->opcode);
                                          orig_insn->opcode);
  if (orig_insn->ntok < noperands)
  if (orig_insn->ntok < noperands)
    {
    {
      as_bad (_("found %d operands for '%s':  Expected %d"),
      as_bad (_("found %d operands for '%s':  Expected %d"),
              orig_insn->ntok,
              orig_insn->ntok,
              xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
              xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
              noperands);
              noperands);
      return TRUE;
      return TRUE;
    }
    }
  if (orig_insn->ntok > noperands)
  if (orig_insn->ntok > noperands)
    as_warn (_("found too many (%d) operands for '%s':  Expected %d"),
    as_warn (_("found too many (%d) operands for '%s':  Expected %d"),
             orig_insn->ntok,
             orig_insn->ntok,
             xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
             xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
             noperands);
             noperands);
 
 
  /* If there are not enough operands, we will assert above.  If there
  /* If there are not enough operands, we will assert above.  If there
     are too many, just cut out the extras here.  */
     are too many, just cut out the extras here.  */
  orig_insn->ntok = noperands;
  orig_insn->ntok = noperands;
 
 
  if (tinsn_has_invalid_symbolic_operands (orig_insn))
  if (tinsn_has_invalid_symbolic_operands (orig_insn))
    return TRUE;
    return TRUE;
 
 
  /* Special case for extui opcode which has constraints not handled
  /* Special case for extui opcode which has constraints not handled
     by the ordinary operand encoding checks.  The number of operands
     by the ordinary operand encoding checks.  The number of operands
     and related syntax issues have already been checked.  */
     and related syntax issues have already been checked.  */
  if (orig_insn->opcode == xtensa_extui_opcode)
  if (orig_insn->opcode == xtensa_extui_opcode)
    {
    {
      int shiftimm = orig_insn->tok[2].X_add_number;
      int shiftimm = orig_insn->tok[2].X_add_number;
      int maskimm = orig_insn->tok[3].X_add_number;
      int maskimm = orig_insn->tok[3].X_add_number;
      if (shiftimm + maskimm > 32)
      if (shiftimm + maskimm > 32)
        {
        {
          as_bad (_("immediate operands sum to greater than 32"));
          as_bad (_("immediate operands sum to greater than 32"));
          return TRUE;
          return TRUE;
        }
        }
    }
    }
 
 
  /* If the instruction will definitely need to be relaxed, it is better
  /* If the instruction will definitely need to be relaxed, it is better
     to expand it now for better scheduling.  Decide whether to expand
     to expand it now for better scheduling.  Decide whether to expand
     now....  */
     now....  */
  do_expand = (!orig_insn->is_specific_opcode && use_transform ());
  do_expand = (!orig_insn->is_specific_opcode && use_transform ());
 
 
  /* Calls should be expanded to longcalls only in the backend relaxation
  /* Calls should be expanded to longcalls only in the backend relaxation
     so that the assembly scheduler will keep the L32R/CALLX instructions
     so that the assembly scheduler will keep the L32R/CALLX instructions
     adjacent.  */
     adjacent.  */
  if (is_direct_call_opcode (orig_insn->opcode))
  if (is_direct_call_opcode (orig_insn->opcode))
    do_expand = FALSE;
    do_expand = FALSE;
 
 
  if (tinsn_has_symbolic_operands (orig_insn))
  if (tinsn_has_symbolic_operands (orig_insn))
    {
    {
      /* The values of symbolic operands are not known yet, so only expand
      /* The values of symbolic operands are not known yet, so only expand
         now if an operand is "complex" (e.g., difference of symbols) and
         now if an operand is "complex" (e.g., difference of symbols) and
         will have to be stored as a literal regardless of the value.  */
         will have to be stored as a literal regardless of the value.  */
      if (!tinsn_has_complex_operands (orig_insn))
      if (!tinsn_has_complex_operands (orig_insn))
        do_expand = FALSE;
        do_expand = FALSE;
    }
    }
  else if (xg_immeds_fit (orig_insn))
  else if (xg_immeds_fit (orig_insn))
    do_expand = FALSE;
    do_expand = FALSE;
 
 
  if (do_expand)
  if (do_expand)
    xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
    xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
  else
  else
    istack_push (istack, orig_insn);
    istack_push (istack, orig_insn);
 
 
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Return TRUE if the section flags are marked linkonce
/* Return TRUE if the section flags are marked linkonce
   or the name is .gnu.linkonce.*.  */
   or the name is .gnu.linkonce.*.  */
 
 
static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
 
 
static bfd_boolean
static bfd_boolean
get_is_linkonce_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec)
get_is_linkonce_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec)
{
{
  flagword flags, link_once_flags;
  flagword flags, link_once_flags;
 
 
  flags = bfd_get_section_flags (abfd, sec);
  flags = bfd_get_section_flags (abfd, sec);
  link_once_flags = (flags & SEC_LINK_ONCE);
  link_once_flags = (flags & SEC_LINK_ONCE);
 
 
  /* Flags might not be set yet.  */
  /* Flags might not be set yet.  */
  if (!link_once_flags
  if (!link_once_flags
      && strncmp (segment_name (sec), ".gnu.linkonce.", linkonce_len) == 0)
      && strncmp (segment_name (sec), ".gnu.linkonce.", linkonce_len) == 0)
    link_once_flags = SEC_LINK_ONCE;
    link_once_flags = SEC_LINK_ONCE;
 
 
  return (link_once_flags != 0);
  return (link_once_flags != 0);
}
}
 
 
 
 
static void
static void
xtensa_add_literal_sym (symbolS *sym)
xtensa_add_literal_sym (symbolS *sym)
{
{
  sym_list *l;
  sym_list *l;
 
 
  l = (sym_list *) xmalloc (sizeof (sym_list));
  l = (sym_list *) xmalloc (sizeof (sym_list));
  l->sym = sym;
  l->sym = sym;
  l->next = literal_syms;
  l->next = literal_syms;
  literal_syms = l;
  literal_syms = l;
}
}
 
 
 
 
static symbolS *
static symbolS *
xtensa_create_literal_symbol (segT sec, fragS *frag)
xtensa_create_literal_symbol (segT sec, fragS *frag)
{
{
  static int lit_num = 0;
  static int lit_num = 0;
  static char name[256];
  static char name[256];
  symbolS *symbolP;
  symbolS *symbolP;
 
 
  sprintf (name, ".L_lit_sym%d", lit_num);
  sprintf (name, ".L_lit_sym%d", lit_num);
 
 
  /* Create a local symbol.  If it is in a linkonce section, we have to
  /* Create a local symbol.  If it is in a linkonce section, we have to
     be careful to make sure that if it is used in a relocation that the
     be careful to make sure that if it is used in a relocation that the
     symbol will be in the output file.  */
     symbol will be in the output file.  */
  if (get_is_linkonce_section (stdoutput, sec))
  if (get_is_linkonce_section (stdoutput, sec))
    {
    {
      symbolP = symbol_new (name, sec, 0, frag);
      symbolP = symbol_new (name, sec, 0, frag);
      S_CLEAR_EXTERNAL (symbolP);
      S_CLEAR_EXTERNAL (symbolP);
      /* symbolP->local = 1; */
      /* symbolP->local = 1; */
    }
    }
  else
  else
    symbolP = symbol_new (name, sec, 0, frag);
    symbolP = symbol_new (name, sec, 0, frag);
 
 
  xtensa_add_literal_sym (symbolP);
  xtensa_add_literal_sym (symbolP);
 
 
  lit_num++;
  lit_num++;
  return symbolP;
  return symbolP;
}
}
 
 
 
 
/* Currently all literals that are generated here are 32-bit L32R targets.  */
/* Currently all literals that are generated here are 32-bit L32R targets.  */
 
 
static symbolS *
static symbolS *
xg_assemble_literal (/* const */ TInsn *insn)
xg_assemble_literal (/* const */ TInsn *insn)
{
{
  emit_state state;
  emit_state state;
  symbolS *lit_sym = NULL;
  symbolS *lit_sym = NULL;
  bfd_reloc_code_real_type reloc;
  bfd_reloc_code_real_type reloc;
  bfd_boolean pcrel = FALSE;
  bfd_boolean pcrel = FALSE;
  char *p;
  char *p;
 
 
  /* size = 4 for L32R.  It could easily be larger when we move to
  /* size = 4 for L32R.  It could easily be larger when we move to
     larger constants.  Add a parameter later.  */
     larger constants.  Add a parameter later.  */
  offsetT litsize = 4;
  offsetT litsize = 4;
  offsetT litalign = 2;         /* 2^2 = 4 */
  offsetT litalign = 2;         /* 2^2 = 4 */
  expressionS saved_loc;
  expressionS saved_loc;
  expressionS * emit_val;
  expressionS * emit_val;
 
 
  set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
  set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
 
 
  gas_assert (insn->insn_type == ITYPE_LITERAL);
  gas_assert (insn->insn_type == ITYPE_LITERAL);
  gas_assert (insn->ntok == 1); /* must be only one token here */
  gas_assert (insn->ntok == 1); /* must be only one token here */
 
 
  xtensa_switch_to_literal_fragment (&state);
  xtensa_switch_to_literal_fragment (&state);
 
 
  emit_val = &insn->tok[0];
  emit_val = &insn->tok[0];
  if (emit_val->X_op == O_big)
  if (emit_val->X_op == O_big)
    {
    {
      int size = emit_val->X_add_number * CHARS_PER_LITTLENUM;
      int size = emit_val->X_add_number * CHARS_PER_LITTLENUM;
      if (size > litsize)
      if (size > litsize)
        {
        {
          /* This happens when someone writes a "movi a2, big_number".  */
          /* This happens when someone writes a "movi a2, big_number".  */
          as_bad_where (frag_now->fr_file, frag_now->fr_line,
          as_bad_where (frag_now->fr_file, frag_now->fr_line,
                        _("invalid immediate"));
                        _("invalid immediate"));
          xtensa_restore_emit_state (&state);
          xtensa_restore_emit_state (&state);
          return NULL;
          return NULL;
        }
        }
    }
    }
 
 
  /* Force a 4-byte align here.  Note that this opens a new frag, so all
  /* Force a 4-byte align here.  Note that this opens a new frag, so all
     literals done with this function have a frag to themselves.  That's
     literals done with this function have a frag to themselves.  That's
     important for the way text section literals work.  */
     important for the way text section literals work.  */
  frag_align (litalign, 0, 0);
  frag_align (litalign, 0, 0);
  record_alignment (now_seg, litalign);
  record_alignment (now_seg, litalign);
 
 
  switch (emit_val->X_op)
  switch (emit_val->X_op)
    {
    {
    case O_pcrel:
    case O_pcrel:
      pcrel = TRUE;
      pcrel = TRUE;
      /* fall through */
      /* fall through */
    case O_pltrel:
    case O_pltrel:
    case O_tlsfunc:
    case O_tlsfunc:
    case O_tlsarg:
    case O_tlsarg:
    case O_tpoff:
    case O_tpoff:
    case O_dtpoff:
    case O_dtpoff:
      p = frag_more (litsize);
      p = frag_more (litsize);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      reloc = map_operator_to_reloc (emit_val->X_op, TRUE);
      reloc = map_operator_to_reloc (emit_val->X_op, TRUE);
      if (emit_val->X_add_symbol)
      if (emit_val->X_add_symbol)
        emit_val->X_op = O_symbol;
        emit_val->X_op = O_symbol;
      else
      else
        emit_val->X_op = O_constant;
        emit_val->X_op = O_constant;
      fix_new_exp (frag_now, p - frag_now->fr_literal,
      fix_new_exp (frag_now, p - frag_now->fr_literal,
                   litsize, emit_val, pcrel, reloc);
                   litsize, emit_val, pcrel, reloc);
      break;
      break;
 
 
    default:
    default:
      emit_expr (emit_val, litsize);
      emit_expr (emit_val, litsize);
      break;
      break;
    }
    }
 
 
  gas_assert (frag_now->tc_frag_data.literal_frag == NULL);
  gas_assert (frag_now->tc_frag_data.literal_frag == NULL);
  frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
  frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
  frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
  frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
  lit_sym = frag_now->fr_symbol;
  lit_sym = frag_now->fr_symbol;
 
 
  /* Go back.  */
  /* Go back.  */
  xtensa_restore_emit_state (&state);
  xtensa_restore_emit_state (&state);
  return lit_sym;
  return lit_sym;
}
}
 
 
 
 
static void
static void
xg_assemble_literal_space (/* const */ int size, int slot)
xg_assemble_literal_space (/* const */ int size, int slot)
{
{
  emit_state state;
  emit_state state;
  /* We might have to do something about this alignment.  It only
  /* We might have to do something about this alignment.  It only
     takes effect if something is placed here.  */
     takes effect if something is placed here.  */
  offsetT litalign = 2;         /* 2^2 = 4 */
  offsetT litalign = 2;         /* 2^2 = 4 */
  fragS *lit_saved_frag;
  fragS *lit_saved_frag;
 
 
  gas_assert (size % 4 == 0);
  gas_assert (size % 4 == 0);
 
 
  xtensa_switch_to_literal_fragment (&state);
  xtensa_switch_to_literal_fragment (&state);
 
 
  /* Force a 4-byte align here.  */
  /* Force a 4-byte align here.  */
  frag_align (litalign, 0, 0);
  frag_align (litalign, 0, 0);
  record_alignment (now_seg, litalign);
  record_alignment (now_seg, litalign);
 
 
  frag_grow (size);
  frag_grow (size);
 
 
  lit_saved_frag = frag_now;
  lit_saved_frag = frag_now;
  frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
  frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
  frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
  frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
  xg_finish_frag (0, RELAX_LITERAL, 0, size, FALSE);
  xg_finish_frag (0, RELAX_LITERAL, 0, size, FALSE);
 
 
  /* Go back.  */
  /* Go back.  */
  xtensa_restore_emit_state (&state);
  xtensa_restore_emit_state (&state);
  frag_now->tc_frag_data.literal_frags[slot] = lit_saved_frag;
  frag_now->tc_frag_data.literal_frags[slot] = lit_saved_frag;
}
}
 
 
 
 
/* Put in a fixup record based on the opcode.
/* Put in a fixup record based on the opcode.
   Return TRUE on success.  */
   Return TRUE on success.  */
 
 
static bfd_boolean
static bfd_boolean
xg_add_opcode_fix (TInsn *tinsn,
xg_add_opcode_fix (TInsn *tinsn,
                   int opnum,
                   int opnum,
                   xtensa_format fmt,
                   xtensa_format fmt,
                   int slot,
                   int slot,
                   expressionS *expr,
                   expressionS *expr,
                   fragS *fragP,
                   fragS *fragP,
                   offsetT offset)
                   offsetT offset)
{
{
  xtensa_opcode opcode = tinsn->opcode;
  xtensa_opcode opcode = tinsn->opcode;
  bfd_reloc_code_real_type reloc;
  bfd_reloc_code_real_type reloc;
  reloc_howto_type *howto;
  reloc_howto_type *howto;
  int fmt_length;
  int fmt_length;
  fixS *the_fix;
  fixS *the_fix;
 
 
  reloc = BFD_RELOC_NONE;
  reloc = BFD_RELOC_NONE;
 
 
  /* First try the special cases for "alternate" relocs.  */
  /* First try the special cases for "alternate" relocs.  */
  if (opcode == xtensa_l32r_opcode)
  if (opcode == xtensa_l32r_opcode)
    {
    {
      if (fragP->tc_frag_data.use_absolute_literals)
      if (fragP->tc_frag_data.use_absolute_literals)
        reloc = encode_alt_reloc (slot);
        reloc = encode_alt_reloc (slot);
    }
    }
  else if (opcode == xtensa_const16_opcode)
  else if (opcode == xtensa_const16_opcode)
    {
    {
      if (expr->X_op == O_lo16)
      if (expr->X_op == O_lo16)
        {
        {
          reloc = encode_reloc (slot);
          reloc = encode_reloc (slot);
          expr->X_op = O_symbol;
          expr->X_op = O_symbol;
        }
        }
      else if (expr->X_op == O_hi16)
      else if (expr->X_op == O_hi16)
        {
        {
          reloc = encode_alt_reloc (slot);
          reloc = encode_alt_reloc (slot);
          expr->X_op = O_symbol;
          expr->X_op = O_symbol;
        }
        }
    }
    }
 
 
  if (opnum != get_relaxable_immed (opcode))
  if (opnum != get_relaxable_immed (opcode))
    {
    {
      as_bad (_("invalid relocation for operand %i of '%s'"),
      as_bad (_("invalid relocation for operand %i of '%s'"),
              opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
              opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
      return FALSE;
      return FALSE;
    }
    }
 
 
  /* Handle erroneous "@h" and "@l" expressions here before they propagate
  /* Handle erroneous "@h" and "@l" expressions here before they propagate
     into the symbol table where the generic portions of the assembler
     into the symbol table where the generic portions of the assembler
     won't know what to do with them.  */
     won't know what to do with them.  */
  if (expr->X_op == O_lo16 || expr->X_op == O_hi16)
  if (expr->X_op == O_lo16 || expr->X_op == O_hi16)
    {
    {
      as_bad (_("invalid expression for operand %i of '%s'"),
      as_bad (_("invalid expression for operand %i of '%s'"),
              opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
              opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
      return FALSE;
      return FALSE;
    }
    }
 
 
  /* Next try the generic relocs.  */
  /* Next try the generic relocs.  */
  if (reloc == BFD_RELOC_NONE)
  if (reloc == BFD_RELOC_NONE)
    reloc = encode_reloc (slot);
    reloc = encode_reloc (slot);
  if (reloc == BFD_RELOC_NONE)
  if (reloc == BFD_RELOC_NONE)
    {
    {
      as_bad (_("invalid relocation in instruction slot %i"), slot);
      as_bad (_("invalid relocation in instruction slot %i"), slot);
      return FALSE;
      return FALSE;
    }
    }
 
 
  howto = bfd_reloc_type_lookup (stdoutput, reloc);
  howto = bfd_reloc_type_lookup (stdoutput, reloc);
  if (!howto)
  if (!howto)
    {
    {
      as_bad (_("undefined symbol for opcode \"%s\""),
      as_bad (_("undefined symbol for opcode \"%s\""),
              xtensa_opcode_name (xtensa_default_isa, opcode));
              xtensa_opcode_name (xtensa_default_isa, opcode));
      return FALSE;
      return FALSE;
    }
    }
 
 
  fmt_length = xtensa_format_length (xtensa_default_isa, fmt);
  fmt_length = xtensa_format_length (xtensa_default_isa, fmt);
  the_fix = fix_new_exp (fragP, offset, fmt_length, expr,
  the_fix = fix_new_exp (fragP, offset, fmt_length, expr,
                         howto->pc_relative, reloc);
                         howto->pc_relative, reloc);
  the_fix->fx_no_overflow = 1;
  the_fix->fx_no_overflow = 1;
  the_fix->tc_fix_data.X_add_symbol = expr->X_add_symbol;
  the_fix->tc_fix_data.X_add_symbol = expr->X_add_symbol;
  the_fix->tc_fix_data.X_add_number = expr->X_add_number;
  the_fix->tc_fix_data.X_add_number = expr->X_add_number;
  the_fix->tc_fix_data.slot = slot;
  the_fix->tc_fix_data.slot = slot;
 
 
  return TRUE;
  return TRUE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
xg_emit_insn_to_buf (TInsn *tinsn,
xg_emit_insn_to_buf (TInsn *tinsn,
                     char *buf,
                     char *buf,
                     fragS *fragP,
                     fragS *fragP,
                     offsetT offset,
                     offsetT offset,
                     bfd_boolean build_fix)
                     bfd_boolean build_fix)
{
{
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  bfd_boolean has_symbolic_immed = FALSE;
  bfd_boolean has_symbolic_immed = FALSE;
  bfd_boolean ok = TRUE;
  bfd_boolean ok = TRUE;
 
 
  if (!insnbuf)
  if (!insnbuf)
    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
 
 
  has_symbolic_immed = tinsn_to_insnbuf (tinsn, insnbuf);
  has_symbolic_immed = tinsn_to_insnbuf (tinsn, insnbuf);
  if (has_symbolic_immed && build_fix)
  if (has_symbolic_immed && build_fix)
    {
    {
      /* Add a fixup.  */
      /* Add a fixup.  */
      xtensa_format fmt = xg_get_single_format (tinsn->opcode);
      xtensa_format fmt = xg_get_single_format (tinsn->opcode);
      int slot = xg_get_single_slot (tinsn->opcode);
      int slot = xg_get_single_slot (tinsn->opcode);
      int opnum = get_relaxable_immed (tinsn->opcode);
      int opnum = get_relaxable_immed (tinsn->opcode);
      expressionS *exp = &tinsn->tok[opnum];
      expressionS *exp = &tinsn->tok[opnum];
 
 
      if (!xg_add_opcode_fix (tinsn, opnum, fmt, slot, exp, fragP, offset))
      if (!xg_add_opcode_fix (tinsn, opnum, fmt, slot, exp, fragP, offset))
        ok = FALSE;
        ok = FALSE;
    }
    }
  fragP->tc_frag_data.is_insn = TRUE;
  fragP->tc_frag_data.is_insn = TRUE;
  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
                           (unsigned char *) buf, 0);
                           (unsigned char *) buf, 0);
  return ok;
  return ok;
}
}
 
 
 
 
static void
static void
xg_resolve_literals (TInsn *insn, symbolS *lit_sym)
xg_resolve_literals (TInsn *insn, symbolS *lit_sym)
{
{
  symbolS *sym = get_special_literal_symbol ();
  symbolS *sym = get_special_literal_symbol ();
  int i;
  int i;
  if (lit_sym == 0)
  if (lit_sym == 0)
    return;
    return;
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
  for (i = 0; i < insn->ntok; i++)
  for (i = 0; i < insn->ntok; i++)
    if (insn->tok[i].X_add_symbol == sym)
    if (insn->tok[i].X_add_symbol == sym)
      insn->tok[i].X_add_symbol = lit_sym;
      insn->tok[i].X_add_symbol = lit_sym;
 
 
}
}
 
 
 
 
static void
static void
xg_resolve_labels (TInsn *insn, symbolS *label_sym)
xg_resolve_labels (TInsn *insn, symbolS *label_sym)
{
{
  symbolS *sym = get_special_label_symbol ();
  symbolS *sym = get_special_label_symbol ();
  int i;
  int i;
  for (i = 0; i < insn->ntok; i++)
  for (i = 0; i < insn->ntok; i++)
    if (insn->tok[i].X_add_symbol == sym)
    if (insn->tok[i].X_add_symbol == sym)
      insn->tok[i].X_add_symbol = label_sym;
      insn->tok[i].X_add_symbol = label_sym;
 
 
}
}
 
 
 
 
/* Return TRUE if the instruction can write to the specified
/* Return TRUE if the instruction can write to the specified
   integer register.  */
   integer register.  */
 
 
static bfd_boolean
static bfd_boolean
is_register_writer (const TInsn *insn, const char *regset, int regnum)
is_register_writer (const TInsn *insn, const char *regset, int regnum)
{
{
  int i;
  int i;
  int num_ops;
  int num_ops;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
 
 
  num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
  num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
 
 
  for (i = 0; i < num_ops; i++)
  for (i = 0; i < num_ops; i++)
    {
    {
      char inout;
      char inout;
      inout = xtensa_operand_inout (isa, insn->opcode, i);
      inout = xtensa_operand_inout (isa, insn->opcode, i);
      if ((inout == 'o' || inout == 'm')
      if ((inout == 'o' || inout == 'm')
          && xtensa_operand_is_register (isa, insn->opcode, i) == 1)
          && xtensa_operand_is_register (isa, insn->opcode, i) == 1)
        {
        {
          xtensa_regfile opnd_rf =
          xtensa_regfile opnd_rf =
            xtensa_operand_regfile (isa, insn->opcode, i);
            xtensa_operand_regfile (isa, insn->opcode, i);
          if (!strcmp (xtensa_regfile_shortname (isa, opnd_rf), regset))
          if (!strcmp (xtensa_regfile_shortname (isa, opnd_rf), regset))
            {
            {
              if ((insn->tok[i].X_op == O_register)
              if ((insn->tok[i].X_op == O_register)
                  && (insn->tok[i].X_add_number == regnum))
                  && (insn->tok[i].X_add_number == regnum))
                return TRUE;
                return TRUE;
            }
            }
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
is_bad_loopend_opcode (const TInsn *tinsn)
is_bad_loopend_opcode (const TInsn *tinsn)
{
{
  xtensa_opcode opcode = tinsn->opcode;
  xtensa_opcode opcode = tinsn->opcode;
 
 
  if (opcode == XTENSA_UNDEFINED)
  if (opcode == XTENSA_UNDEFINED)
    return FALSE;
    return FALSE;
 
 
  if (opcode == xtensa_call0_opcode
  if (opcode == xtensa_call0_opcode
      || opcode == xtensa_callx0_opcode
      || opcode == xtensa_callx0_opcode
      || opcode == xtensa_call4_opcode
      || opcode == xtensa_call4_opcode
      || opcode == xtensa_callx4_opcode
      || opcode == xtensa_callx4_opcode
      || opcode == xtensa_call8_opcode
      || opcode == xtensa_call8_opcode
      || opcode == xtensa_callx8_opcode
      || opcode == xtensa_callx8_opcode
      || opcode == xtensa_call12_opcode
      || opcode == xtensa_call12_opcode
      || opcode == xtensa_callx12_opcode
      || opcode == xtensa_callx12_opcode
      || opcode == xtensa_isync_opcode
      || opcode == xtensa_isync_opcode
      || opcode == xtensa_ret_opcode
      || opcode == xtensa_ret_opcode
      || opcode == xtensa_ret_n_opcode
      || opcode == xtensa_ret_n_opcode
      || opcode == xtensa_retw_opcode
      || opcode == xtensa_retw_opcode
      || opcode == xtensa_retw_n_opcode
      || opcode == xtensa_retw_n_opcode
      || opcode == xtensa_waiti_opcode
      || opcode == xtensa_waiti_opcode
      || opcode == xtensa_rsr_lcount_opcode)
      || opcode == xtensa_rsr_lcount_opcode)
    return TRUE;
    return TRUE;
 
 
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Labels that begin with ".Ln" or ".LM"  are unaligned.
/* Labels that begin with ".Ln" or ".LM"  are unaligned.
   This allows the debugger to add unaligned labels.
   This allows the debugger to add unaligned labels.
   Also, the assembler generates stabs labels that need
   Also, the assembler generates stabs labels that need
   not be aligned:  FAKE_LABEL_NAME . {"F", "L", "endfunc"}.  */
   not be aligned:  FAKE_LABEL_NAME . {"F", "L", "endfunc"}.  */
 
 
static bfd_boolean
static bfd_boolean
is_unaligned_label (symbolS *sym)
is_unaligned_label (symbolS *sym)
{
{
  const char *name = S_GET_NAME (sym);
  const char *name = S_GET_NAME (sym);
  static size_t fake_size = 0;
  static size_t fake_size = 0;
 
 
  if (name
  if (name
      && name[0] == '.'
      && name[0] == '.'
      && name[1] == 'L' && (name[2] == 'n' || name[2] == 'M'))
      && name[1] == 'L' && (name[2] == 'n' || name[2] == 'M'))
    return TRUE;
    return TRUE;
 
 
  /* FAKE_LABEL_NAME followed by "F", "L" or "endfunc" */
  /* FAKE_LABEL_NAME followed by "F", "L" or "endfunc" */
  if (fake_size == 0)
  if (fake_size == 0)
    fake_size = strlen (FAKE_LABEL_NAME);
    fake_size = strlen (FAKE_LABEL_NAME);
 
 
  if (name
  if (name
      && strncmp (FAKE_LABEL_NAME, name, fake_size) == 0
      && strncmp (FAKE_LABEL_NAME, name, fake_size) == 0
      && (name[fake_size] == 'F'
      && (name[fake_size] == 'F'
          || name[fake_size] == 'L'
          || name[fake_size] == 'L'
          || (name[fake_size] == 'e'
          || (name[fake_size] == 'e'
              && strncmp ("endfunc", name+fake_size, 7) == 0)))
              && strncmp ("endfunc", name+fake_size, 7) == 0)))
    return TRUE;
    return TRUE;
 
 
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static fragS *
static fragS *
next_non_empty_frag (const fragS *fragP)
next_non_empty_frag (const fragS *fragP)
{
{
  fragS *next_fragP = fragP->fr_next;
  fragS *next_fragP = fragP->fr_next;
 
 
  /* Sometimes an empty will end up here due storage allocation issues.
  /* Sometimes an empty will end up here due storage allocation issues.
     So we have to skip until we find something legit.  */
     So we have to skip until we find something legit.  */
  while (next_fragP && next_fragP->fr_fix == 0)
  while (next_fragP && next_fragP->fr_fix == 0)
    next_fragP = next_fragP->fr_next;
    next_fragP = next_fragP->fr_next;
 
 
  if (next_fragP == NULL || next_fragP->fr_fix == 0)
  if (next_fragP == NULL || next_fragP->fr_fix == 0)
    return NULL;
    return NULL;
 
 
  return next_fragP;
  return next_fragP;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
next_frag_opcode_is_loop (const fragS *fragP, xtensa_opcode *opcode)
next_frag_opcode_is_loop (const fragS *fragP, xtensa_opcode *opcode)
{
{
  xtensa_opcode out_opcode;
  xtensa_opcode out_opcode;
  const fragS *next_fragP = next_non_empty_frag (fragP);
  const fragS *next_fragP = next_non_empty_frag (fragP);
 
 
  if (next_fragP == NULL)
  if (next_fragP == NULL)
    return FALSE;
    return FALSE;
 
 
  out_opcode = get_opcode_from_buf (next_fragP->fr_literal, 0);
  out_opcode = get_opcode_from_buf (next_fragP->fr_literal, 0);
  if (xtensa_opcode_is_loop (xtensa_default_isa, out_opcode) == 1)
  if (xtensa_opcode_is_loop (xtensa_default_isa, out_opcode) == 1)
    {
    {
      *opcode = out_opcode;
      *opcode = out_opcode;
      return TRUE;
      return TRUE;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static int
static int
frag_format_size (const fragS *fragP)
frag_format_size (const fragS *fragP)
{
{
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_format fmt;
  xtensa_format fmt;
  int fmt_size;
  int fmt_size;
 
 
  if (!insnbuf)
  if (!insnbuf)
    insnbuf = xtensa_insnbuf_alloc (isa);
    insnbuf = xtensa_insnbuf_alloc (isa);
 
 
  if (fragP == NULL)
  if (fragP == NULL)
    return XTENSA_UNDEFINED;
    return XTENSA_UNDEFINED;
 
 
  xtensa_insnbuf_from_chars (isa, insnbuf,
  xtensa_insnbuf_from_chars (isa, insnbuf,
                             (unsigned char *) fragP->fr_literal, 0);
                             (unsigned char *) fragP->fr_literal, 0);
 
 
  fmt = xtensa_format_decode (isa, insnbuf);
  fmt = xtensa_format_decode (isa, insnbuf);
  if (fmt == XTENSA_UNDEFINED)
  if (fmt == XTENSA_UNDEFINED)
    return XTENSA_UNDEFINED;
    return XTENSA_UNDEFINED;
  fmt_size = xtensa_format_length (isa, fmt);
  fmt_size = xtensa_format_length (isa, fmt);
 
 
  /* If the next format won't be changing due to relaxation, just
  /* If the next format won't be changing due to relaxation, just
     return the length of the first format.  */
     return the length of the first format.  */
  if (fragP->fr_opcode != fragP->fr_literal)
  if (fragP->fr_opcode != fragP->fr_literal)
    return fmt_size;
    return fmt_size;
 
 
  /* If during relaxation we have to pull an instruction out of a
  /* If during relaxation we have to pull an instruction out of a
     multi-slot instruction, we will return the more conservative
     multi-slot instruction, we will return the more conservative
     number.  This works because alignment on bigger instructions
     number.  This works because alignment on bigger instructions
     is more restrictive than alignment on smaller instructions.
     is more restrictive than alignment on smaller instructions.
     This is more conservative than we would like, but it happens
     This is more conservative than we would like, but it happens
     infrequently.  */
     infrequently.  */
 
 
  if (xtensa_format_num_slots (xtensa_default_isa, fmt) > 1)
  if (xtensa_format_num_slots (xtensa_default_isa, fmt) > 1)
    return fmt_size;
    return fmt_size;
 
 
  /* If we aren't doing one of our own relaxations or it isn't
  /* If we aren't doing one of our own relaxations or it isn't
     slot-based, then the insn size won't change.  */
     slot-based, then the insn size won't change.  */
  if (fragP->fr_type != rs_machine_dependent)
  if (fragP->fr_type != rs_machine_dependent)
    return fmt_size;
    return fmt_size;
  if (fragP->fr_subtype != RELAX_SLOTS)
  if (fragP->fr_subtype != RELAX_SLOTS)
    return fmt_size;
    return fmt_size;
 
 
  /* If an instruction is about to grow, return the longer size.  */
  /* If an instruction is about to grow, return the longer size.  */
  if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP1
  if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP1
      || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP2
      || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP2
      || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP3)
      || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP3)
    {
    {
      /* For most frags at RELAX_IMMED_STEPX, with X > 0, the first
      /* For most frags at RELAX_IMMED_STEPX, with X > 0, the first
         instruction in the relaxed version is of length 3.  (The case
         instruction in the relaxed version is of length 3.  (The case
         where we have to pull the instruction out of a FLIX bundle
         where we have to pull the instruction out of a FLIX bundle
         is handled conservatively above.)  However, frags with opcodes
         is handled conservatively above.)  However, frags with opcodes
         that are expanding to wide branches end up having formats that
         that are expanding to wide branches end up having formats that
         are not determinable by the RELAX_IMMED_STEPX enumeration, and
         are not determinable by the RELAX_IMMED_STEPX enumeration, and
         we can't tell directly what format the relaxer picked.  This
         we can't tell directly what format the relaxer picked.  This
         is a wart in the design of the relaxer that should someday be
         is a wart in the design of the relaxer that should someday be
         fixed, but would require major changes, or at least should
         fixed, but would require major changes, or at least should
         be accompanied by major changes to make use of that data.
         be accompanied by major changes to make use of that data.
 
 
         In any event, we can tell that we are expanding from a single-slot
         In any event, we can tell that we are expanding from a single-slot
         three-byte format to a wider one with the logic below.  */
         three-byte format to a wider one with the logic below.  */
 
 
      if (fmt_size <= 3 && fragP->tc_frag_data.text_expansion[0] != 3)
      if (fmt_size <= 3 && fragP->tc_frag_data.text_expansion[0] != 3)
        return 3 + fragP->tc_frag_data.text_expansion[0];
        return 3 + fragP->tc_frag_data.text_expansion[0];
      else
      else
        return 3;
        return 3;
    }
    }
 
 
  if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
  if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
    return 2 + fragP->tc_frag_data.text_expansion[0];
    return 2 + fragP->tc_frag_data.text_expansion[0];
 
 
  return fmt_size;
  return fmt_size;
}
}
 
 
 
 
static int
static int
next_frag_format_size (const fragS *fragP)
next_frag_format_size (const fragS *fragP)
{
{
  const fragS *next_fragP = next_non_empty_frag (fragP);
  const fragS *next_fragP = next_non_empty_frag (fragP);
  return frag_format_size (next_fragP);
  return frag_format_size (next_fragP);
}
}
 
 
 
 
/* In early Xtensa Processors, for reasons that are unclear, the ISA
/* In early Xtensa Processors, for reasons that are unclear, the ISA
   required two-byte instructions to be treated as three-byte instructions
   required two-byte instructions to be treated as three-byte instructions
   for loop instruction alignment.  This restriction was removed beginning
   for loop instruction alignment.  This restriction was removed beginning
   with Xtensa LX.  Now the only requirement on loop instruction alignment
   with Xtensa LX.  Now the only requirement on loop instruction alignment
   is that the first instruction of the loop must appear at an address that
   is that the first instruction of the loop must appear at an address that
   does not cross a fetch boundary.  */
   does not cross a fetch boundary.  */
 
 
static int
static int
get_loop_align_size (int insn_size)
get_loop_align_size (int insn_size)
{
{
  if (insn_size == XTENSA_UNDEFINED)
  if (insn_size == XTENSA_UNDEFINED)
    return xtensa_fetch_width;
    return xtensa_fetch_width;
 
 
  if (enforce_three_byte_loop_align && insn_size == 2)
  if (enforce_three_byte_loop_align && insn_size == 2)
    return 3;
    return 3;
 
 
  return insn_size;
  return insn_size;
}
}
 
 
 
 
/* If the next legit fragment is an end-of-loop marker,
/* If the next legit fragment is an end-of-loop marker,
   switch its state so it will instantiate a NOP.  */
   switch its state so it will instantiate a NOP.  */
 
 
static void
static void
update_next_frag_state (fragS *fragP)
update_next_frag_state (fragS *fragP)
{
{
  fragS *next_fragP = fragP->fr_next;
  fragS *next_fragP = fragP->fr_next;
  fragS *new_target = NULL;
  fragS *new_target = NULL;
 
 
  if (align_targets)
  if (align_targets)
    {
    {
      /* We are guaranteed there will be one of these...   */
      /* We are guaranteed there will be one of these...   */
      while (!(next_fragP->fr_type == rs_machine_dependent
      while (!(next_fragP->fr_type == rs_machine_dependent
               && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
               && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
                   || next_fragP->fr_subtype == RELAX_UNREACHABLE)))
                   || next_fragP->fr_subtype == RELAX_UNREACHABLE)))
        next_fragP = next_fragP->fr_next;
        next_fragP = next_fragP->fr_next;
 
 
      gas_assert (next_fragP->fr_type == rs_machine_dependent
      gas_assert (next_fragP->fr_type == rs_machine_dependent
              && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
              && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
                  || next_fragP->fr_subtype == RELAX_UNREACHABLE));
                  || next_fragP->fr_subtype == RELAX_UNREACHABLE));
 
 
      /* ...and one of these.  */
      /* ...and one of these.  */
      new_target = next_fragP->fr_next;
      new_target = next_fragP->fr_next;
      while (!(new_target->fr_type == rs_machine_dependent
      while (!(new_target->fr_type == rs_machine_dependent
               && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
               && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
                   || new_target->fr_subtype == RELAX_DESIRE_ALIGN)))
                   || new_target->fr_subtype == RELAX_DESIRE_ALIGN)))
        new_target = new_target->fr_next;
        new_target = new_target->fr_next;
 
 
      gas_assert (new_target->fr_type == rs_machine_dependent
      gas_assert (new_target->fr_type == rs_machine_dependent
              && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
              && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
                  || new_target->fr_subtype == RELAX_DESIRE_ALIGN));
                  || new_target->fr_subtype == RELAX_DESIRE_ALIGN));
    }
    }
 
 
  while (next_fragP && next_fragP->fr_fix == 0)
  while (next_fragP && next_fragP->fr_fix == 0)
    {
    {
      if (next_fragP->fr_type == rs_machine_dependent
      if (next_fragP->fr_type == rs_machine_dependent
          && next_fragP->fr_subtype == RELAX_LOOP_END)
          && next_fragP->fr_subtype == RELAX_LOOP_END)
        {
        {
          next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP;
          next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP;
          return;
          return;
        }
        }
 
 
      next_fragP = next_fragP->fr_next;
      next_fragP = next_fragP->fr_next;
    }
    }
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
next_frag_is_branch_target (const fragS *fragP)
next_frag_is_branch_target (const fragS *fragP)
{
{
  /* Sometimes an empty will end up here due to storage allocation issues,
  /* Sometimes an empty will end up here due to storage allocation issues,
     so we have to skip until we find something legit.  */
     so we have to skip until we find something legit.  */
  for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
  for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
    {
    {
      if (fragP->tc_frag_data.is_branch_target)
      if (fragP->tc_frag_data.is_branch_target)
        return TRUE;
        return TRUE;
      if (fragP->fr_fix != 0)
      if (fragP->fr_fix != 0)
        break;
        break;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
next_frag_is_loop_target (const fragS *fragP)
next_frag_is_loop_target (const fragS *fragP)
{
{
  /* Sometimes an empty will end up here due storage allocation issues.
  /* Sometimes an empty will end up here due storage allocation issues.
     So we have to skip until we find something legit. */
     So we have to skip until we find something legit. */
  for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
  for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
    {
    {
      if (fragP->tc_frag_data.is_loop_target)
      if (fragP->tc_frag_data.is_loop_target)
        return TRUE;
        return TRUE;
      if (fragP->fr_fix != 0)
      if (fragP->fr_fix != 0)
        break;
        break;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static addressT
static addressT
next_frag_pre_opcode_bytes (const fragS *fragp)
next_frag_pre_opcode_bytes (const fragS *fragp)
{
{
  const fragS *next_fragp = fragp->fr_next;
  const fragS *next_fragp = fragp->fr_next;
  xtensa_opcode next_opcode;
  xtensa_opcode next_opcode;
 
 
  if (!next_frag_opcode_is_loop (fragp, &next_opcode))
  if (!next_frag_opcode_is_loop (fragp, &next_opcode))
    return 0;
    return 0;
 
 
  /* Sometimes an empty will end up here due to storage allocation issues,
  /* Sometimes an empty will end up here due to storage allocation issues,
     so we have to skip until we find something legit.  */
     so we have to skip until we find something legit.  */
  while (next_fragp->fr_fix == 0)
  while (next_fragp->fr_fix == 0)
    next_fragp = next_fragp->fr_next;
    next_fragp = next_fragp->fr_next;
 
 
  if (next_fragp->fr_type != rs_machine_dependent)
  if (next_fragp->fr_type != rs_machine_dependent)
    return 0;
    return 0;
 
 
  /* There is some implicit knowledge encoded in here.
  /* There is some implicit knowledge encoded in here.
     The LOOP instructions that are NOT RELAX_IMMED have
     The LOOP instructions that are NOT RELAX_IMMED have
     been relaxed.  Note that we can assume that the LOOP
     been relaxed.  Note that we can assume that the LOOP
     instruction is in slot 0 because loops aren't bundleable.  */
     instruction is in slot 0 because loops aren't bundleable.  */
  if (next_fragp->tc_frag_data.slot_subtypes[0] > RELAX_IMMED)
  if (next_fragp->tc_frag_data.slot_subtypes[0] > RELAX_IMMED)
      return get_expanded_loop_offset (next_opcode);
      return get_expanded_loop_offset (next_opcode);
 
 
  return 0;
  return 0;
}
}
 
 
 
 
/* Mark a location where we can later insert literal frags.  Update
/* Mark a location where we can later insert literal frags.  Update
   the section's literal_pool_loc, so subsequent literals can be
   the section's literal_pool_loc, so subsequent literals can be
   placed nearest to their use.  */
   placed nearest to their use.  */
 
 
static void
static void
xtensa_mark_literal_pool_location (void)
xtensa_mark_literal_pool_location (void)
{
{
  /* Any labels pointing to the current location need
  /* Any labels pointing to the current location need
     to be adjusted to after the literal pool.  */
     to be adjusted to after the literal pool.  */
  emit_state s;
  emit_state s;
  fragS *pool_location;
  fragS *pool_location;
 
 
  if (use_literal_section)
  if (use_literal_section)
    return;
    return;
 
 
  /* We stash info in these frags so we can later move the literal's
  /* We stash info in these frags so we can later move the literal's
     fixes into this frchain's fix list.  */
     fixes into this frchain's fix list.  */
  pool_location = frag_now;
  pool_location = frag_now;
  frag_now->tc_frag_data.lit_frchain = frchain_now;
  frag_now->tc_frag_data.lit_frchain = frchain_now;
  frag_now->tc_frag_data.literal_frag = frag_now;
  frag_now->tc_frag_data.literal_frag = frag_now;
  frag_variant (rs_machine_dependent, 0, 0,
  frag_variant (rs_machine_dependent, 0, 0,
                RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
                RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
  xtensa_set_frag_assembly_state (frag_now);
  xtensa_set_frag_assembly_state (frag_now);
  frag_now->tc_frag_data.lit_seg = now_seg;
  frag_now->tc_frag_data.lit_seg = now_seg;
  frag_variant (rs_machine_dependent, 0, 0,
  frag_variant (rs_machine_dependent, 0, 0,
                RELAX_LITERAL_POOL_END, NULL, 0, NULL);
                RELAX_LITERAL_POOL_END, NULL, 0, NULL);
  xtensa_set_frag_assembly_state (frag_now);
  xtensa_set_frag_assembly_state (frag_now);
 
 
  /* Now put a frag into the literal pool that points to this location.  */
  /* Now put a frag into the literal pool that points to this location.  */
  set_literal_pool_location (now_seg, pool_location);
  set_literal_pool_location (now_seg, pool_location);
  xtensa_switch_to_non_abs_literal_fragment (&s);
  xtensa_switch_to_non_abs_literal_fragment (&s);
  frag_align (2, 0, 0);
  frag_align (2, 0, 0);
  record_alignment (now_seg, 2);
  record_alignment (now_seg, 2);
 
 
  /* Close whatever frag is there.  */
  /* Close whatever frag is there.  */
  frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
  frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
  xtensa_set_frag_assembly_state (frag_now);
  xtensa_set_frag_assembly_state (frag_now);
  frag_now->tc_frag_data.literal_frag = pool_location;
  frag_now->tc_frag_data.literal_frag = pool_location;
  frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
  frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
  xtensa_restore_emit_state (&s);
  xtensa_restore_emit_state (&s);
  xtensa_set_frag_assembly_state (frag_now);
  xtensa_set_frag_assembly_state (frag_now);
}
}
 
 
 
 
/* Build a nop of the correct size into tinsn.  */
/* Build a nop of the correct size into tinsn.  */
 
 
static void
static void
build_nop (TInsn *tinsn, int size)
build_nop (TInsn *tinsn, int size)
{
{
  tinsn_init (tinsn);
  tinsn_init (tinsn);
  switch (size)
  switch (size)
    {
    {
    case 2:
    case 2:
      tinsn->opcode = xtensa_nop_n_opcode;
      tinsn->opcode = xtensa_nop_n_opcode;
      tinsn->ntok = 0;
      tinsn->ntok = 0;
      if (tinsn->opcode == XTENSA_UNDEFINED)
      if (tinsn->opcode == XTENSA_UNDEFINED)
        as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
        as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
      break;
      break;
 
 
    case 3:
    case 3:
      if (xtensa_nop_opcode == XTENSA_UNDEFINED)
      if (xtensa_nop_opcode == XTENSA_UNDEFINED)
        {
        {
          tinsn->opcode = xtensa_or_opcode;
          tinsn->opcode = xtensa_or_opcode;
          set_expr_const (&tinsn->tok[0], 1);
          set_expr_const (&tinsn->tok[0], 1);
          set_expr_const (&tinsn->tok[1], 1);
          set_expr_const (&tinsn->tok[1], 1);
          set_expr_const (&tinsn->tok[2], 1);
          set_expr_const (&tinsn->tok[2], 1);
          tinsn->ntok = 3;
          tinsn->ntok = 3;
        }
        }
      else
      else
        tinsn->opcode = xtensa_nop_opcode;
        tinsn->opcode = xtensa_nop_opcode;
 
 
      gas_assert (tinsn->opcode != XTENSA_UNDEFINED);
      gas_assert (tinsn->opcode != XTENSA_UNDEFINED);
    }
    }
}
}
 
 
 
 
/* Assemble a NOP of the requested size in the buffer.  User must have
/* Assemble a NOP of the requested size in the buffer.  User must have
   allocated "buf" with at least "size" bytes.  */
   allocated "buf" with at least "size" bytes.  */
 
 
static void
static void
assemble_nop (int size, char *buf)
assemble_nop (int size, char *buf)
{
{
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  TInsn tinsn;
  TInsn tinsn;
 
 
  build_nop (&tinsn, size);
  build_nop (&tinsn, size);
 
 
  if (!insnbuf)
  if (!insnbuf)
    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
 
 
  tinsn_to_insnbuf (&tinsn, insnbuf);
  tinsn_to_insnbuf (&tinsn, insnbuf);
  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
                           (unsigned char *) buf, 0);
                           (unsigned char *) buf, 0);
}
}
 
 
 
 
/* Return the number of bytes for the offset of the expanded loop
/* Return the number of bytes for the offset of the expanded loop
   instruction.  This should be incorporated into the relaxation
   instruction.  This should be incorporated into the relaxation
   specification but is hard-coded here.  This is used to auto-align
   specification but is hard-coded here.  This is used to auto-align
   the loop instruction.  It is invalid to call this function if the
   the loop instruction.  It is invalid to call this function if the
   configuration does not have loops or if the opcode is not a loop
   configuration does not have loops or if the opcode is not a loop
   opcode.  */
   opcode.  */
 
 
static addressT
static addressT
get_expanded_loop_offset (xtensa_opcode opcode)
get_expanded_loop_offset (xtensa_opcode opcode)
{
{
  /* This is the OFFSET of the loop instruction in the expanded loop.
  /* This is the OFFSET of the loop instruction in the expanded loop.
     This MUST correspond directly to the specification of the loop
     This MUST correspond directly to the specification of the loop
     expansion.  It will be validated on fragment conversion.  */
     expansion.  It will be validated on fragment conversion.  */
  gas_assert (opcode != XTENSA_UNDEFINED);
  gas_assert (opcode != XTENSA_UNDEFINED);
  if (opcode == xtensa_loop_opcode)
  if (opcode == xtensa_loop_opcode)
    return 0;
    return 0;
  if (opcode == xtensa_loopnez_opcode)
  if (opcode == xtensa_loopnez_opcode)
    return 3;
    return 3;
  if (opcode == xtensa_loopgtz_opcode)
  if (opcode == xtensa_loopgtz_opcode)
    return 6;
    return 6;
  as_fatal (_("get_expanded_loop_offset: invalid opcode"));
  as_fatal (_("get_expanded_loop_offset: invalid opcode"));
  return 0;
  return 0;
}
}
 
 
 
 
static fragS *
static fragS *
get_literal_pool_location (segT seg)
get_literal_pool_location (segT seg)
{
{
  return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
  return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
}
}
 
 
 
 
static void
static void
set_literal_pool_location (segT seg, fragS *literal_pool_loc)
set_literal_pool_location (segT seg, fragS *literal_pool_loc)
{
{
  seg_info (seg)->tc_segment_info_data.literal_pool_loc = literal_pool_loc;
  seg_info (seg)->tc_segment_info_data.literal_pool_loc = literal_pool_loc;
}
}
 
 
 
 
/* Set frag assembly state should be called when a new frag is
/* Set frag assembly state should be called when a new frag is
   opened and after a frag has been closed.  */
   opened and after a frag has been closed.  */
 
 
static void
static void
xtensa_set_frag_assembly_state (fragS *fragP)
xtensa_set_frag_assembly_state (fragS *fragP)
{
{
  if (!density_supported)
  if (!density_supported)
    fragP->tc_frag_data.is_no_density = TRUE;
    fragP->tc_frag_data.is_no_density = TRUE;
 
 
  /* This function is called from subsegs_finish, which is called
  /* This function is called from subsegs_finish, which is called
     after xtensa_end, so we can't use "use_transform" or
     after xtensa_end, so we can't use "use_transform" or
     "use_schedule" here.  */
     "use_schedule" here.  */
  if (!directive_state[directive_transform])
  if (!directive_state[directive_transform])
    fragP->tc_frag_data.is_no_transform = TRUE;
    fragP->tc_frag_data.is_no_transform = TRUE;
  if (directive_state[directive_longcalls])
  if (directive_state[directive_longcalls])
    fragP->tc_frag_data.use_longcalls = TRUE;
    fragP->tc_frag_data.use_longcalls = TRUE;
  fragP->tc_frag_data.use_absolute_literals =
  fragP->tc_frag_data.use_absolute_literals =
    directive_state[directive_absolute_literals];
    directive_state[directive_absolute_literals];
  fragP->tc_frag_data.is_assembly_state_set = TRUE;
  fragP->tc_frag_data.is_assembly_state_set = TRUE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
relaxable_section (asection *sec)
relaxable_section (asection *sec)
{
{
  return ((sec->flags & SEC_DEBUGGING) == 0
  return ((sec->flags & SEC_DEBUGGING) == 0
          && strcmp (sec->name, ".eh_frame") != 0);
          && strcmp (sec->name, ".eh_frame") != 0);
}
}
 
 
 
 
static void
static void
xtensa_mark_frags_for_org (void)
xtensa_mark_frags_for_org (void)
{
{
  segT *seclist;
  segT *seclist;
 
 
  /* Walk over each fragment of all of the current segments.  If we find
  /* Walk over each fragment of all of the current segments.  If we find
     a .org frag in any of the segments, mark all frags prior to it as
     a .org frag in any of the segments, mark all frags prior to it as
     "no transform", which will prevent linker optimizations from messing
     "no transform", which will prevent linker optimizations from messing
     up the .org distance.  This should be done after
     up the .org distance.  This should be done after
     xtensa_find_unmarked_state_frags, because we don't want to worry here
     xtensa_find_unmarked_state_frags, because we don't want to worry here
     about that function trashing the data we save here.  */
     about that function trashing the data we save here.  */
 
 
  for (seclist = &stdoutput->sections;
  for (seclist = &stdoutput->sections;
       seclist && *seclist;
       seclist && *seclist;
       seclist = &(*seclist)->next)
       seclist = &(*seclist)->next)
    {
    {
      segT sec = *seclist;
      segT sec = *seclist;
      segment_info_type *seginfo;
      segment_info_type *seginfo;
      fragS *fragP;
      fragS *fragP;
      flagword flags;
      flagword flags;
      flags = bfd_get_section_flags (stdoutput, sec);
      flags = bfd_get_section_flags (stdoutput, sec);
      if (flags & SEC_DEBUGGING)
      if (flags & SEC_DEBUGGING)
        continue;
        continue;
      if (!(flags & SEC_ALLOC))
      if (!(flags & SEC_ALLOC))
        continue;
        continue;
 
 
      seginfo = seg_info (sec);
      seginfo = seg_info (sec);
      if (seginfo && seginfo->frchainP)
      if (seginfo && seginfo->frchainP)
        {
        {
          fragS *last_fragP = seginfo->frchainP->frch_root;
          fragS *last_fragP = seginfo->frchainP->frch_root;
          for (fragP = seginfo->frchainP->frch_root; fragP;
          for (fragP = seginfo->frchainP->frch_root; fragP;
               fragP = fragP->fr_next)
               fragP = fragP->fr_next)
            {
            {
              /* cvt_frag_to_fill has changed the fr_type of org frags to
              /* cvt_frag_to_fill has changed the fr_type of org frags to
                 rs_fill, so use the value as cached in rs_subtype here.  */
                 rs_fill, so use the value as cached in rs_subtype here.  */
              if (fragP->fr_subtype == RELAX_ORG)
              if (fragP->fr_subtype == RELAX_ORG)
                {
                {
                  while (last_fragP != fragP->fr_next)
                  while (last_fragP != fragP->fr_next)
                    {
                    {
                      last_fragP->tc_frag_data.is_no_transform = TRUE;
                      last_fragP->tc_frag_data.is_no_transform = TRUE;
                      last_fragP = last_fragP->fr_next;
                      last_fragP = last_fragP->fr_next;
                    }
                    }
                }
                }
            }
            }
        }
        }
    }
    }
}
}
 
 
 
 
static void
static void
xtensa_find_unmarked_state_frags (void)
xtensa_find_unmarked_state_frags (void)
{
{
  segT *seclist;
  segT *seclist;
 
 
  /* Walk over each fragment of all of the current segments.  For each
  /* Walk over each fragment of all of the current segments.  For each
     unmarked fragment, mark it with the same info as the previous
     unmarked fragment, mark it with the same info as the previous
     fragment.  */
     fragment.  */
  for (seclist = &stdoutput->sections;
  for (seclist = &stdoutput->sections;
       seclist && *seclist;
       seclist && *seclist;
       seclist = &(*seclist)->next)
       seclist = &(*seclist)->next)
    {
    {
      segT sec = *seclist;
      segT sec = *seclist;
      segment_info_type *seginfo;
      segment_info_type *seginfo;
      fragS *fragP;
      fragS *fragP;
      flagword flags;
      flagword flags;
      flags = bfd_get_section_flags (stdoutput, sec);
      flags = bfd_get_section_flags (stdoutput, sec);
      if (flags & SEC_DEBUGGING)
      if (flags & SEC_DEBUGGING)
        continue;
        continue;
      if (!(flags & SEC_ALLOC))
      if (!(flags & SEC_ALLOC))
        continue;
        continue;
 
 
      seginfo = seg_info (sec);
      seginfo = seg_info (sec);
      if (seginfo && seginfo->frchainP)
      if (seginfo && seginfo->frchainP)
        {
        {
          fragS *last_fragP = 0;
          fragS *last_fragP = 0;
          for (fragP = seginfo->frchainP->frch_root; fragP;
          for (fragP = seginfo->frchainP->frch_root; fragP;
               fragP = fragP->fr_next)
               fragP = fragP->fr_next)
            {
            {
              if (fragP->fr_fix != 0
              if (fragP->fr_fix != 0
                  && !fragP->tc_frag_data.is_assembly_state_set)
                  && !fragP->tc_frag_data.is_assembly_state_set)
                {
                {
                  if (last_fragP == 0)
                  if (last_fragP == 0)
                    {
                    {
                      as_warn_where (fragP->fr_file, fragP->fr_line,
                      as_warn_where (fragP->fr_file, fragP->fr_line,
                                     _("assembly state not set for first frag in section %s"),
                                     _("assembly state not set for first frag in section %s"),
                                     sec->name);
                                     sec->name);
                    }
                    }
                  else
                  else
                    {
                    {
                      fragP->tc_frag_data.is_assembly_state_set = TRUE;
                      fragP->tc_frag_data.is_assembly_state_set = TRUE;
                      fragP->tc_frag_data.is_no_density =
                      fragP->tc_frag_data.is_no_density =
                        last_fragP->tc_frag_data.is_no_density;
                        last_fragP->tc_frag_data.is_no_density;
                      fragP->tc_frag_data.is_no_transform =
                      fragP->tc_frag_data.is_no_transform =
                        last_fragP->tc_frag_data.is_no_transform;
                        last_fragP->tc_frag_data.is_no_transform;
                      fragP->tc_frag_data.use_longcalls =
                      fragP->tc_frag_data.use_longcalls =
                        last_fragP->tc_frag_data.use_longcalls;
                        last_fragP->tc_frag_data.use_longcalls;
                      fragP->tc_frag_data.use_absolute_literals =
                      fragP->tc_frag_data.use_absolute_literals =
                        last_fragP->tc_frag_data.use_absolute_literals;
                        last_fragP->tc_frag_data.use_absolute_literals;
                    }
                    }
                }
                }
              if (fragP->tc_frag_data.is_assembly_state_set)
              if (fragP->tc_frag_data.is_assembly_state_set)
                last_fragP = fragP;
                last_fragP = fragP;
            }
            }
        }
        }
    }
    }
}
}
 
 
 
 
static void
static void
xtensa_find_unaligned_branch_targets (bfd *abfd ATTRIBUTE_UNUSED,
xtensa_find_unaligned_branch_targets (bfd *abfd ATTRIBUTE_UNUSED,
                                      asection *sec,
                                      asection *sec,
                                      void *unused ATTRIBUTE_UNUSED)
                                      void *unused ATTRIBUTE_UNUSED)
{
{
  flagword flags = bfd_get_section_flags (abfd, sec);
  flagword flags = bfd_get_section_flags (abfd, sec);
  segment_info_type *seginfo = seg_info (sec);
  segment_info_type *seginfo = seg_info (sec);
  fragS *frag = seginfo->frchainP->frch_root;
  fragS *frag = seginfo->frchainP->frch_root;
 
 
  if (flags & SEC_CODE)
  if (flags & SEC_CODE)
    {
    {
      xtensa_isa isa = xtensa_default_isa;
      xtensa_isa isa = xtensa_default_isa;
      xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
      xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
      while (frag != NULL)
      while (frag != NULL)
        {
        {
          if (frag->tc_frag_data.is_branch_target)
          if (frag->tc_frag_data.is_branch_target)
            {
            {
              int op_size;
              int op_size;
              addressT branch_align, frag_addr;
              addressT branch_align, frag_addr;
              xtensa_format fmt;
              xtensa_format fmt;
 
 
              xtensa_insnbuf_from_chars
              xtensa_insnbuf_from_chars
                (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
                (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
              fmt = xtensa_format_decode (isa, insnbuf);
              fmt = xtensa_format_decode (isa, insnbuf);
              op_size = xtensa_format_length (isa, fmt);
              op_size = xtensa_format_length (isa, fmt);
              branch_align = 1 << branch_align_power (sec);
              branch_align = 1 << branch_align_power (sec);
              frag_addr = frag->fr_address % branch_align;
              frag_addr = frag->fr_address % branch_align;
              if (frag_addr + op_size > branch_align)
              if (frag_addr + op_size > branch_align)
                as_warn_where (frag->fr_file, frag->fr_line,
                as_warn_where (frag->fr_file, frag->fr_line,
                               _("unaligned branch target: %d bytes at 0x%lx"),
                               _("unaligned branch target: %d bytes at 0x%lx"),
                               op_size, (long) frag->fr_address);
                               op_size, (long) frag->fr_address);
            }
            }
          frag = frag->fr_next;
          frag = frag->fr_next;
        }
        }
      xtensa_insnbuf_free (isa, insnbuf);
      xtensa_insnbuf_free (isa, insnbuf);
    }
    }
}
}
 
 
 
 
static void
static void
xtensa_find_unaligned_loops (bfd *abfd ATTRIBUTE_UNUSED,
xtensa_find_unaligned_loops (bfd *abfd ATTRIBUTE_UNUSED,
                             asection *sec,
                             asection *sec,
                             void *unused ATTRIBUTE_UNUSED)
                             void *unused ATTRIBUTE_UNUSED)
{
{
  flagword flags = bfd_get_section_flags (abfd, sec);
  flagword flags = bfd_get_section_flags (abfd, sec);
  segment_info_type *seginfo = seg_info (sec);
  segment_info_type *seginfo = seg_info (sec);
  fragS *frag = seginfo->frchainP->frch_root;
  fragS *frag = seginfo->frchainP->frch_root;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
 
 
  if (flags & SEC_CODE)
  if (flags & SEC_CODE)
    {
    {
      xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
      xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
      while (frag != NULL)
      while (frag != NULL)
        {
        {
          if (frag->tc_frag_data.is_first_loop_insn)
          if (frag->tc_frag_data.is_first_loop_insn)
            {
            {
              int op_size;
              int op_size;
              addressT frag_addr;
              addressT frag_addr;
              xtensa_format fmt;
              xtensa_format fmt;
 
 
              xtensa_insnbuf_from_chars
              xtensa_insnbuf_from_chars
                (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
                (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
              fmt = xtensa_format_decode (isa, insnbuf);
              fmt = xtensa_format_decode (isa, insnbuf);
              op_size = xtensa_format_length (isa, fmt);
              op_size = xtensa_format_length (isa, fmt);
              frag_addr = frag->fr_address % xtensa_fetch_width;
              frag_addr = frag->fr_address % xtensa_fetch_width;
 
 
              if (frag_addr + op_size > xtensa_fetch_width)
              if (frag_addr + op_size > xtensa_fetch_width)
                as_warn_where (frag->fr_file, frag->fr_line,
                as_warn_where (frag->fr_file, frag->fr_line,
                               _("unaligned loop: %d bytes at 0x%lx"),
                               _("unaligned loop: %d bytes at 0x%lx"),
                               op_size, (long) frag->fr_address);
                               op_size, (long) frag->fr_address);
            }
            }
          frag = frag->fr_next;
          frag = frag->fr_next;
        }
        }
      xtensa_insnbuf_free (isa, insnbuf);
      xtensa_insnbuf_free (isa, insnbuf);
    }
    }
}
}
 
 
 
 
static int
static int
xg_apply_fix_value (fixS *fixP, valueT val)
xg_apply_fix_value (fixS *fixP, valueT val)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  xtensa_format fmt;
  xtensa_format fmt;
  int slot;
  int slot;
  bfd_boolean alt_reloc;
  bfd_boolean alt_reloc;
  xtensa_opcode opcode;
  xtensa_opcode opcode;
  char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
  char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
 
 
  if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc)
  if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc)
      || alt_reloc)
      || alt_reloc)
    as_fatal (_("unexpected fix"));
    as_fatal (_("unexpected fix"));
 
 
  if (!insnbuf)
  if (!insnbuf)
    {
    {
      insnbuf = xtensa_insnbuf_alloc (isa);
      insnbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
    }
    }
 
 
  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
  fmt = xtensa_format_decode (isa, insnbuf);
  fmt = xtensa_format_decode (isa, insnbuf);
  if (fmt == XTENSA_UNDEFINED)
  if (fmt == XTENSA_UNDEFINED)
    as_fatal (_("undecodable fix"));
    as_fatal (_("undecodable fix"));
  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
  opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
  opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
  if (opcode == XTENSA_UNDEFINED)
  if (opcode == XTENSA_UNDEFINED)
    as_fatal (_("undecodable fix"));
    as_fatal (_("undecodable fix"));
 
 
  /* CONST16 immediates are not PC-relative, despite the fact that we
  /* CONST16 immediates are not PC-relative, despite the fact that we
     reuse the normal PC-relative operand relocations for the low part
     reuse the normal PC-relative operand relocations for the low part
     of a CONST16 operand.  */
     of a CONST16 operand.  */
  if (opcode == xtensa_const16_opcode)
  if (opcode == xtensa_const16_opcode)
    return 0;
    return 0;
 
 
  xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode,
  xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode,
                              get_relaxable_immed (opcode), val,
                              get_relaxable_immed (opcode), val,
                              fixP->fx_file, fixP->fx_line);
                              fixP->fx_file, fixP->fx_line);
 
 
  xtensa_format_set_slot (isa, fmt, slot, insnbuf, slotbuf);
  xtensa_format_set_slot (isa, fmt, slot, insnbuf, slotbuf);
  xtensa_insnbuf_to_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
  xtensa_insnbuf_to_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
 
 
  return 1;
  return 1;
}
}
 
 


/* External Functions and Other GAS Hooks.  */
/* External Functions and Other GAS Hooks.  */
 
 
const char *
const char *
xtensa_target_format (void)
xtensa_target_format (void)
{
{
  return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
  return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
}
}
 
 
 
 
void
void
xtensa_file_arch_init (bfd *abfd)
xtensa_file_arch_init (bfd *abfd)
{
{
  bfd_set_private_flags (abfd, 0x100 | 0x200);
  bfd_set_private_flags (abfd, 0x100 | 0x200);
}
}
 
 
 
 
void
void
md_number_to_chars (char *buf, valueT val, int n)
md_number_to_chars (char *buf, valueT val, int n)
{
{
  if (target_big_endian)
  if (target_big_endian)
    number_to_chars_bigendian (buf, val, n);
    number_to_chars_bigendian (buf, val, n);
  else
  else
    number_to_chars_littleendian (buf, val, n);
    number_to_chars_littleendian (buf, val, n);
}
}
 
 
 
 
/* This function is called once, at assembler startup time.  It should
/* 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
   set up all the tables, etc. that the MD part of the assembler will
   need.  */
   need.  */
 
 
void
void
md_begin (void)
md_begin (void)
{
{
  segT current_section = now_seg;
  segT current_section = now_seg;
  int current_subsec = now_subseg;
  int current_subsec = now_subseg;
  xtensa_isa isa;
  xtensa_isa isa;
  int i;
  int i;
 
 
  xtensa_default_isa = xtensa_isa_init (0, 0);
  xtensa_default_isa = xtensa_isa_init (0, 0);
  isa = xtensa_default_isa;
  isa = xtensa_default_isa;
 
 
  linkrelax = 1;
  linkrelax = 1;
 
 
  /* Set up the literal sections.  */
  /* Set up the literal sections.  */
  memset (&default_lit_sections, 0, sizeof (default_lit_sections));
  memset (&default_lit_sections, 0, sizeof (default_lit_sections));
 
 
  subseg_set (current_section, current_subsec);
  subseg_set (current_section, current_subsec);
 
 
  xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
  xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
  xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
  xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
  xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
  xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
  xtensa_call4_opcode = xtensa_opcode_lookup (isa, "call4");
  xtensa_call4_opcode = xtensa_opcode_lookup (isa, "call4");
  xtensa_call8_opcode = xtensa_opcode_lookup (isa, "call8");
  xtensa_call8_opcode = xtensa_opcode_lookup (isa, "call8");
  xtensa_call12_opcode = xtensa_opcode_lookup (isa, "call12");
  xtensa_call12_opcode = xtensa_opcode_lookup (isa, "call12");
  xtensa_callx0_opcode = xtensa_opcode_lookup (isa, "callx0");
  xtensa_callx0_opcode = xtensa_opcode_lookup (isa, "callx0");
  xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4");
  xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4");
  xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8");
  xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8");
  xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
  xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
  xtensa_const16_opcode = xtensa_opcode_lookup (isa, "const16");
  xtensa_const16_opcode = xtensa_opcode_lookup (isa, "const16");
  xtensa_entry_opcode = xtensa_opcode_lookup (isa, "entry");
  xtensa_entry_opcode = xtensa_opcode_lookup (isa, "entry");
  xtensa_extui_opcode = xtensa_opcode_lookup (isa, "extui");
  xtensa_extui_opcode = xtensa_opcode_lookup (isa, "extui");
  xtensa_movi_opcode = xtensa_opcode_lookup (isa, "movi");
  xtensa_movi_opcode = xtensa_opcode_lookup (isa, "movi");
  xtensa_movi_n_opcode = xtensa_opcode_lookup (isa, "movi.n");
  xtensa_movi_n_opcode = xtensa_opcode_lookup (isa, "movi.n");
  xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync");
  xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync");
  xtensa_j_opcode = xtensa_opcode_lookup (isa, "j");
  xtensa_j_opcode = xtensa_opcode_lookup (isa, "j");
  xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx");
  xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx");
  xtensa_l32r_opcode = xtensa_opcode_lookup (isa, "l32r");
  xtensa_l32r_opcode = xtensa_opcode_lookup (isa, "l32r");
  xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop");
  xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop");
  xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez");
  xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez");
  xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz");
  xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz");
  xtensa_nop_opcode = xtensa_opcode_lookup (isa, "nop");
  xtensa_nop_opcode = xtensa_opcode_lookup (isa, "nop");
  xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n");
  xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n");
  xtensa_or_opcode = xtensa_opcode_lookup (isa, "or");
  xtensa_or_opcode = xtensa_opcode_lookup (isa, "or");
  xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret");
  xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret");
  xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n");
  xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n");
  xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw");
  xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw");
  xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n");
  xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n");
  xtensa_rsr_lcount_opcode = xtensa_opcode_lookup (isa, "rsr.lcount");
  xtensa_rsr_lcount_opcode = xtensa_opcode_lookup (isa, "rsr.lcount");
  xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti");
  xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti");
 
 
  for (i = 0; i < xtensa_isa_num_formats (isa); i++)
  for (i = 0; i < xtensa_isa_num_formats (isa); i++)
    {
    {
      int format_slots = xtensa_format_num_slots (isa, i);
      int format_slots = xtensa_format_num_slots (isa, i);
      if (format_slots > config_max_slots)
      if (format_slots > config_max_slots)
        config_max_slots = format_slots;
        config_max_slots = format_slots;
    }
    }
 
 
  xg_init_vinsn (&cur_vinsn);
  xg_init_vinsn (&cur_vinsn);
 
 
  xtensa_num_pipe_stages = xtensa_isa_num_pipe_stages (isa);
  xtensa_num_pipe_stages = xtensa_isa_num_pipe_stages (isa);
 
 
  init_op_placement_info_table ();
  init_op_placement_info_table ();
 
 
  /* Set up the assembly state.  */
  /* Set up the assembly state.  */
  if (!frag_now->tc_frag_data.is_assembly_state_set)
  if (!frag_now->tc_frag_data.is_assembly_state_set)
    xtensa_set_frag_assembly_state (frag_now);
    xtensa_set_frag_assembly_state (frag_now);
}
}
 
 
 
 
/* TC_INIT_FIX_DATA hook */
/* TC_INIT_FIX_DATA hook */
 
 
void
void
xtensa_init_fix_data (fixS *x)
xtensa_init_fix_data (fixS *x)
{
{
  x->tc_fix_data.slot = 0;
  x->tc_fix_data.slot = 0;
  x->tc_fix_data.X_add_symbol = NULL;
  x->tc_fix_data.X_add_symbol = NULL;
  x->tc_fix_data.X_add_number = 0;
  x->tc_fix_data.X_add_number = 0;
}
}
 
 
 
 
/* tc_frob_label hook */
/* tc_frob_label hook */
 
 
void
void
xtensa_frob_label (symbolS *sym)
xtensa_frob_label (symbolS *sym)
{
{
  float freq;
  float freq;
 
 
  if (cur_vinsn.inside_bundle)
  if (cur_vinsn.inside_bundle)
    {
    {
      as_bad (_("labels are not valid inside bundles"));
      as_bad (_("labels are not valid inside bundles"));
      return;
      return;
    }
    }
 
 
  freq = get_subseg_target_freq (now_seg, now_subseg);
  freq = get_subseg_target_freq (now_seg, now_subseg);
 
 
  /* Since the label was already attached to a frag associated with the
  /* Since the label was already attached to a frag associated with the
     previous basic block, it now needs to be reset to the current frag.  */
     previous basic block, it now needs to be reset to the current frag.  */
  symbol_set_frag (sym, frag_now);
  symbol_set_frag (sym, frag_now);
  S_SET_VALUE (sym, (valueT) frag_now_fix ());
  S_SET_VALUE (sym, (valueT) frag_now_fix ());
 
 
  if (generating_literals)
  if (generating_literals)
    xtensa_add_literal_sym (sym);
    xtensa_add_literal_sym (sym);
  else
  else
    xtensa_add_insn_label (sym);
    xtensa_add_insn_label (sym);
 
 
  if (symbol_get_tc (sym)->is_loop_target)
  if (symbol_get_tc (sym)->is_loop_target)
    {
    {
      if ((get_last_insn_flags (now_seg, now_subseg)
      if ((get_last_insn_flags (now_seg, now_subseg)
          & FLAG_IS_BAD_LOOPEND) != 0)
          & FLAG_IS_BAD_LOOPEND) != 0)
        as_bad (_("invalid last instruction for a zero-overhead loop"));
        as_bad (_("invalid last instruction for a zero-overhead loop"));
 
 
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      frag_var (rs_machine_dependent, 4, 4, RELAX_LOOP_END,
      frag_var (rs_machine_dependent, 4, 4, RELAX_LOOP_END,
                frag_now->fr_symbol, frag_now->fr_offset, NULL);
                frag_now->fr_symbol, frag_now->fr_offset, NULL);
 
 
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_move_labels (frag_now, 0);
      xtensa_move_labels (frag_now, 0);
    }
    }
 
 
  /* No target aligning in the absolute section.  */
  /* No target aligning in the absolute section.  */
  if (now_seg != absolute_section
  if (now_seg != absolute_section
      && !is_unaligned_label (sym)
      && !is_unaligned_label (sym)
      && !generating_literals)
      && !generating_literals)
    {
    {
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
 
 
      if (do_align_targets ())
      if (do_align_targets ())
        frag_var (rs_machine_dependent, 0, (int) freq,
        frag_var (rs_machine_dependent, 0, (int) freq,
                  RELAX_DESIRE_ALIGN_IF_TARGET, frag_now->fr_symbol,
                  RELAX_DESIRE_ALIGN_IF_TARGET, frag_now->fr_symbol,
                  frag_now->fr_offset, NULL);
                  frag_now->fr_offset, NULL);
      else
      else
        frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
        frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
                  frag_now->fr_symbol, frag_now->fr_offset, NULL);
                  frag_now->fr_symbol, frag_now->fr_offset, NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_move_labels (frag_now, 0);
      xtensa_move_labels (frag_now, 0);
    }
    }
 
 
  /* We need to mark the following properties even if we aren't aligning.  */
  /* We need to mark the following properties even if we aren't aligning.  */
 
 
  /* If the label is already known to be a branch target, i.e., a
  /* If the label is already known to be a branch target, i.e., a
     forward branch, mark the frag accordingly.  Backward branches
     forward branch, mark the frag accordingly.  Backward branches
     are handled by xg_add_branch_and_loop_targets.  */
     are handled by xg_add_branch_and_loop_targets.  */
  if (symbol_get_tc (sym)->is_branch_target)
  if (symbol_get_tc (sym)->is_branch_target)
    symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
    symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
 
 
  /* Loops only go forward, so they can be identified here.  */
  /* Loops only go forward, so they can be identified here.  */
  if (symbol_get_tc (sym)->is_loop_target)
  if (symbol_get_tc (sym)->is_loop_target)
    symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
    symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
 
 
  dwarf2_emit_label (sym);
  dwarf2_emit_label (sym);
}
}
 
 
 
 
/* tc_unrecognized_line hook */
/* tc_unrecognized_line hook */
 
 
int
int
xtensa_unrecognized_line (int ch)
xtensa_unrecognized_line (int ch)
{
{
  switch (ch)
  switch (ch)
    {
    {
    case '{' :
    case '{' :
      if (cur_vinsn.inside_bundle == 0)
      if (cur_vinsn.inside_bundle == 0)
        {
        {
          /* PR8110: Cannot emit line number info inside a FLIX bundle
          /* PR8110: Cannot emit line number info inside a FLIX bundle
             when using --gstabs.  Temporarily disable debug info.  */
             when using --gstabs.  Temporarily disable debug info.  */
          generate_lineno_debug ();
          generate_lineno_debug ();
          if (debug_type == DEBUG_STABS)
          if (debug_type == DEBUG_STABS)
            {
            {
              xt_saved_debug_type = debug_type;
              xt_saved_debug_type = debug_type;
              debug_type = DEBUG_NONE;
              debug_type = DEBUG_NONE;
            }
            }
 
 
          cur_vinsn.inside_bundle = 1;
          cur_vinsn.inside_bundle = 1;
        }
        }
      else
      else
        {
        {
          as_bad (_("extra opening brace"));
          as_bad (_("extra opening brace"));
          return 0;
          return 0;
        }
        }
      break;
      break;
 
 
    case '}' :
    case '}' :
      if (cur_vinsn.inside_bundle)
      if (cur_vinsn.inside_bundle)
        finish_vinsn (&cur_vinsn);
        finish_vinsn (&cur_vinsn);
      else
      else
        {
        {
          as_bad (_("extra closing brace"));
          as_bad (_("extra closing brace"));
          return 0;
          return 0;
        }
        }
      break;
      break;
    default:
    default:
      as_bad (_("syntax error"));
      as_bad (_("syntax error"));
      return 0;
      return 0;
    }
    }
  return 1;
  return 1;
}
}
 
 
 
 
/* md_flush_pending_output hook */
/* md_flush_pending_output hook */
 
 
void
void
xtensa_flush_pending_output (void)
xtensa_flush_pending_output (void)
{
{
  /* This line fixes a bug where automatically generated gstabs info
  /* This line fixes a bug where automatically generated gstabs info
     separates a function label from its entry instruction, ending up
     separates a function label from its entry instruction, ending up
     with the literal position between the function label and the entry
     with the literal position between the function label and the entry
     instruction and crashing code.  It only happens with --gstabs and
     instruction and crashing code.  It only happens with --gstabs and
     --text-section-literals, and when several other obscure relaxation
     --text-section-literals, and when several other obscure relaxation
     conditions are met.  */
     conditions are met.  */
  if (outputting_stabs_line_debug)
  if (outputting_stabs_line_debug)
    return;
    return;
 
 
  if (cur_vinsn.inside_bundle)
  if (cur_vinsn.inside_bundle)
    as_bad (_("missing closing brace"));
    as_bad (_("missing closing brace"));
 
 
  /* If there is a non-zero instruction fragment, close it.  */
  /* If there is a non-zero instruction fragment, close it.  */
  if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn)
  if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn)
    {
    {
      frag_wane (frag_now);
      frag_wane (frag_now);
      frag_new (0);
      frag_new (0);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
    }
    }
  frag_now->tc_frag_data.is_insn = FALSE;
  frag_now->tc_frag_data.is_insn = FALSE;
 
 
  xtensa_clear_insn_labels ();
  xtensa_clear_insn_labels ();
}
}
 
 
 
 
/* We had an error while parsing an instruction.  The string might look
/* We had an error while parsing an instruction.  The string might look
   like this: "insn arg1, arg2 }".  If so, we need to see the closing
   like this: "insn arg1, arg2 }".  If so, we need to see the closing
   brace and reset some fields.  Otherwise, the vinsn never gets closed
   brace and reset some fields.  Otherwise, the vinsn never gets closed
   and the num_slots field will grow past the end of the array of slots,
   and the num_slots field will grow past the end of the array of slots,
   and bad things happen.  */
   and bad things happen.  */
 
 
static void
static void
error_reset_cur_vinsn (void)
error_reset_cur_vinsn (void)
{
{
  if (cur_vinsn.inside_bundle)
  if (cur_vinsn.inside_bundle)
    {
    {
      if (*input_line_pointer == '}'
      if (*input_line_pointer == '}'
          || *(input_line_pointer - 1) == '}'
          || *(input_line_pointer - 1) == '}'
          || *(input_line_pointer - 2) == '}')
          || *(input_line_pointer - 2) == '}')
        xg_clear_vinsn (&cur_vinsn);
        xg_clear_vinsn (&cur_vinsn);
    }
    }
}
}
 
 
 
 
void
void
md_assemble (char *str)
md_assemble (char *str)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  char *opname;
  char *opname;
  unsigned opnamelen;
  unsigned opnamelen;
  bfd_boolean has_underbar = FALSE;
  bfd_boolean has_underbar = FALSE;
  char *arg_strings[MAX_INSN_ARGS];
  char *arg_strings[MAX_INSN_ARGS];
  int num_args;
  int num_args;
  TInsn orig_insn;              /* Original instruction from the input.  */
  TInsn orig_insn;              /* Original instruction from the input.  */
 
 
  tinsn_init (&orig_insn);
  tinsn_init (&orig_insn);
 
 
  /* Split off the opcode.  */
  /* Split off the opcode.  */
  opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/0123456789.");
  opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/0123456789.");
  opname = xmalloc (opnamelen + 1);
  opname = xmalloc (opnamelen + 1);
  memcpy (opname, str, opnamelen);
  memcpy (opname, str, opnamelen);
  opname[opnamelen] = '\0';
  opname[opnamelen] = '\0';
 
 
  num_args = tokenize_arguments (arg_strings, str + opnamelen);
  num_args = tokenize_arguments (arg_strings, str + opnamelen);
  if (num_args == -1)
  if (num_args == -1)
    {
    {
      as_bad (_("syntax error"));
      as_bad (_("syntax error"));
      return;
      return;
    }
    }
 
 
  if (xg_translate_idioms (&opname, &num_args, arg_strings))
  if (xg_translate_idioms (&opname, &num_args, arg_strings))
    return;
    return;
 
 
  /* Check for an underbar prefix.  */
  /* Check for an underbar prefix.  */
  if (*opname == '_')
  if (*opname == '_')
    {
    {
      has_underbar = TRUE;
      has_underbar = TRUE;
      opname += 1;
      opname += 1;
    }
    }
 
 
  orig_insn.insn_type = ITYPE_INSN;
  orig_insn.insn_type = ITYPE_INSN;
  orig_insn.ntok = 0;
  orig_insn.ntok = 0;
  orig_insn.is_specific_opcode = (has_underbar || !use_transform ());
  orig_insn.is_specific_opcode = (has_underbar || !use_transform ());
  orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
  orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
 
 
  /* Special case: Check for "CALLXn.TLS" psuedo op.  If found, grab its
  /* Special case: Check for "CALLXn.TLS" psuedo op.  If found, grab its
     extra argument and set the opcode to "CALLXn".  */
     extra argument and set the opcode to "CALLXn".  */
  if (orig_insn.opcode == XTENSA_UNDEFINED
  if (orig_insn.opcode == XTENSA_UNDEFINED
      && strncasecmp (opname, "callx", 5) == 0)
      && strncasecmp (opname, "callx", 5) == 0)
    {
    {
      unsigned long window_size;
      unsigned long window_size;
      char *suffix;
      char *suffix;
 
 
      window_size = strtoul (opname + 5, &suffix, 10);
      window_size = strtoul (opname + 5, &suffix, 10);
      if (suffix != opname + 5
      if (suffix != opname + 5
          && (window_size == 0
          && (window_size == 0
              || window_size == 4
              || window_size == 4
              || window_size == 8
              || window_size == 8
              || window_size == 12)
              || window_size == 12)
          && strcasecmp (suffix, ".tls") == 0)
          && strcasecmp (suffix, ".tls") == 0)
        {
        {
          switch (window_size)
          switch (window_size)
            {
            {
            case 0: orig_insn.opcode = xtensa_callx0_opcode; break;
            case 0: orig_insn.opcode = xtensa_callx0_opcode; break;
            case 4: orig_insn.opcode = xtensa_callx4_opcode; break;
            case 4: orig_insn.opcode = xtensa_callx4_opcode; break;
            case 8: orig_insn.opcode = xtensa_callx8_opcode; break;
            case 8: orig_insn.opcode = xtensa_callx8_opcode; break;
            case 12: orig_insn.opcode = xtensa_callx12_opcode; break;
            case 12: orig_insn.opcode = xtensa_callx12_opcode; break;
            }
            }
 
 
          if (num_args != 2)
          if (num_args != 2)
            as_bad (_("wrong number of operands for '%s'"), opname);
            as_bad (_("wrong number of operands for '%s'"), opname);
          else
          else
            {
            {
              bfd_reloc_code_real_type reloc;
              bfd_reloc_code_real_type reloc;
              char *old_input_line_pointer;
              char *old_input_line_pointer;
              expressionS *tok = &orig_insn.extra_arg;
              expressionS *tok = &orig_insn.extra_arg;
              segT t;
              segT t;
 
 
              old_input_line_pointer = input_line_pointer;
              old_input_line_pointer = input_line_pointer;
              input_line_pointer = arg_strings[num_args - 1];
              input_line_pointer = arg_strings[num_args - 1];
 
 
              t = expression (tok);
              t = expression (tok);
              if (tok->X_op == O_symbol
              if (tok->X_op == O_symbol
                  && ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
                  && ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
                      == BFD_RELOC_XTENSA_TLS_CALL))
                      == BFD_RELOC_XTENSA_TLS_CALL))
                tok->X_op = map_suffix_reloc_to_operator (reloc);
                tok->X_op = map_suffix_reloc_to_operator (reloc);
              else
              else
                as_bad (_("bad relocation expression for '%s'"), opname);
                as_bad (_("bad relocation expression for '%s'"), opname);
 
 
              input_line_pointer = old_input_line_pointer;
              input_line_pointer = old_input_line_pointer;
              num_args -= 1;
              num_args -= 1;
            }
            }
        }
        }
    }
    }
 
 
  /* Special case: Check for "j.l" psuedo op.  */
  /* Special case: Check for "j.l" psuedo op.  */
  if (orig_insn.opcode == XTENSA_UNDEFINED
  if (orig_insn.opcode == XTENSA_UNDEFINED
      && strncasecmp (opname, "j.l", 3) == 0)
      && strncasecmp (opname, "j.l", 3) == 0)
    {
    {
      if (num_args != 2)
      if (num_args != 2)
        as_bad (_("wrong number of operands for '%s'"), opname);
        as_bad (_("wrong number of operands for '%s'"), opname);
      else
      else
        {
        {
          char *old_input_line_pointer;
          char *old_input_line_pointer;
          expressionS *tok = &orig_insn.extra_arg;
          expressionS *tok = &orig_insn.extra_arg;
 
 
          old_input_line_pointer = input_line_pointer;
          old_input_line_pointer = input_line_pointer;
          input_line_pointer = arg_strings[num_args - 1];
          input_line_pointer = arg_strings[num_args - 1];
 
 
          expression_maybe_register (xtensa_jx_opcode, 0, tok);
          expression_maybe_register (xtensa_jx_opcode, 0, tok);
          input_line_pointer = old_input_line_pointer;
          input_line_pointer = old_input_line_pointer;
 
 
          num_args -= 1;
          num_args -= 1;
          orig_insn.opcode = xtensa_j_opcode;
          orig_insn.opcode = xtensa_j_opcode;
        }
        }
    }
    }
 
 
  if (orig_insn.opcode == XTENSA_UNDEFINED)
  if (orig_insn.opcode == XTENSA_UNDEFINED)
    {
    {
      xtensa_format fmt = xtensa_format_lookup (isa, opname);
      xtensa_format fmt = xtensa_format_lookup (isa, opname);
      if (fmt == XTENSA_UNDEFINED)
      if (fmt == XTENSA_UNDEFINED)
        {
        {
          as_bad (_("unknown opcode or format name '%s'"), opname);
          as_bad (_("unknown opcode or format name '%s'"), opname);
          error_reset_cur_vinsn ();
          error_reset_cur_vinsn ();
          return;
          return;
        }
        }
      if (!cur_vinsn.inside_bundle)
      if (!cur_vinsn.inside_bundle)
        {
        {
          as_bad (_("format names only valid inside bundles"));
          as_bad (_("format names only valid inside bundles"));
          error_reset_cur_vinsn ();
          error_reset_cur_vinsn ();
          return;
          return;
        }
        }
      if (cur_vinsn.format != XTENSA_UNDEFINED)
      if (cur_vinsn.format != XTENSA_UNDEFINED)
        as_warn (_("multiple formats specified for one bundle; using '%s'"),
        as_warn (_("multiple formats specified for one bundle; using '%s'"),
                 opname);
                 opname);
      cur_vinsn.format = fmt;
      cur_vinsn.format = fmt;
      free (has_underbar ? opname - 1 : opname);
      free (has_underbar ? opname - 1 : opname);
      error_reset_cur_vinsn ();
      error_reset_cur_vinsn ();
      return;
      return;
    }
    }
 
 
  /* Parse the arguments.  */
  /* Parse the arguments.  */
  if (parse_arguments (&orig_insn, num_args, arg_strings))
  if (parse_arguments (&orig_insn, num_args, arg_strings))
    {
    {
      as_bad (_("syntax error"));
      as_bad (_("syntax error"));
      error_reset_cur_vinsn ();
      error_reset_cur_vinsn ();
      return;
      return;
    }
    }
 
 
  /* Free the opcode and argument strings, now that they've been parsed.  */
  /* Free the opcode and argument strings, now that they've been parsed.  */
  free (has_underbar ? opname - 1 : opname);
  free (has_underbar ? opname - 1 : opname);
  opname = 0;
  opname = 0;
  while (num_args-- > 0)
  while (num_args-- > 0)
    free (arg_strings[num_args]);
    free (arg_strings[num_args]);
 
 
  /* Get expressions for invisible operands.  */
  /* Get expressions for invisible operands.  */
  if (get_invisible_operands (&orig_insn))
  if (get_invisible_operands (&orig_insn))
    {
    {
      error_reset_cur_vinsn ();
      error_reset_cur_vinsn ();
      return;
      return;
    }
    }
 
 
  /* Check for the right number and type of arguments.  */
  /* Check for the right number and type of arguments.  */
  if (tinsn_check_arguments (&orig_insn))
  if (tinsn_check_arguments (&orig_insn))
    {
    {
      error_reset_cur_vinsn ();
      error_reset_cur_vinsn ();
      return;
      return;
    }
    }
 
 
  /* Record the line number for each TInsn, because a FLIX bundle may be
  /* Record the line number for each TInsn, because a FLIX bundle may be
     spread across multiple input lines and individual instructions may be
     spread across multiple input lines and individual instructions may be
     moved around in some cases.  */
     moved around in some cases.  */
  orig_insn.loc_directive_seen = dwarf2_loc_directive_seen;
  orig_insn.loc_directive_seen = dwarf2_loc_directive_seen;
  dwarf2_where (&orig_insn.debug_line);
  dwarf2_where (&orig_insn.debug_line);
  dwarf2_consume_line_info ();
  dwarf2_consume_line_info ();
 
 
  xg_add_branch_and_loop_targets (&orig_insn);
  xg_add_branch_and_loop_targets (&orig_insn);
 
 
  /* Check that immediate value for ENTRY is >= 16.  */
  /* Check that immediate value for ENTRY is >= 16.  */
  if (orig_insn.opcode == xtensa_entry_opcode && orig_insn.ntok >= 3)
  if (orig_insn.opcode == xtensa_entry_opcode && orig_insn.ntok >= 3)
    {
    {
      expressionS *exp = &orig_insn.tok[2];
      expressionS *exp = &orig_insn.tok[2];
      if (exp->X_op == O_constant && exp->X_add_number < 16)
      if (exp->X_op == O_constant && exp->X_add_number < 16)
        as_warn (_("entry instruction with stack decrement < 16"));
        as_warn (_("entry instruction with stack decrement < 16"));
    }
    }
 
 
  /* Finish it off:
  /* Finish it off:
     assemble_tokens (opcode, tok, ntok);
     assemble_tokens (opcode, tok, ntok);
     expand the tokens from the orig_insn into the
     expand the tokens from the orig_insn into the
     stack of instructions that will not expand
     stack of instructions that will not expand
     unless required at relaxation time.  */
     unless required at relaxation time.  */
 
 
  if (!cur_vinsn.inside_bundle)
  if (!cur_vinsn.inside_bundle)
    emit_single_op (&orig_insn);
    emit_single_op (&orig_insn);
  else /* We are inside a bundle.  */
  else /* We are inside a bundle.  */
    {
    {
      cur_vinsn.slots[cur_vinsn.num_slots] = orig_insn;
      cur_vinsn.slots[cur_vinsn.num_slots] = orig_insn;
      cur_vinsn.num_slots++;
      cur_vinsn.num_slots++;
      if (*input_line_pointer == '}'
      if (*input_line_pointer == '}'
          || *(input_line_pointer - 1) == '}'
          || *(input_line_pointer - 1) == '}'
          || *(input_line_pointer - 2) == '}')
          || *(input_line_pointer - 2) == '}')
        finish_vinsn (&cur_vinsn);
        finish_vinsn (&cur_vinsn);
    }
    }
 
 
  /* We've just emitted a new instruction so clear the list of labels.  */
  /* We've just emitted a new instruction so clear the list of labels.  */
  xtensa_clear_insn_labels ();
  xtensa_clear_insn_labels ();
}
}
 
 
 
 
/* HANDLE_ALIGN hook */
/* HANDLE_ALIGN hook */
 
 
/* For a .align directive, we mark the previous block with the alignment
/* For a .align directive, we mark the previous block with the alignment
   information.  This will be placed in the object file in the
   information.  This will be placed in the object file in the
   property section corresponding to this section.  */
   property section corresponding to this section.  */
 
 
void
void
xtensa_handle_align (fragS *fragP)
xtensa_handle_align (fragS *fragP)
{
{
  if (linkrelax
  if (linkrelax
      && ! fragP->tc_frag_data.is_literal
      && ! fragP->tc_frag_data.is_literal
      && (fragP->fr_type == rs_align
      && (fragP->fr_type == rs_align
          || fragP->fr_type == rs_align_code)
          || fragP->fr_type == rs_align_code)
      && fragP->fr_address + fragP->fr_fix > 0
      && fragP->fr_address + fragP->fr_fix > 0
      && fragP->fr_offset > 0
      && fragP->fr_offset > 0
      && now_seg != bss_section)
      && now_seg != bss_section)
    {
    {
      fragP->tc_frag_data.is_align = TRUE;
      fragP->tc_frag_data.is_align = TRUE;
      fragP->tc_frag_data.alignment = fragP->fr_offset;
      fragP->tc_frag_data.alignment = fragP->fr_offset;
    }
    }
 
 
  if (fragP->fr_type == rs_align_test)
  if (fragP->fr_type == rs_align_test)
    {
    {
      int count;
      int count;
      count = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
      count = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
      if (count != 0)
      if (count != 0)
        as_bad_where (fragP->fr_file, fragP->fr_line,
        as_bad_where (fragP->fr_file, fragP->fr_line,
                      _("unaligned entry instruction"));
                      _("unaligned entry instruction"));
    }
    }
 
 
  if (linkrelax && fragP->fr_type == rs_org)
  if (linkrelax && fragP->fr_type == rs_org)
    fragP->fr_subtype = RELAX_ORG;
    fragP->fr_subtype = RELAX_ORG;
}
}
 
 
 
 
/* TC_FRAG_INIT hook */
/* TC_FRAG_INIT hook */
 
 
void
void
xtensa_frag_init (fragS *frag)
xtensa_frag_init (fragS *frag)
{
{
  xtensa_set_frag_assembly_state (frag);
  xtensa_set_frag_assembly_state (frag);
}
}
 
 
 
 
symbolS *
symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
{
  return NULL;
  return NULL;
}
}
 
 
 
 
/* Round up a section size to the appropriate boundary.  */
/* Round up a section size to the appropriate boundary.  */
 
 
valueT
valueT
md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
{
{
  return size;                  /* Byte alignment is fine.  */
  return size;                  /* Byte alignment is fine.  */
}
}
 
 
 
 
long
long
md_pcrel_from (fixS *fixP)
md_pcrel_from (fixS *fixP)
{
{
  char *insn_p;
  char *insn_p;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  int opnum;
  int opnum;
  uint32 opnd_value;
  uint32 opnd_value;
  xtensa_opcode opcode;
  xtensa_opcode opcode;
  xtensa_format fmt;
  xtensa_format fmt;
  int slot;
  int slot;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
  valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
  bfd_boolean alt_reloc;
  bfd_boolean alt_reloc;
 
 
  if (fixP->fx_r_type == BFD_RELOC_XTENSA_ASM_EXPAND)
  if (fixP->fx_r_type == BFD_RELOC_XTENSA_ASM_EXPAND)
    return 0;
    return 0;
 
 
  if (fixP->fx_r_type == BFD_RELOC_32_PCREL)
  if (fixP->fx_r_type == BFD_RELOC_32_PCREL)
    return addr;
    return addr;
 
 
  if (!insnbuf)
  if (!insnbuf)
    {
    {
      insnbuf = xtensa_insnbuf_alloc (isa);
      insnbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
    }
    }
 
 
  insn_p = &fixP->fx_frag->fr_literal[fixP->fx_where];
  insn_p = &fixP->fx_frag->fr_literal[fixP->fx_where];
  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) insn_p, 0);
  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) insn_p, 0);
  fmt = xtensa_format_decode (isa, insnbuf);
  fmt = xtensa_format_decode (isa, insnbuf);
 
 
  if (fmt == XTENSA_UNDEFINED)
  if (fmt == XTENSA_UNDEFINED)
    as_fatal (_("bad instruction format"));
    as_fatal (_("bad instruction format"));
 
 
  if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc) != 0)
  if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc) != 0)
    as_fatal (_("invalid relocation"));
    as_fatal (_("invalid relocation"));
 
 
  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
  opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
  opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
 
 
  /* Check for "alternate" relocations (operand not specified).  None
  /* Check for "alternate" relocations (operand not specified).  None
     of the current uses for these are really PC-relative.  */
     of the current uses for these are really PC-relative.  */
  if (alt_reloc || opcode == xtensa_const16_opcode)
  if (alt_reloc || opcode == xtensa_const16_opcode)
    {
    {
      if (opcode != xtensa_l32r_opcode
      if (opcode != xtensa_l32r_opcode
          && opcode != xtensa_const16_opcode)
          && opcode != xtensa_const16_opcode)
        as_fatal (_("invalid relocation for '%s' instruction"),
        as_fatal (_("invalid relocation for '%s' instruction"),
                  xtensa_opcode_name (isa, opcode));
                  xtensa_opcode_name (isa, opcode));
      return 0;
      return 0;
    }
    }
 
 
  opnum = get_relaxable_immed (opcode);
  opnum = get_relaxable_immed (opcode);
  opnd_value = 0;
  opnd_value = 0;
  if (xtensa_operand_is_PCrelative (isa, opcode, opnum) != 1
  if (xtensa_operand_is_PCrelative (isa, opcode, opnum) != 1
      || xtensa_operand_do_reloc (isa, opcode, opnum, &opnd_value, addr))
      || xtensa_operand_do_reloc (isa, opcode, opnum, &opnd_value, addr))
    {
    {
      as_bad_where (fixP->fx_file,
      as_bad_where (fixP->fx_file,
                    fixP->fx_line,
                    fixP->fx_line,
                    _("invalid relocation for operand %d of '%s'"),
                    _("invalid relocation for operand %d of '%s'"),
                    opnum, xtensa_opcode_name (isa, opcode));
                    opnum, xtensa_opcode_name (isa, opcode));
      return 0;
      return 0;
    }
    }
  return 0 - opnd_value;
  return 0 - opnd_value;
}
}
 
 
 
 
/* TC_FORCE_RELOCATION hook */
/* TC_FORCE_RELOCATION hook */
 
 
int
int
xtensa_force_relocation (fixS *fix)
xtensa_force_relocation (fixS *fix)
{
{
  switch (fix->fx_r_type)
  switch (fix->fx_r_type)
    {
    {
    case BFD_RELOC_XTENSA_ASM_EXPAND:
    case BFD_RELOC_XTENSA_ASM_EXPAND:
    case BFD_RELOC_XTENSA_SLOT0_ALT:
    case BFD_RELOC_XTENSA_SLOT0_ALT:
    case BFD_RELOC_XTENSA_SLOT1_ALT:
    case BFD_RELOC_XTENSA_SLOT1_ALT:
    case BFD_RELOC_XTENSA_SLOT2_ALT:
    case BFD_RELOC_XTENSA_SLOT2_ALT:
    case BFD_RELOC_XTENSA_SLOT3_ALT:
    case BFD_RELOC_XTENSA_SLOT3_ALT:
    case BFD_RELOC_XTENSA_SLOT4_ALT:
    case BFD_RELOC_XTENSA_SLOT4_ALT:
    case BFD_RELOC_XTENSA_SLOT5_ALT:
    case BFD_RELOC_XTENSA_SLOT5_ALT:
    case BFD_RELOC_XTENSA_SLOT6_ALT:
    case BFD_RELOC_XTENSA_SLOT6_ALT:
    case BFD_RELOC_XTENSA_SLOT7_ALT:
    case BFD_RELOC_XTENSA_SLOT7_ALT:
    case BFD_RELOC_XTENSA_SLOT8_ALT:
    case BFD_RELOC_XTENSA_SLOT8_ALT:
    case BFD_RELOC_XTENSA_SLOT9_ALT:
    case BFD_RELOC_XTENSA_SLOT9_ALT:
    case BFD_RELOC_XTENSA_SLOT10_ALT:
    case BFD_RELOC_XTENSA_SLOT10_ALT:
    case BFD_RELOC_XTENSA_SLOT11_ALT:
    case BFD_RELOC_XTENSA_SLOT11_ALT:
    case BFD_RELOC_XTENSA_SLOT12_ALT:
    case BFD_RELOC_XTENSA_SLOT12_ALT:
    case BFD_RELOC_XTENSA_SLOT13_ALT:
    case BFD_RELOC_XTENSA_SLOT13_ALT:
    case BFD_RELOC_XTENSA_SLOT14_ALT:
    case BFD_RELOC_XTENSA_SLOT14_ALT:
      return 1;
      return 1;
    default:
    default:
      break;
      break;
    }
    }
 
 
  if (linkrelax && fix->fx_addsy
  if (linkrelax && fix->fx_addsy
      && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
      && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
    return 1;
    return 1;
 
 
  return generic_force_reloc (fix);
  return generic_force_reloc (fix);
}
}
 
 
 
 
/* TC_VALIDATE_FIX_SUB hook */
/* TC_VALIDATE_FIX_SUB hook */
 
 
int
int
xtensa_validate_fix_sub (fixS *fix)
xtensa_validate_fix_sub (fixS *fix)
{
{
  segT add_symbol_segment, sub_symbol_segment;
  segT add_symbol_segment, sub_symbol_segment;
 
 
  /* The difference of two symbols should be resolved by the assembler when
  /* The difference of two symbols should be resolved by the assembler when
     linkrelax is not set.  If the linker may relax the section containing
     linkrelax is not set.  If the linker may relax the section containing
     the symbols, then an Xtensa DIFF relocation must be generated so that
     the symbols, then an Xtensa DIFF relocation must be generated so that
     the linker knows to adjust the difference value.  */
     the linker knows to adjust the difference value.  */
  if (!linkrelax || fix->fx_addsy == NULL)
  if (!linkrelax || fix->fx_addsy == NULL)
    return 0;
    return 0;
 
 
  /* Make sure both symbols are in the same segment, and that segment is
  /* Make sure both symbols are in the same segment, and that segment is
     "normal" and relaxable.  If the segment is not "normal", then the
     "normal" and relaxable.  If the segment is not "normal", then the
     fix is not valid.  If the segment is not "relaxable", then the fix
     fix is not valid.  If the segment is not "relaxable", then the fix
     should have been handled earlier.  */
     should have been handled earlier.  */
  add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
  add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
  if (! SEG_NORMAL (add_symbol_segment) ||
  if (! SEG_NORMAL (add_symbol_segment) ||
      ! relaxable_section (add_symbol_segment))
      ! relaxable_section (add_symbol_segment))
    return 0;
    return 0;
  sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
  sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
  return (sub_symbol_segment == add_symbol_segment);
  return (sub_symbol_segment == add_symbol_segment);
}
}
 
 
 
 
/* NO_PSEUDO_DOT hook */
/* NO_PSEUDO_DOT hook */
 
 
/* This function has nothing to do with pseudo dots, but this is the
/* This function has nothing to do with pseudo dots, but this is the
   nearest macro to where the check needs to take place.  FIXME: This
   nearest macro to where the check needs to take place.  FIXME: This
   seems wrong.  */
   seems wrong.  */
 
 
bfd_boolean
bfd_boolean
xtensa_check_inside_bundle (void)
xtensa_check_inside_bundle (void)
{
{
  if (cur_vinsn.inside_bundle && input_line_pointer[-1] == '.')
  if (cur_vinsn.inside_bundle && input_line_pointer[-1] == '.')
    as_bad (_("directives are not valid inside bundles"));
    as_bad (_("directives are not valid inside bundles"));
 
 
  /* This function must always return FALSE because it is called via a
  /* This function must always return FALSE because it is called via a
     macro that has nothing to do with bundling.  */
     macro that has nothing to do with bundling.  */
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* md_elf_section_change_hook */
/* md_elf_section_change_hook */
 
 
void
void
xtensa_elf_section_change_hook (void)
xtensa_elf_section_change_hook (void)
{
{
  /* Set up the assembly state.  */
  /* Set up the assembly state.  */
  if (!frag_now->tc_frag_data.is_assembly_state_set)
  if (!frag_now->tc_frag_data.is_assembly_state_set)
    xtensa_set_frag_assembly_state (frag_now);
    xtensa_set_frag_assembly_state (frag_now);
}
}
 
 
 
 
/* tc_fix_adjustable hook */
/* tc_fix_adjustable hook */
 
 
bfd_boolean
bfd_boolean
xtensa_fix_adjustable (fixS *fixP)
xtensa_fix_adjustable (fixS *fixP)
{
{
  /* An offset is not allowed in combination with the difference of two
  /* An offset is not allowed in combination with the difference of two
     symbols, but that cannot be easily detected after a local symbol
     symbols, but that cannot be easily detected after a local symbol
     has been adjusted to a (section+offset) form.  Return 0 so that such
     has been adjusted to a (section+offset) form.  Return 0 so that such
     an fix will not be adjusted.  */
     an fix will not be adjusted.  */
  if (fixP->fx_subsy && fixP->fx_addsy && fixP->fx_offset
  if (fixP->fx_subsy && fixP->fx_addsy && fixP->fx_offset
      && relaxable_section (S_GET_SEGMENT (fixP->fx_subsy)))
      && relaxable_section (S_GET_SEGMENT (fixP->fx_subsy)))
    return 0;
    return 0;
 
 
  /* We need the symbol name for the VTABLE entries.  */
  /* We need the symbol name for the VTABLE entries.  */
  if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
  if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
    return 0;
    return 0;
 
 
  return 1;
  return 1;
}
}
 
 
 
 
/* tc_symbol_new_hook */
/* tc_symbol_new_hook */
 
 
symbolS *expr_symbols = NULL;
symbolS *expr_symbols = NULL;
 
 
void
void
xtensa_symbol_new_hook (symbolS *sym)
xtensa_symbol_new_hook (symbolS *sym)
{
{
  if (is_leb128_expr && S_GET_SEGMENT (sym) == expr_section)
  if (is_leb128_expr && S_GET_SEGMENT (sym) == expr_section)
    {
    {
      symbol_get_tc (sym)->next_expr_symbol = expr_symbols;
      symbol_get_tc (sym)->next_expr_symbol = expr_symbols;
      expr_symbols = sym;
      expr_symbols = sym;
    }
    }
}
}
 
 
 
 
void
void
md_apply_fix (fixS *fixP, valueT *valP, segT seg)
md_apply_fix (fixS *fixP, valueT *valP, segT seg)
{
{
  char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
  char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
  valueT val = 0;
  valueT val = 0;
 
 
  /* Subtracted symbols are only allowed for a few relocation types, and
  /* Subtracted symbols are only allowed for a few relocation types, and
     unless linkrelax is enabled, they should not make it to this point.  */
     unless linkrelax is enabled, they should not make it to this point.  */
  if (fixP->fx_subsy && !(linkrelax && (fixP->fx_r_type == BFD_RELOC_32
  if (fixP->fx_subsy && !(linkrelax && (fixP->fx_r_type == BFD_RELOC_32
                                        || fixP->fx_r_type == BFD_RELOC_16
                                        || fixP->fx_r_type == BFD_RELOC_16
                                        || fixP->fx_r_type == BFD_RELOC_8)))
                                        || fixP->fx_r_type == BFD_RELOC_8)))
    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
 
 
  switch (fixP->fx_r_type)
  switch (fixP->fx_r_type)
    {
    {
    case BFD_RELOC_32_PCREL:
    case BFD_RELOC_32_PCREL:
    case BFD_RELOC_32:
    case BFD_RELOC_32:
    case BFD_RELOC_16:
    case BFD_RELOC_16:
    case BFD_RELOC_8:
    case BFD_RELOC_8:
      if (fixP->fx_subsy)
      if (fixP->fx_subsy)
        {
        {
          switch (fixP->fx_r_type)
          switch (fixP->fx_r_type)
            {
            {
            case BFD_RELOC_8:
            case BFD_RELOC_8:
              fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
              fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
              break;
              break;
            case BFD_RELOC_16:
            case BFD_RELOC_16:
              fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
              fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
              break;
              break;
            case BFD_RELOC_32:
            case BFD_RELOC_32:
              fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
              fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
              break;
              break;
            default:
            default:
              break;
              break;
            }
            }
 
 
          /* An offset is only allowed when it results from adjusting a
          /* An offset is only allowed when it results from adjusting a
             local symbol into a section-relative offset.  If the offset
             local symbol into a section-relative offset.  If the offset
             came from the original expression, tc_fix_adjustable will have
             came from the original expression, tc_fix_adjustable will have
             prevented the fix from being converted to a section-relative
             prevented the fix from being converted to a section-relative
             form so that we can flag the error here.  */
             form so that we can flag the error here.  */
          if (fixP->fx_offset != 0 && !symbol_section_p (fixP->fx_addsy))
          if (fixP->fx_offset != 0 && !symbol_section_p (fixP->fx_addsy))
            as_bad_where (fixP->fx_file, fixP->fx_line,
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("cannot represent subtraction with an offset"));
                          _("cannot represent subtraction with an offset"));
 
 
          val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
          val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
                 - S_GET_VALUE (fixP->fx_subsy));
                 - S_GET_VALUE (fixP->fx_subsy));
 
 
          /* The difference value gets written out, and the DIFF reloc
          /* The difference value gets written out, and the DIFF reloc
             identifies the address of the subtracted symbol (i.e., the one
             identifies the address of the subtracted symbol (i.e., the one
             with the lowest address).  */
             with the lowest address).  */
          *valP = val;
          *valP = val;
          fixP->fx_offset -= val;
          fixP->fx_offset -= val;
          fixP->fx_subsy = NULL;
          fixP->fx_subsy = NULL;
        }
        }
      else if (! fixP->fx_addsy)
      else if (! fixP->fx_addsy)
        {
        {
          val = *valP;
          val = *valP;
          fixP->fx_done = 1;
          fixP->fx_done = 1;
        }
        }
      /* fall through */
      /* fall through */
 
 
    case BFD_RELOC_XTENSA_PLT:
    case BFD_RELOC_XTENSA_PLT:
      md_number_to_chars (fixpos, val, fixP->fx_size);
      md_number_to_chars (fixpos, val, fixP->fx_size);
      fixP->fx_no_overflow = 0; /* Use the standard overflow check.  */
      fixP->fx_no_overflow = 0; /* Use the standard overflow check.  */
      break;
      break;
 
 
    case BFD_RELOC_XTENSA_TLSDESC_FN:
    case BFD_RELOC_XTENSA_TLSDESC_FN:
    case BFD_RELOC_XTENSA_TLSDESC_ARG:
    case BFD_RELOC_XTENSA_TLSDESC_ARG:
    case BFD_RELOC_XTENSA_TLS_TPOFF:
    case BFD_RELOC_XTENSA_TLS_TPOFF:
    case BFD_RELOC_XTENSA_TLS_DTPOFF:
    case BFD_RELOC_XTENSA_TLS_DTPOFF:
      S_SET_THREAD_LOCAL (fixP->fx_addsy);
      S_SET_THREAD_LOCAL (fixP->fx_addsy);
      md_number_to_chars (fixpos, 0, fixP->fx_size);
      md_number_to_chars (fixpos, 0, fixP->fx_size);
      fixP->fx_no_overflow = 0; /* Use the standard overflow check.  */
      fixP->fx_no_overflow = 0; /* Use the standard overflow check.  */
      break;
      break;
 
 
    case BFD_RELOC_XTENSA_SLOT0_OP:
    case BFD_RELOC_XTENSA_SLOT0_OP:
    case BFD_RELOC_XTENSA_SLOT1_OP:
    case BFD_RELOC_XTENSA_SLOT1_OP:
    case BFD_RELOC_XTENSA_SLOT2_OP:
    case BFD_RELOC_XTENSA_SLOT2_OP:
    case BFD_RELOC_XTENSA_SLOT3_OP:
    case BFD_RELOC_XTENSA_SLOT3_OP:
    case BFD_RELOC_XTENSA_SLOT4_OP:
    case BFD_RELOC_XTENSA_SLOT4_OP:
    case BFD_RELOC_XTENSA_SLOT5_OP:
    case BFD_RELOC_XTENSA_SLOT5_OP:
    case BFD_RELOC_XTENSA_SLOT6_OP:
    case BFD_RELOC_XTENSA_SLOT6_OP:
    case BFD_RELOC_XTENSA_SLOT7_OP:
    case BFD_RELOC_XTENSA_SLOT7_OP:
    case BFD_RELOC_XTENSA_SLOT8_OP:
    case BFD_RELOC_XTENSA_SLOT8_OP:
    case BFD_RELOC_XTENSA_SLOT9_OP:
    case BFD_RELOC_XTENSA_SLOT9_OP:
    case BFD_RELOC_XTENSA_SLOT10_OP:
    case BFD_RELOC_XTENSA_SLOT10_OP:
    case BFD_RELOC_XTENSA_SLOT11_OP:
    case BFD_RELOC_XTENSA_SLOT11_OP:
    case BFD_RELOC_XTENSA_SLOT12_OP:
    case BFD_RELOC_XTENSA_SLOT12_OP:
    case BFD_RELOC_XTENSA_SLOT13_OP:
    case BFD_RELOC_XTENSA_SLOT13_OP:
    case BFD_RELOC_XTENSA_SLOT14_OP:
    case BFD_RELOC_XTENSA_SLOT14_OP:
      if (linkrelax)
      if (linkrelax)
        {
        {
          /* Write the tentative value of a PC-relative relocation to a
          /* Write the tentative value of a PC-relative relocation to a
             local symbol into the instruction.  The value will be ignored
             local symbol into the instruction.  The value will be ignored
             by the linker, and it makes the object file disassembly
             by the linker, and it makes the object file disassembly
             readable when all branch targets are encoded in relocations.  */
             readable when all branch targets are encoded in relocations.  */
 
 
          gas_assert (fixP->fx_addsy);
          gas_assert (fixP->fx_addsy);
          if (S_GET_SEGMENT (fixP->fx_addsy) == seg
          if (S_GET_SEGMENT (fixP->fx_addsy) == seg
              && !S_FORCE_RELOC (fixP->fx_addsy, 1))
              && !S_FORCE_RELOC (fixP->fx_addsy, 1))
            {
            {
              val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
              val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
                     - md_pcrel_from (fixP));
                     - md_pcrel_from (fixP));
              (void) xg_apply_fix_value (fixP, val);
              (void) xg_apply_fix_value (fixP, val);
            }
            }
        }
        }
      else if (! fixP->fx_addsy)
      else if (! fixP->fx_addsy)
        {
        {
          val = *valP;
          val = *valP;
          if (xg_apply_fix_value (fixP, val))
          if (xg_apply_fix_value (fixP, val))
            fixP->fx_done = 1;
            fixP->fx_done = 1;
        }
        }
      break;
      break;
 
 
    case BFD_RELOC_XTENSA_ASM_EXPAND:
    case BFD_RELOC_XTENSA_ASM_EXPAND:
    case BFD_RELOC_XTENSA_TLS_FUNC:
    case BFD_RELOC_XTENSA_TLS_FUNC:
    case BFD_RELOC_XTENSA_TLS_ARG:
    case BFD_RELOC_XTENSA_TLS_ARG:
    case BFD_RELOC_XTENSA_TLS_CALL:
    case BFD_RELOC_XTENSA_TLS_CALL:
    case BFD_RELOC_XTENSA_SLOT0_ALT:
    case BFD_RELOC_XTENSA_SLOT0_ALT:
    case BFD_RELOC_XTENSA_SLOT1_ALT:
    case BFD_RELOC_XTENSA_SLOT1_ALT:
    case BFD_RELOC_XTENSA_SLOT2_ALT:
    case BFD_RELOC_XTENSA_SLOT2_ALT:
    case BFD_RELOC_XTENSA_SLOT3_ALT:
    case BFD_RELOC_XTENSA_SLOT3_ALT:
    case BFD_RELOC_XTENSA_SLOT4_ALT:
    case BFD_RELOC_XTENSA_SLOT4_ALT:
    case BFD_RELOC_XTENSA_SLOT5_ALT:
    case BFD_RELOC_XTENSA_SLOT5_ALT:
    case BFD_RELOC_XTENSA_SLOT6_ALT:
    case BFD_RELOC_XTENSA_SLOT6_ALT:
    case BFD_RELOC_XTENSA_SLOT7_ALT:
    case BFD_RELOC_XTENSA_SLOT7_ALT:
    case BFD_RELOC_XTENSA_SLOT8_ALT:
    case BFD_RELOC_XTENSA_SLOT8_ALT:
    case BFD_RELOC_XTENSA_SLOT9_ALT:
    case BFD_RELOC_XTENSA_SLOT9_ALT:
    case BFD_RELOC_XTENSA_SLOT10_ALT:
    case BFD_RELOC_XTENSA_SLOT10_ALT:
    case BFD_RELOC_XTENSA_SLOT11_ALT:
    case BFD_RELOC_XTENSA_SLOT11_ALT:
    case BFD_RELOC_XTENSA_SLOT12_ALT:
    case BFD_RELOC_XTENSA_SLOT12_ALT:
    case BFD_RELOC_XTENSA_SLOT13_ALT:
    case BFD_RELOC_XTENSA_SLOT13_ALT:
    case BFD_RELOC_XTENSA_SLOT14_ALT:
    case BFD_RELOC_XTENSA_SLOT14_ALT:
      /* These all need to be resolved at link-time.  Do nothing now.  */
      /* These all need to be resolved at link-time.  Do nothing now.  */
      break;
      break;
 
 
    case BFD_RELOC_VTABLE_INHERIT:
    case BFD_RELOC_VTABLE_INHERIT:
    case BFD_RELOC_VTABLE_ENTRY:
    case BFD_RELOC_VTABLE_ENTRY:
      fixP->fx_done = 0;
      fixP->fx_done = 0;
      break;
      break;
 
 
    default:
    default:
      as_bad (_("unhandled local relocation fix %s"),
      as_bad (_("unhandled local relocation fix %s"),
              bfd_get_reloc_code_name (fixP->fx_r_type));
              bfd_get_reloc_code_name (fixP->fx_r_type));
    }
    }
}
}
 
 
 
 
char *
char *
md_atof (int type, char *litP, int *sizeP)
md_atof (int type, char *litP, int *sizeP)
{
{
  return ieee_md_atof (type, litP, sizeP, target_big_endian);
  return ieee_md_atof (type, litP, sizeP, target_big_endian);
}
}
 
 
 
 
int
int
md_estimate_size_before_relax (fragS *fragP, segT seg ATTRIBUTE_UNUSED)
md_estimate_size_before_relax (fragS *fragP, segT seg ATTRIBUTE_UNUSED)
{
{
  return total_frag_text_expansion (fragP);
  return total_frag_text_expansion (fragP);
}
}
 
 
 
 
/* Translate internal representation of relocation info to BFD target
/* Translate internal representation of relocation info to BFD target
   format.  */
   format.  */
 
 
arelent *
arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
{
{
  arelent *reloc;
  arelent *reloc;
 
 
  reloc = (arelent *) xmalloc (sizeof (arelent));
  reloc = (arelent *) xmalloc (sizeof (arelent));
  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
 
  /* Make sure none of our internal relocations make it this far.
  /* Make sure none of our internal relocations make it this far.
     They'd better have been fully resolved by this point.  */
     They'd better have been fully resolved by this point.  */
  gas_assert ((int) fixp->fx_r_type > 0);
  gas_assert ((int) fixp->fx_r_type > 0);
 
 
  reloc->addend = fixp->fx_offset;
  reloc->addend = fixp->fx_offset;
 
 
  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
  if (reloc->howto == NULL)
  if (reloc->howto == NULL)
    {
    {
      as_bad_where (fixp->fx_file, fixp->fx_line,
      as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("cannot represent `%s' relocation in object file"),
                    _("cannot represent `%s' relocation in object file"),
                    bfd_get_reloc_code_name (fixp->fx_r_type));
                    bfd_get_reloc_code_name (fixp->fx_r_type));
      free (reloc->sym_ptr_ptr);
      free (reloc->sym_ptr_ptr);
      free (reloc);
      free (reloc);
      return NULL;
      return NULL;
    }
    }
 
 
  if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
  if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
    as_fatal (_("internal error; cannot generate `%s' relocation"),
    as_fatal (_("internal error; cannot generate `%s' relocation"),
              bfd_get_reloc_code_name (fixp->fx_r_type));
              bfd_get_reloc_code_name (fixp->fx_r_type));
 
 
  return reloc;
  return reloc;
}
}
 
 


/* Checks for resource conflicts between instructions.  */
/* Checks for resource conflicts between instructions.  */
 
 
/* The func unit stuff could be implemented as bit-vectors rather
/* The func unit stuff could be implemented as bit-vectors rather
   than the iterative approach here.  If it ends up being too
   than the iterative approach here.  If it ends up being too
   slow, we will switch it.  */
   slow, we will switch it.  */
 
 
resource_table *
resource_table *
new_resource_table (void *data,
new_resource_table (void *data,
                    int cycles,
                    int cycles,
                    int nu,
                    int nu,
                    unit_num_copies_func uncf,
                    unit_num_copies_func uncf,
                    opcode_num_units_func onuf,
                    opcode_num_units_func onuf,
                    opcode_funcUnit_use_unit_func ouuf,
                    opcode_funcUnit_use_unit_func ouuf,
                    opcode_funcUnit_use_stage_func ousf)
                    opcode_funcUnit_use_stage_func ousf)
{
{
  int i;
  int i;
  resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
  resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
  rt->data = data;
  rt->data = data;
  rt->cycles = cycles;
  rt->cycles = cycles;
  rt->allocated_cycles = cycles;
  rt->allocated_cycles = cycles;
  rt->num_units = nu;
  rt->num_units = nu;
  rt->unit_num_copies = uncf;
  rt->unit_num_copies = uncf;
  rt->opcode_num_units = onuf;
  rt->opcode_num_units = onuf;
  rt->opcode_unit_use = ouuf;
  rt->opcode_unit_use = ouuf;
  rt->opcode_unit_stage = ousf;
  rt->opcode_unit_stage = ousf;
 
 
  rt->units = (unsigned char **) xcalloc (cycles, sizeof (unsigned char *));
  rt->units = (unsigned char **) xcalloc (cycles, sizeof (unsigned char *));
  for (i = 0; i < cycles; i++)
  for (i = 0; i < cycles; i++)
    rt->units[i] = (unsigned char *) xcalloc (nu, sizeof (unsigned char));
    rt->units[i] = (unsigned char *) xcalloc (nu, sizeof (unsigned char));
 
 
  return rt;
  return rt;
}
}
 
 
 
 
void
void
clear_resource_table (resource_table *rt)
clear_resource_table (resource_table *rt)
{
{
  int i, j;
  int i, j;
  for (i = 0; i < rt->allocated_cycles; i++)
  for (i = 0; i < rt->allocated_cycles; i++)
    for (j = 0; j < rt->num_units; j++)
    for (j = 0; j < rt->num_units; j++)
      rt->units[i][j] = 0;
      rt->units[i][j] = 0;
}
}
 
 
 
 
/* We never shrink it, just fake it into thinking so.  */
/* We never shrink it, just fake it into thinking so.  */
 
 
void
void
resize_resource_table (resource_table *rt, int cycles)
resize_resource_table (resource_table *rt, int cycles)
{
{
  int i, old_cycles;
  int i, old_cycles;
 
 
  rt->cycles = cycles;
  rt->cycles = cycles;
  if (cycles <= rt->allocated_cycles)
  if (cycles <= rt->allocated_cycles)
    return;
    return;
 
 
  old_cycles = rt->allocated_cycles;
  old_cycles = rt->allocated_cycles;
  rt->allocated_cycles = cycles;
  rt->allocated_cycles = cycles;
 
 
  rt->units = xrealloc (rt->units,
  rt->units = xrealloc (rt->units,
                        rt->allocated_cycles * sizeof (unsigned char *));
                        rt->allocated_cycles * sizeof (unsigned char *));
  for (i = 0; i < old_cycles; i++)
  for (i = 0; i < old_cycles; i++)
    rt->units[i] = xrealloc (rt->units[i],
    rt->units[i] = xrealloc (rt->units[i],
                             rt->num_units * sizeof (unsigned char));
                             rt->num_units * sizeof (unsigned char));
  for (i = old_cycles; i < cycles; i++)
  for (i = old_cycles; i < cycles; i++)
    rt->units[i] = xcalloc (rt->num_units, sizeof (unsigned char));
    rt->units[i] = xcalloc (rt->num_units, sizeof (unsigned char));
}
}
 
 
 
 
bfd_boolean
bfd_boolean
resources_available (resource_table *rt, xtensa_opcode opcode, int cycle)
resources_available (resource_table *rt, xtensa_opcode opcode, int cycle)
{
{
  int i;
  int i;
  int uses = (rt->opcode_num_units) (rt->data, opcode);
  int uses = (rt->opcode_num_units) (rt->data, opcode);
 
 
  for (i = 0; i < uses; i++)
  for (i = 0; i < uses; i++)
    {
    {
      xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
      xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
      int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
      int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
      int copies_in_use = rt->units[stage + cycle][unit];
      int copies_in_use = rt->units[stage + cycle][unit];
      int copies = (rt->unit_num_copies) (rt->data, unit);
      int copies = (rt->unit_num_copies) (rt->data, unit);
      if (copies_in_use >= copies)
      if (copies_in_use >= copies)
        return FALSE;
        return FALSE;
    }
    }
  return TRUE;
  return TRUE;
}
}
 
 
 
 
void
void
reserve_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
reserve_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
{
{
  int i;
  int i;
  int uses = (rt->opcode_num_units) (rt->data, opcode);
  int uses = (rt->opcode_num_units) (rt->data, opcode);
 
 
  for (i = 0; i < uses; i++)
  for (i = 0; i < uses; i++)
    {
    {
      xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
      xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
      int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
      int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
      /* Note that this allows resources to be oversubscribed.  That's
      /* Note that this allows resources to be oversubscribed.  That's
         essential to the way the optional scheduler works.
         essential to the way the optional scheduler works.
         resources_available reports when a resource is over-subscribed,
         resources_available reports when a resource is over-subscribed,
         so it's easy to tell.  */
         so it's easy to tell.  */
      rt->units[stage + cycle][unit]++;
      rt->units[stage + cycle][unit]++;
    }
    }
}
}
 
 
 
 
void
void
release_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
release_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
{
{
  int i;
  int i;
  int uses = (rt->opcode_num_units) (rt->data, opcode);
  int uses = (rt->opcode_num_units) (rt->data, opcode);
 
 
  for (i = 0; i < uses; i++)
  for (i = 0; i < uses; i++)
    {
    {
      xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
      xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
      int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
      int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
      gas_assert (rt->units[stage + cycle][unit] > 0);
      gas_assert (rt->units[stage + cycle][unit] > 0);
      rt->units[stage + cycle][unit]--;
      rt->units[stage + cycle][unit]--;
    }
    }
}
}
 
 
 
 
/* Wrapper functions make parameterized resource reservation
/* Wrapper functions make parameterized resource reservation
   more convenient.  */
   more convenient.  */
 
 
int
int
opcode_funcUnit_use_unit (void *data, xtensa_opcode opcode, int idx)
opcode_funcUnit_use_unit (void *data, xtensa_opcode opcode, int idx)
{
{
  xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
  xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
  return use->unit;
  return use->unit;
}
}
 
 
 
 
int
int
opcode_funcUnit_use_stage (void *data, xtensa_opcode opcode, int idx)
opcode_funcUnit_use_stage (void *data, xtensa_opcode opcode, int idx)
{
{
  xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
  xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
  return use->stage;
  return use->stage;
}
}
 
 
 
 
/* Note that this function does not check issue constraints, but
/* Note that this function does not check issue constraints, but
   solely whether the hardware is available to execute the given
   solely whether the hardware is available to execute the given
   instructions together.  It also doesn't check if the tinsns
   instructions together.  It also doesn't check if the tinsns
   write the same state, or access the same tieports.  That is
   write the same state, or access the same tieports.  That is
   checked by check_t1_t2_reads_and_writes.  */
   checked by check_t1_t2_reads_and_writes.  */
 
 
static bfd_boolean
static bfd_boolean
resources_conflict (vliw_insn *vinsn)
resources_conflict (vliw_insn *vinsn)
{
{
  int i;
  int i;
  static resource_table *rt = NULL;
  static resource_table *rt = NULL;
 
 
  /* This is the most common case by far.  Optimize it.  */
  /* This is the most common case by far.  Optimize it.  */
  if (vinsn->num_slots == 1)
  if (vinsn->num_slots == 1)
    return FALSE;
    return FALSE;
 
 
  if (rt == NULL)
  if (rt == NULL)
    {
    {
      xtensa_isa isa = xtensa_default_isa;
      xtensa_isa isa = xtensa_default_isa;
      rt = new_resource_table
      rt = new_resource_table
        (isa, xtensa_num_pipe_stages,
        (isa, xtensa_num_pipe_stages,
         xtensa_isa_num_funcUnits (isa),
         xtensa_isa_num_funcUnits (isa),
         (unit_num_copies_func) xtensa_funcUnit_num_copies,
         (unit_num_copies_func) xtensa_funcUnit_num_copies,
         (opcode_num_units_func) xtensa_opcode_num_funcUnit_uses,
         (opcode_num_units_func) xtensa_opcode_num_funcUnit_uses,
         opcode_funcUnit_use_unit,
         opcode_funcUnit_use_unit,
         opcode_funcUnit_use_stage);
         opcode_funcUnit_use_stage);
    }
    }
 
 
  clear_resource_table (rt);
  clear_resource_table (rt);
 
 
  for (i = 0; i < vinsn->num_slots; i++)
  for (i = 0; i < vinsn->num_slots; i++)
    {
    {
      if (!resources_available (rt, vinsn->slots[i].opcode, 0))
      if (!resources_available (rt, vinsn->slots[i].opcode, 0))
        return TRUE;
        return TRUE;
      reserve_resources (rt, vinsn->slots[i].opcode, 0);
      reserve_resources (rt, vinsn->slots[i].opcode, 0);
    }
    }
 
 
  return FALSE;
  return FALSE;
}
}
 
 


/* finish_vinsn, emit_single_op and helper functions.  */
/* finish_vinsn, emit_single_op and helper functions.  */
 
 
static bfd_boolean find_vinsn_conflicts (vliw_insn *);
static bfd_boolean find_vinsn_conflicts (vliw_insn *);
static xtensa_format xg_find_narrowest_format (vliw_insn *);
static xtensa_format xg_find_narrowest_format (vliw_insn *);
static void xg_assemble_vliw_tokens (vliw_insn *);
static void xg_assemble_vliw_tokens (vliw_insn *);
 
 
 
 
/* We have reached the end of a bundle; emit into the frag.  */
/* We have reached the end of a bundle; emit into the frag.  */
 
 
static void
static void
finish_vinsn (vliw_insn *vinsn)
finish_vinsn (vliw_insn *vinsn)
{
{
  IStack slotstack;
  IStack slotstack;
  int i;
  int i;
  char *file_name;
  char *file_name;
  unsigned line;
  unsigned line;
 
 
  if (find_vinsn_conflicts (vinsn))
  if (find_vinsn_conflicts (vinsn))
    {
    {
      xg_clear_vinsn (vinsn);
      xg_clear_vinsn (vinsn);
      return;
      return;
    }
    }
 
 
  /* First, find a format that works.  */
  /* First, find a format that works.  */
  if (vinsn->format == XTENSA_UNDEFINED)
  if (vinsn->format == XTENSA_UNDEFINED)
    vinsn->format = xg_find_narrowest_format (vinsn);
    vinsn->format = xg_find_narrowest_format (vinsn);
 
 
  if (xtensa_format_num_slots (xtensa_default_isa, vinsn->format) > 1
  if (xtensa_format_num_slots (xtensa_default_isa, vinsn->format) > 1
      && produce_flix == FLIX_NONE)
      && produce_flix == FLIX_NONE)
    {
    {
      as_bad (_("The option \"--no-allow-flix\" prohibits multi-slot flix."));
      as_bad (_("The option \"--no-allow-flix\" prohibits multi-slot flix."));
      xg_clear_vinsn (vinsn);
      xg_clear_vinsn (vinsn);
      return;
      return;
    }
    }
 
 
  if (vinsn->format == XTENSA_UNDEFINED)
  if (vinsn->format == XTENSA_UNDEFINED)
    {
    {
      as_where (&file_name, &line);
      as_where (&file_name, &line);
      as_bad_where (file_name, line,
      as_bad_where (file_name, line,
                    _("couldn't find a valid instruction format"));
                    _("couldn't find a valid instruction format"));
      fprintf (stderr, _("    ops were: "));
      fprintf (stderr, _("    ops were: "));
      for (i = 0; i < vinsn->num_slots; i++)
      for (i = 0; i < vinsn->num_slots; i++)
        fprintf (stderr, _(" %s;"),
        fprintf (stderr, _(" %s;"),
                 xtensa_opcode_name (xtensa_default_isa,
                 xtensa_opcode_name (xtensa_default_isa,
                                     vinsn->slots[i].opcode));
                                     vinsn->slots[i].opcode));
      fprintf (stderr, _("\n"));
      fprintf (stderr, _("\n"));
      xg_clear_vinsn (vinsn);
      xg_clear_vinsn (vinsn);
      return;
      return;
    }
    }
 
 
  if (vinsn->num_slots
  if (vinsn->num_slots
      != xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
      != xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
    {
    {
      as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
      as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
              xtensa_format_name (xtensa_default_isa, vinsn->format),
              xtensa_format_name (xtensa_default_isa, vinsn->format),
              xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
              xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
              vinsn->num_slots);
              vinsn->num_slots);
      xg_clear_vinsn (vinsn);
      xg_clear_vinsn (vinsn);
      return;
      return;
    }
    }
 
 
  if (resources_conflict (vinsn))
  if (resources_conflict (vinsn))
    {
    {
      as_where (&file_name, &line);
      as_where (&file_name, &line);
      as_bad_where (file_name, line, _("illegal resource usage in bundle"));
      as_bad_where (file_name, line, _("illegal resource usage in bundle"));
      fprintf (stderr, "    ops were: ");
      fprintf (stderr, "    ops were: ");
      for (i = 0; i < vinsn->num_slots; i++)
      for (i = 0; i < vinsn->num_slots; i++)
        fprintf (stderr, " %s;",
        fprintf (stderr, " %s;",
                 xtensa_opcode_name (xtensa_default_isa,
                 xtensa_opcode_name (xtensa_default_isa,
                                     vinsn->slots[i].opcode));
                                     vinsn->slots[i].opcode));
      fprintf (stderr, "\n");
      fprintf (stderr, "\n");
      xg_clear_vinsn (vinsn);
      xg_clear_vinsn (vinsn);
      return;
      return;
    }
    }
 
 
  for (i = 0; i < vinsn->num_slots; i++)
  for (i = 0; i < vinsn->num_slots; i++)
    {
    {
      if (vinsn->slots[i].opcode != XTENSA_UNDEFINED)
      if (vinsn->slots[i].opcode != XTENSA_UNDEFINED)
        {
        {
          symbolS *lit_sym = NULL;
          symbolS *lit_sym = NULL;
          int j;
          int j;
          bfd_boolean e = FALSE;
          bfd_boolean e = FALSE;
          bfd_boolean saved_density = density_supported;
          bfd_boolean saved_density = density_supported;
 
 
          /* We don't want to narrow ops inside multi-slot bundles.  */
          /* We don't want to narrow ops inside multi-slot bundles.  */
          if (vinsn->num_slots > 1)
          if (vinsn->num_slots > 1)
            density_supported = FALSE;
            density_supported = FALSE;
 
 
          istack_init (&slotstack);
          istack_init (&slotstack);
          if (vinsn->slots[i].opcode == xtensa_nop_opcode)
          if (vinsn->slots[i].opcode == xtensa_nop_opcode)
            {
            {
              vinsn->slots[i].opcode =
              vinsn->slots[i].opcode =
                xtensa_format_slot_nop_opcode (xtensa_default_isa,
                xtensa_format_slot_nop_opcode (xtensa_default_isa,
                                               vinsn->format, i);
                                               vinsn->format, i);
              vinsn->slots[i].ntok = 0;
              vinsn->slots[i].ntok = 0;
            }
            }
 
 
          if (xg_expand_assembly_insn (&slotstack, &vinsn->slots[i]))
          if (xg_expand_assembly_insn (&slotstack, &vinsn->slots[i]))
            {
            {
              e = TRUE;
              e = TRUE;
              continue;
              continue;
            }
            }
 
 
          density_supported = saved_density;
          density_supported = saved_density;
 
 
          if (e)
          if (e)
            {
            {
              xg_clear_vinsn (vinsn);
              xg_clear_vinsn (vinsn);
              return;
              return;
            }
            }
 
 
          for (j = 0; j < slotstack.ninsn; j++)
          for (j = 0; j < slotstack.ninsn; j++)
            {
            {
              TInsn *insn = &slotstack.insn[j];
              TInsn *insn = &slotstack.insn[j];
              if (insn->insn_type == ITYPE_LITERAL)
              if (insn->insn_type == ITYPE_LITERAL)
                {
                {
                  gas_assert (lit_sym == NULL);
                  gas_assert (lit_sym == NULL);
                  lit_sym = xg_assemble_literal (insn);
                  lit_sym = xg_assemble_literal (insn);
                }
                }
              else
              else
                {
                {
                  gas_assert (insn->insn_type == ITYPE_INSN);
                  gas_assert (insn->insn_type == ITYPE_INSN);
                  if (lit_sym)
                  if (lit_sym)
                    xg_resolve_literals (insn, lit_sym);
                    xg_resolve_literals (insn, lit_sym);
                  if (j != slotstack.ninsn - 1)
                  if (j != slotstack.ninsn - 1)
                    emit_single_op (insn);
                    emit_single_op (insn);
                }
                }
            }
            }
 
 
          if (vinsn->num_slots > 1)
          if (vinsn->num_slots > 1)
            {
            {
              if (opcode_fits_format_slot
              if (opcode_fits_format_slot
                  (slotstack.insn[slotstack.ninsn - 1].opcode,
                  (slotstack.insn[slotstack.ninsn - 1].opcode,
                   vinsn->format, i))
                   vinsn->format, i))
                {
                {
                  vinsn->slots[i] = slotstack.insn[slotstack.ninsn - 1];
                  vinsn->slots[i] = slotstack.insn[slotstack.ninsn - 1];
                }
                }
              else
              else
                {
                {
                  emit_single_op (&slotstack.insn[slotstack.ninsn - 1]);
                  emit_single_op (&slotstack.insn[slotstack.ninsn - 1]);
                  if (vinsn->format == XTENSA_UNDEFINED)
                  if (vinsn->format == XTENSA_UNDEFINED)
                    vinsn->slots[i].opcode = xtensa_nop_opcode;
                    vinsn->slots[i].opcode = xtensa_nop_opcode;
                  else
                  else
                    vinsn->slots[i].opcode
                    vinsn->slots[i].opcode
                      = xtensa_format_slot_nop_opcode (xtensa_default_isa,
                      = xtensa_format_slot_nop_opcode (xtensa_default_isa,
                                                       vinsn->format, i);
                                                       vinsn->format, i);
 
 
                  vinsn->slots[i].ntok = 0;
                  vinsn->slots[i].ntok = 0;
                }
                }
            }
            }
          else
          else
            {
            {
              vinsn->slots[0] = slotstack.insn[slotstack.ninsn - 1];
              vinsn->slots[0] = slotstack.insn[slotstack.ninsn - 1];
              vinsn->format = XTENSA_UNDEFINED;
              vinsn->format = XTENSA_UNDEFINED;
            }
            }
        }
        }
    }
    }
 
 
  /* Now check resource conflicts on the modified bundle.  */
  /* Now check resource conflicts on the modified bundle.  */
  if (resources_conflict (vinsn))
  if (resources_conflict (vinsn))
    {
    {
      as_where (&file_name, &line);
      as_where (&file_name, &line);
      as_bad_where (file_name, line, _("illegal resource usage in bundle"));
      as_bad_where (file_name, line, _("illegal resource usage in bundle"));
      fprintf (stderr, "    ops were: ");
      fprintf (stderr, "    ops were: ");
      for (i = 0; i < vinsn->num_slots; i++)
      for (i = 0; i < vinsn->num_slots; i++)
        fprintf (stderr, " %s;",
        fprintf (stderr, " %s;",
                 xtensa_opcode_name (xtensa_default_isa,
                 xtensa_opcode_name (xtensa_default_isa,
                                     vinsn->slots[i].opcode));
                                     vinsn->slots[i].opcode));
      fprintf (stderr, "\n");
      fprintf (stderr, "\n");
      xg_clear_vinsn (vinsn);
      xg_clear_vinsn (vinsn);
      return;
      return;
    }
    }
 
 
  /* First, find a format that works.  */
  /* First, find a format that works.  */
  if (vinsn->format == XTENSA_UNDEFINED)
  if (vinsn->format == XTENSA_UNDEFINED)
      vinsn->format = xg_find_narrowest_format (vinsn);
      vinsn->format = xg_find_narrowest_format (vinsn);
 
 
  xg_assemble_vliw_tokens (vinsn);
  xg_assemble_vliw_tokens (vinsn);
 
 
  xg_clear_vinsn (vinsn);
  xg_clear_vinsn (vinsn);
}
}
 
 
 
 
/* Given an vliw instruction, what conflicts are there in register
/* Given an vliw instruction, what conflicts are there in register
   usage and in writes to states and queues?
   usage and in writes to states and queues?
 
 
   This function does two things:
   This function does two things:
   1. Reports an error when a vinsn contains illegal combinations
   1. Reports an error when a vinsn contains illegal combinations
      of writes to registers states or queues.
      of writes to registers states or queues.
   2. Marks individual tinsns as not relaxable if the combination
   2. Marks individual tinsns as not relaxable if the combination
      contains antidependencies.
      contains antidependencies.
 
 
   Job 2 handles things like swap semantics in instructions that need
   Job 2 handles things like swap semantics in instructions that need
   to be relaxed.  For example,
   to be relaxed.  For example,
 
 
        addi a0, a1, 100000
        addi a0, a1, 100000
 
 
   normally would be relaxed to
   normally would be relaxed to
 
 
        l32r a0, some_label
        l32r a0, some_label
        add a0, a1, a0
        add a0, a1, a0
 
 
   _but_, if the above instruction is bundled with an a0 reader, e.g.,
   _but_, if the above instruction is bundled with an a0 reader, e.g.,
 
 
        { addi a0, a1, 10000 ; add a2, a0, a4 ; }
        { addi a0, a1, 10000 ; add a2, a0, a4 ; }
 
 
   then we can't relax it into
   then we can't relax it into
 
 
        l32r a0, some_label
        l32r a0, some_label
        { add a0, a1, a0 ; add a2, a0, a4 ; }
        { add a0, a1, a0 ; add a2, a0, a4 ; }
 
 
   because the value of a0 is trashed before the second add can read it.  */
   because the value of a0 is trashed before the second add can read it.  */
 
 
static char check_t1_t2_reads_and_writes (TInsn *, TInsn *);
static char check_t1_t2_reads_and_writes (TInsn *, TInsn *);
 
 
static bfd_boolean
static bfd_boolean
find_vinsn_conflicts (vliw_insn *vinsn)
find_vinsn_conflicts (vliw_insn *vinsn)
{
{
  int i, j;
  int i, j;
  int branches = 0;
  int branches = 0;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
 
 
  gas_assert (!past_xtensa_end);
  gas_assert (!past_xtensa_end);
 
 
  for (i = 0 ; i < vinsn->num_slots; i++)
  for (i = 0 ; i < vinsn->num_slots; i++)
    {
    {
      TInsn *op1 = &vinsn->slots[i];
      TInsn *op1 = &vinsn->slots[i];
      if (op1->is_specific_opcode)
      if (op1->is_specific_opcode)
        op1->keep_wide = TRUE;
        op1->keep_wide = TRUE;
      else
      else
        op1->keep_wide = FALSE;
        op1->keep_wide = FALSE;
    }
    }
 
 
  for (i = 0 ; i < vinsn->num_slots; i++)
  for (i = 0 ; i < vinsn->num_slots; i++)
    {
    {
      TInsn *op1 = &vinsn->slots[i];
      TInsn *op1 = &vinsn->slots[i];
 
 
      if (xtensa_opcode_is_branch (isa, op1->opcode) == 1)
      if (xtensa_opcode_is_branch (isa, op1->opcode) == 1)
        branches++;
        branches++;
 
 
      for (j = 0; j < vinsn->num_slots; j++)
      for (j = 0; j < vinsn->num_slots; j++)
        {
        {
          if (i != j)
          if (i != j)
            {
            {
              TInsn *op2 = &vinsn->slots[j];
              TInsn *op2 = &vinsn->slots[j];
              char conflict_type = check_t1_t2_reads_and_writes (op1, op2);
              char conflict_type = check_t1_t2_reads_and_writes (op1, op2);
              switch (conflict_type)
              switch (conflict_type)
                {
                {
                case 'c':
                case 'c':
                  as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same register"),
                  as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same register"),
                          xtensa_opcode_name (isa, op1->opcode), i,
                          xtensa_opcode_name (isa, op1->opcode), i,
                          xtensa_opcode_name (isa, op2->opcode), j);
                          xtensa_opcode_name (isa, op2->opcode), j);
                  return TRUE;
                  return TRUE;
                case 'd':
                case 'd':
                  as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same state"),
                  as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same state"),
                          xtensa_opcode_name (isa, op1->opcode), i,
                          xtensa_opcode_name (isa, op1->opcode), i,
                          xtensa_opcode_name (isa, op2->opcode), j);
                          xtensa_opcode_name (isa, op2->opcode), j);
                  return TRUE;
                  return TRUE;
                case 'e':
                case 'e':
                  as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same port"),
                  as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same port"),
                          xtensa_opcode_name (isa, op1->opcode), i,
                          xtensa_opcode_name (isa, op1->opcode), i,
                          xtensa_opcode_name (isa, op2->opcode), j);
                          xtensa_opcode_name (isa, op2->opcode), j);
                  return TRUE;
                  return TRUE;
                case 'f':
                case 'f':
                  as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) both have volatile port accesses"),
                  as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) both have volatile port accesses"),
                          xtensa_opcode_name (isa, op1->opcode), i,
                          xtensa_opcode_name (isa, op1->opcode), i,
                          xtensa_opcode_name (isa, op2->opcode), j);
                          xtensa_opcode_name (isa, op2->opcode), j);
                  return TRUE;
                  return TRUE;
                default:
                default:
                  /* Everything is OK.  */
                  /* Everything is OK.  */
                  break;
                  break;
                }
                }
              op2->is_specific_opcode = (op2->is_specific_opcode
              op2->is_specific_opcode = (op2->is_specific_opcode
                                         || conflict_type == 'a');
                                         || conflict_type == 'a');
            }
            }
        }
        }
    }
    }
 
 
  if (branches > 1)
  if (branches > 1)
    {
    {
      as_bad (_("multiple branches or jumps in the same bundle"));
      as_bad (_("multiple branches or jumps in the same bundle"));
      return TRUE;
      return TRUE;
    }
    }
 
 
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Check how the state used by t1 and t2 relate.
/* Check how the state used by t1 and t2 relate.
   Cases found are:
   Cases found are:
 
 
   case A: t1 reads a register t2 writes (an antidependency within a bundle)
   case A: t1 reads a register t2 writes (an antidependency within a bundle)
   case B: no relationship between what is read and written (both could
   case B: no relationship between what is read and written (both could
           read the same reg though)
           read the same reg though)
   case C: t1 writes a register t2 writes (a register conflict within a
   case C: t1 writes a register t2 writes (a register conflict within a
           bundle)
           bundle)
   case D: t1 writes a state that t2 also writes
   case D: t1 writes a state that t2 also writes
   case E: t1 writes a tie queue that t2 also writes
   case E: t1 writes a tie queue that t2 also writes
   case F: two volatile queue accesses
   case F: two volatile queue accesses
*/
*/
 
 
static char
static char
check_t1_t2_reads_and_writes (TInsn *t1, TInsn *t2)
check_t1_t2_reads_and_writes (TInsn *t1, TInsn *t2)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_regfile t1_regfile, t2_regfile;
  xtensa_regfile t1_regfile, t2_regfile;
  int t1_reg, t2_reg;
  int t1_reg, t2_reg;
  int t1_base_reg, t1_last_reg;
  int t1_base_reg, t1_last_reg;
  int t2_base_reg, t2_last_reg;
  int t2_base_reg, t2_last_reg;
  char t1_inout, t2_inout;
  char t1_inout, t2_inout;
  int i, j;
  int i, j;
  char conflict = 'b';
  char conflict = 'b';
  int t1_states;
  int t1_states;
  int t2_states;
  int t2_states;
  int t1_interfaces;
  int t1_interfaces;
  int t2_interfaces;
  int t2_interfaces;
  bfd_boolean t1_volatile = FALSE;
  bfd_boolean t1_volatile = FALSE;
  bfd_boolean t2_volatile = FALSE;
  bfd_boolean t2_volatile = FALSE;
 
 
  /* Check registers.  */
  /* Check registers.  */
  for (j = 0; j < t2->ntok; j++)
  for (j = 0; j < t2->ntok; j++)
    {
    {
      if (xtensa_operand_is_register (isa, t2->opcode, j) != 1)
      if (xtensa_operand_is_register (isa, t2->opcode, j) != 1)
        continue;
        continue;
 
 
      t2_regfile = xtensa_operand_regfile (isa, t2->opcode, j);
      t2_regfile = xtensa_operand_regfile (isa, t2->opcode, j);
      t2_base_reg = t2->tok[j].X_add_number;
      t2_base_reg = t2->tok[j].X_add_number;
      t2_last_reg = t2_base_reg + xtensa_operand_num_regs (isa, t2->opcode, j);
      t2_last_reg = t2_base_reg + xtensa_operand_num_regs (isa, t2->opcode, j);
 
 
      for (i = 0; i < t1->ntok; i++)
      for (i = 0; i < t1->ntok; i++)
        {
        {
          if (xtensa_operand_is_register (isa, t1->opcode, i) != 1)
          if (xtensa_operand_is_register (isa, t1->opcode, i) != 1)
            continue;
            continue;
 
 
          t1_regfile = xtensa_operand_regfile (isa, t1->opcode, i);
          t1_regfile = xtensa_operand_regfile (isa, t1->opcode, i);
 
 
          if (t1_regfile != t2_regfile)
          if (t1_regfile != t2_regfile)
            continue;
            continue;
 
 
          t1_inout = xtensa_operand_inout (isa, t1->opcode, i);
          t1_inout = xtensa_operand_inout (isa, t1->opcode, i);
          t2_inout = xtensa_operand_inout (isa, t2->opcode, j);
          t2_inout = xtensa_operand_inout (isa, t2->opcode, j);
 
 
          if (xtensa_operand_is_known_reg (isa, t1->opcode, i) == 0
          if (xtensa_operand_is_known_reg (isa, t1->opcode, i) == 0
              || xtensa_operand_is_known_reg (isa, t2->opcode, j) == 0)
              || xtensa_operand_is_known_reg (isa, t2->opcode, j) == 0)
            {
            {
              if (t1_inout == 'm' || t1_inout == 'o'
              if (t1_inout == 'm' || t1_inout == 'o'
                  || t2_inout == 'm' || t2_inout == 'o')
                  || t2_inout == 'm' || t2_inout == 'o')
                {
                {
                  conflict = 'a';
                  conflict = 'a';
                  continue;
                  continue;
                }
                }
            }
            }
 
 
          t1_base_reg = t1->tok[i].X_add_number;
          t1_base_reg = t1->tok[i].X_add_number;
          t1_last_reg = (t1_base_reg
          t1_last_reg = (t1_base_reg
                         + xtensa_operand_num_regs (isa, t1->opcode, i));
                         + xtensa_operand_num_regs (isa, t1->opcode, i));
 
 
          for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
          for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
            {
            {
              for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
              for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
                {
                {
                  if (t1_reg != t2_reg)
                  if (t1_reg != t2_reg)
                    continue;
                    continue;
 
 
                  if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
                  if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
                    {
                    {
                      conflict = 'a';
                      conflict = 'a';
                      continue;
                      continue;
                    }
                    }
 
 
                  if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
                  if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
                    {
                    {
                      conflict = 'a';
                      conflict = 'a';
                      continue;
                      continue;
                    }
                    }
 
 
                  if (t1_inout != 'i' && t2_inout != 'i')
                  if (t1_inout != 'i' && t2_inout != 'i')
                    return 'c';
                    return 'c';
                }
                }
            }
            }
        }
        }
    }
    }
 
 
  /* Check states.  */
  /* Check states.  */
  t1_states = xtensa_opcode_num_stateOperands (isa, t1->opcode);
  t1_states = xtensa_opcode_num_stateOperands (isa, t1->opcode);
  t2_states = xtensa_opcode_num_stateOperands (isa, t2->opcode);
  t2_states = xtensa_opcode_num_stateOperands (isa, t2->opcode);
  for (j = 0; j < t2_states; j++)
  for (j = 0; j < t2_states; j++)
    {
    {
      xtensa_state t2_so = xtensa_stateOperand_state (isa, t2->opcode, j);
      xtensa_state t2_so = xtensa_stateOperand_state (isa, t2->opcode, j);
      t2_inout = xtensa_stateOperand_inout (isa, t2->opcode, j);
      t2_inout = xtensa_stateOperand_inout (isa, t2->opcode, j);
      for (i = 0; i < t1_states; i++)
      for (i = 0; i < t1_states; i++)
        {
        {
          xtensa_state t1_so = xtensa_stateOperand_state (isa, t1->opcode, i);
          xtensa_state t1_so = xtensa_stateOperand_state (isa, t1->opcode, i);
          t1_inout = xtensa_stateOperand_inout (isa, t1->opcode, i);
          t1_inout = xtensa_stateOperand_inout (isa, t1->opcode, i);
          if (t1_so != t2_so || xtensa_state_is_shared_or (isa, t1_so) == 1)
          if (t1_so != t2_so || xtensa_state_is_shared_or (isa, t1_so) == 1)
            continue;
            continue;
 
 
          if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
          if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
            {
            {
              conflict = 'a';
              conflict = 'a';
              continue;
              continue;
            }
            }
 
 
          if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
          if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
            {
            {
              conflict = 'a';
              conflict = 'a';
              continue;
              continue;
            }
            }
 
 
          if (t1_inout != 'i' && t2_inout != 'i')
          if (t1_inout != 'i' && t2_inout != 'i')
            return 'd';
            return 'd';
        }
        }
    }
    }
 
 
  /* Check tieports.  */
  /* Check tieports.  */
  t1_interfaces = xtensa_opcode_num_interfaceOperands (isa, t1->opcode);
  t1_interfaces = xtensa_opcode_num_interfaceOperands (isa, t1->opcode);
  t2_interfaces = xtensa_opcode_num_interfaceOperands (isa, t2->opcode);
  t2_interfaces = xtensa_opcode_num_interfaceOperands (isa, t2->opcode);
  for (j = 0; j < t2_interfaces; j++)
  for (j = 0; j < t2_interfaces; j++)
    {
    {
      xtensa_interface t2_int
      xtensa_interface t2_int
        = xtensa_interfaceOperand_interface (isa, t2->opcode, j);
        = xtensa_interfaceOperand_interface (isa, t2->opcode, j);
      int t2_class = xtensa_interface_class_id (isa, t2_int);
      int t2_class = xtensa_interface_class_id (isa, t2_int);
 
 
      t2_inout = xtensa_interface_inout (isa, t2_int);
      t2_inout = xtensa_interface_inout (isa, t2_int);
      if (xtensa_interface_has_side_effect (isa, t2_int) == 1)
      if (xtensa_interface_has_side_effect (isa, t2_int) == 1)
        t2_volatile = TRUE;
        t2_volatile = TRUE;
 
 
      for (i = 0; i < t1_interfaces; i++)
      for (i = 0; i < t1_interfaces; i++)
        {
        {
          xtensa_interface t1_int
          xtensa_interface t1_int
            = xtensa_interfaceOperand_interface (isa, t1->opcode, j);
            = xtensa_interfaceOperand_interface (isa, t1->opcode, j);
          int t1_class = xtensa_interface_class_id (isa, t1_int);
          int t1_class = xtensa_interface_class_id (isa, t1_int);
 
 
          t1_inout = xtensa_interface_inout (isa, t1_int);
          t1_inout = xtensa_interface_inout (isa, t1_int);
          if (xtensa_interface_has_side_effect (isa, t1_int) == 1)
          if (xtensa_interface_has_side_effect (isa, t1_int) == 1)
            t1_volatile = TRUE;
            t1_volatile = TRUE;
 
 
          if (t1_volatile && t2_volatile && (t1_class == t2_class))
          if (t1_volatile && t2_volatile && (t1_class == t2_class))
            return 'f';
            return 'f';
 
 
          if (t1_int != t2_int)
          if (t1_int != t2_int)
            continue;
            continue;
 
 
          if (t2_inout == 'i' && t1_inout == 'o')
          if (t2_inout == 'i' && t1_inout == 'o')
            {
            {
              conflict = 'a';
              conflict = 'a';
              continue;
              continue;
            }
            }
 
 
          if (t1_inout == 'i' && t2_inout == 'o')
          if (t1_inout == 'i' && t2_inout == 'o')
            {
            {
              conflict = 'a';
              conflict = 'a';
              continue;
              continue;
            }
            }
 
 
          if (t1_inout != 'i' && t2_inout != 'i')
          if (t1_inout != 'i' && t2_inout != 'i')
            return 'e';
            return 'e';
        }
        }
    }
    }
 
 
  return conflict;
  return conflict;
}
}
 
 
 
 
static xtensa_format
static xtensa_format
xg_find_narrowest_format (vliw_insn *vinsn)
xg_find_narrowest_format (vliw_insn *vinsn)
{
{
  /* Right now we assume that the ops within the vinsn are properly
  /* Right now we assume that the ops within the vinsn are properly
     ordered for the slots that the programmer wanted them in.  In
     ordered for the slots that the programmer wanted them in.  In
     other words, we don't rearrange the ops in hopes of finding a
     other words, we don't rearrange the ops in hopes of finding a
     better format.  The scheduler handles that.  */
     better format.  The scheduler handles that.  */
 
 
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_format format;
  xtensa_format format;
  xtensa_opcode nop_opcode = xtensa_nop_opcode;
  xtensa_opcode nop_opcode = xtensa_nop_opcode;
 
 
  if (vinsn->num_slots == 1)
  if (vinsn->num_slots == 1)
    return xg_get_single_format (vinsn->slots[0].opcode);
    return xg_get_single_format (vinsn->slots[0].opcode);
 
 
  for (format = 0; format < xtensa_isa_num_formats (isa); format++)
  for (format = 0; format < xtensa_isa_num_formats (isa); format++)
    {
    {
      vliw_insn v_copy;
      vliw_insn v_copy;
      xg_copy_vinsn (&v_copy, vinsn);
      xg_copy_vinsn (&v_copy, vinsn);
      if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
      if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
        {
        {
          int slot;
          int slot;
          int fit = 0;
          int fit = 0;
          for (slot = 0; slot < v_copy.num_slots; slot++)
          for (slot = 0; slot < v_copy.num_slots; slot++)
            {
            {
              if (v_copy.slots[slot].opcode == nop_opcode)
              if (v_copy.slots[slot].opcode == nop_opcode)
                {
                {
                  v_copy.slots[slot].opcode =
                  v_copy.slots[slot].opcode =
                    xtensa_format_slot_nop_opcode (isa, format, slot);
                    xtensa_format_slot_nop_opcode (isa, format, slot);
                  v_copy.slots[slot].ntok = 0;
                  v_copy.slots[slot].ntok = 0;
                }
                }
 
 
              if (opcode_fits_format_slot (v_copy.slots[slot].opcode,
              if (opcode_fits_format_slot (v_copy.slots[slot].opcode,
                                           format, slot))
                                           format, slot))
                fit++;
                fit++;
              else if (v_copy.num_slots > 1)
              else if (v_copy.num_slots > 1)
                {
                {
                  TInsn widened;
                  TInsn widened;
                  /* Try the widened version.  */
                  /* Try the widened version.  */
                  if (!v_copy.slots[slot].keep_wide
                  if (!v_copy.slots[slot].keep_wide
                      && !v_copy.slots[slot].is_specific_opcode
                      && !v_copy.slots[slot].is_specific_opcode
                      && xg_is_single_relaxable_insn (&v_copy.slots[slot],
                      && xg_is_single_relaxable_insn (&v_copy.slots[slot],
                                                      &widened, TRUE)
                                                      &widened, TRUE)
                      && opcode_fits_format_slot (widened.opcode,
                      && opcode_fits_format_slot (widened.opcode,
                                                  format, slot))
                                                  format, slot))
                    {
                    {
                      v_copy.slots[slot] = widened;
                      v_copy.slots[slot] = widened;
                      fit++;
                      fit++;
                    }
                    }
                }
                }
            }
            }
          if (fit == v_copy.num_slots)
          if (fit == v_copy.num_slots)
            {
            {
              xg_copy_vinsn (vinsn, &v_copy);
              xg_copy_vinsn (vinsn, &v_copy);
              xtensa_format_encode (isa, format, vinsn->insnbuf);
              xtensa_format_encode (isa, format, vinsn->insnbuf);
              vinsn->format = format;
              vinsn->format = format;
              break;
              break;
            }
            }
        }
        }
    }
    }
 
 
  if (format == xtensa_isa_num_formats (isa))
  if (format == xtensa_isa_num_formats (isa))
    return XTENSA_UNDEFINED;
    return XTENSA_UNDEFINED;
 
 
  return format;
  return format;
}
}
 
 
 
 
/* Return the additional space needed in a frag
/* Return the additional space needed in a frag
   for possible relaxations of any ops in a VLIW insn.
   for possible relaxations of any ops in a VLIW insn.
   Also fill out the relaxations that might be required of
   Also fill out the relaxations that might be required of
   each tinsn in the vinsn.  */
   each tinsn in the vinsn.  */
 
 
static int
static int
relaxation_requirements (vliw_insn *vinsn, bfd_boolean *pfinish_frag)
relaxation_requirements (vliw_insn *vinsn, bfd_boolean *pfinish_frag)
{
{
  bfd_boolean finish_frag = FALSE;
  bfd_boolean finish_frag = FALSE;
  int extra_space = 0;
  int extra_space = 0;
  int slot;
  int slot;
 
 
  for (slot = 0; slot < vinsn->num_slots; slot++)
  for (slot = 0; slot < vinsn->num_slots; slot++)
    {
    {
      TInsn *tinsn = &vinsn->slots[slot];
      TInsn *tinsn = &vinsn->slots[slot];
      if (!tinsn_has_symbolic_operands (tinsn))
      if (!tinsn_has_symbolic_operands (tinsn))
        {
        {
          /* A narrow instruction could be widened later to help
          /* A narrow instruction could be widened later to help
             alignment issues.  */
             alignment issues.  */
          if (xg_is_single_relaxable_insn (tinsn, 0, TRUE)
          if (xg_is_single_relaxable_insn (tinsn, 0, TRUE)
              && !tinsn->is_specific_opcode
              && !tinsn->is_specific_opcode
              && vinsn->num_slots == 1)
              && vinsn->num_slots == 1)
            {
            {
              /* Difference in bytes between narrow and wide insns...  */
              /* Difference in bytes between narrow and wide insns...  */
              extra_space += 1;
              extra_space += 1;
              tinsn->subtype = RELAX_NARROW;
              tinsn->subtype = RELAX_NARROW;
            }
            }
        }
        }
      else
      else
        {
        {
          if (workaround_b_j_loop_end
          if (workaround_b_j_loop_end
              && tinsn->opcode == xtensa_jx_opcode
              && tinsn->opcode == xtensa_jx_opcode
              && use_transform ())
              && use_transform ())
            {
            {
              /* Add 2 of these.  */
              /* Add 2 of these.  */
              extra_space += 3; /* for the nop size */
              extra_space += 3; /* for the nop size */
              tinsn->subtype = RELAX_ADD_NOP_IF_PRE_LOOP_END;
              tinsn->subtype = RELAX_ADD_NOP_IF_PRE_LOOP_END;
            }
            }
 
 
          /* Need to assemble it with space for the relocation.  */
          /* Need to assemble it with space for the relocation.  */
          if (xg_is_relaxable_insn (tinsn, 0)
          if (xg_is_relaxable_insn (tinsn, 0)
              && !tinsn->is_specific_opcode)
              && !tinsn->is_specific_opcode)
            {
            {
              int max_size = xg_get_max_insn_widen_size (tinsn->opcode);
              int max_size = xg_get_max_insn_widen_size (tinsn->opcode);
              int max_literal_size =
              int max_literal_size =
                xg_get_max_insn_widen_literal_size (tinsn->opcode);
                xg_get_max_insn_widen_literal_size (tinsn->opcode);
 
 
              tinsn->literal_space = max_literal_size;
              tinsn->literal_space = max_literal_size;
 
 
              tinsn->subtype = RELAX_IMMED;
              tinsn->subtype = RELAX_IMMED;
              extra_space += max_size;
              extra_space += max_size;
            }
            }
          else
          else
            {
            {
              /* A fix record will be added for this instruction prior
              /* A fix record will be added for this instruction prior
                 to relaxation, so make it end the frag.  */
                 to relaxation, so make it end the frag.  */
              finish_frag = TRUE;
              finish_frag = TRUE;
            }
            }
        }
        }
    }
    }
  *pfinish_frag = finish_frag;
  *pfinish_frag = finish_frag;
  return extra_space;
  return extra_space;
}
}
 
 
 
 
static void
static void
bundle_tinsn (TInsn *tinsn, vliw_insn *vinsn)
bundle_tinsn (TInsn *tinsn, vliw_insn *vinsn)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int slot, chosen_slot;
  int slot, chosen_slot;
 
 
  vinsn->format = xg_get_single_format (tinsn->opcode);
  vinsn->format = xg_get_single_format (tinsn->opcode);
  gas_assert (vinsn->format != XTENSA_UNDEFINED);
  gas_assert (vinsn->format != XTENSA_UNDEFINED);
  vinsn->num_slots = xtensa_format_num_slots (isa, vinsn->format);
  vinsn->num_slots = xtensa_format_num_slots (isa, vinsn->format);
 
 
  chosen_slot = xg_get_single_slot (tinsn->opcode);
  chosen_slot = xg_get_single_slot (tinsn->opcode);
  for (slot = 0; slot < vinsn->num_slots; slot++)
  for (slot = 0; slot < vinsn->num_slots; slot++)
    {
    {
      if (slot == chosen_slot)
      if (slot == chosen_slot)
        vinsn->slots[slot] = *tinsn;
        vinsn->slots[slot] = *tinsn;
      else
      else
        {
        {
          vinsn->slots[slot].opcode =
          vinsn->slots[slot].opcode =
            xtensa_format_slot_nop_opcode (isa, vinsn->format, slot);
            xtensa_format_slot_nop_opcode (isa, vinsn->format, slot);
          vinsn->slots[slot].ntok = 0;
          vinsn->slots[slot].ntok = 0;
          vinsn->slots[slot].insn_type = ITYPE_INSN;
          vinsn->slots[slot].insn_type = ITYPE_INSN;
        }
        }
    }
    }
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
emit_single_op (TInsn *orig_insn)
emit_single_op (TInsn *orig_insn)
{
{
  int i;
  int i;
  IStack istack;                /* put instructions into here */
  IStack istack;                /* put instructions into here */
  symbolS *lit_sym = NULL;
  symbolS *lit_sym = NULL;
  symbolS *label_sym = NULL;
  symbolS *label_sym = NULL;
 
 
  istack_init (&istack);
  istack_init (&istack);
 
 
  /* Special-case for "movi aX, foo" which is guaranteed to need relaxing.
  /* Special-case for "movi aX, foo" which is guaranteed to need relaxing.
     Because the scheduling and bundling characteristics of movi and
     Because the scheduling and bundling characteristics of movi and
     l32r or const16 are so different, we can do much better if we relax
     l32r or const16 are so different, we can do much better if we relax
     it prior to scheduling and bundling, rather than after.  */
     it prior to scheduling and bundling, rather than after.  */
  if ((orig_insn->opcode == xtensa_movi_opcode
  if ((orig_insn->opcode == xtensa_movi_opcode
       || orig_insn->opcode == xtensa_movi_n_opcode)
       || orig_insn->opcode == xtensa_movi_n_opcode)
      && !cur_vinsn.inside_bundle
      && !cur_vinsn.inside_bundle
      && (orig_insn->tok[1].X_op == O_symbol
      && (orig_insn->tok[1].X_op == O_symbol
          || orig_insn->tok[1].X_op == O_pltrel
          || orig_insn->tok[1].X_op == O_pltrel
          || orig_insn->tok[1].X_op == O_tlsfunc
          || orig_insn->tok[1].X_op == O_tlsfunc
          || orig_insn->tok[1].X_op == O_tlsarg
          || orig_insn->tok[1].X_op == O_tlsarg
          || orig_insn->tok[1].X_op == O_tpoff
          || orig_insn->tok[1].X_op == O_tpoff
          || orig_insn->tok[1].X_op == O_dtpoff)
          || orig_insn->tok[1].X_op == O_dtpoff)
      && !orig_insn->is_specific_opcode && use_transform ())
      && !orig_insn->is_specific_opcode && use_transform ())
    xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
    xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
  else
  else
    if (xg_expand_assembly_insn (&istack, orig_insn))
    if (xg_expand_assembly_insn (&istack, orig_insn))
      return TRUE;
      return TRUE;
 
 
  for (i = 0; i < istack.ninsn; i++)
  for (i = 0; i < istack.ninsn; i++)
    {
    {
      TInsn *insn = &istack.insn[i];
      TInsn *insn = &istack.insn[i];
      switch (insn->insn_type)
      switch (insn->insn_type)
        {
        {
        case ITYPE_LITERAL:
        case ITYPE_LITERAL:
          gas_assert (lit_sym == NULL);
          gas_assert (lit_sym == NULL);
          lit_sym = xg_assemble_literal (insn);
          lit_sym = xg_assemble_literal (insn);
          break;
          break;
        case ITYPE_LABEL:
        case ITYPE_LABEL:
          {
          {
            static int relaxed_sym_idx = 0;
            static int relaxed_sym_idx = 0;
            char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
            char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
            sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
            sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
            colon (label);
            colon (label);
            gas_assert (label_sym == NULL);
            gas_assert (label_sym == NULL);
            label_sym = symbol_find_or_make (label);
            label_sym = symbol_find_or_make (label);
            gas_assert (label_sym);
            gas_assert (label_sym);
            free (label);
            free (label);
          }
          }
          break;
          break;
        case ITYPE_INSN:
        case ITYPE_INSN:
          {
          {
            vliw_insn v;
            vliw_insn v;
            if (lit_sym)
            if (lit_sym)
              xg_resolve_literals (insn, lit_sym);
              xg_resolve_literals (insn, lit_sym);
            if (label_sym)
            if (label_sym)
              xg_resolve_labels (insn, label_sym);
              xg_resolve_labels (insn, label_sym);
            xg_init_vinsn (&v);
            xg_init_vinsn (&v);
            bundle_tinsn (insn, &v);
            bundle_tinsn (insn, &v);
            finish_vinsn (&v);
            finish_vinsn (&v);
            xg_free_vinsn (&v);
            xg_free_vinsn (&v);
          }
          }
          break;
          break;
        default:
        default:
          gas_assert (0);
          gas_assert (0);
          break;
          break;
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static int
static int
total_frag_text_expansion (fragS *fragP)
total_frag_text_expansion (fragS *fragP)
{
{
  int slot;
  int slot;
  int total_expansion = 0;
  int total_expansion = 0;
 
 
  for (slot = 0; slot < config_max_slots; slot++)
  for (slot = 0; slot < config_max_slots; slot++)
    total_expansion += fragP->tc_frag_data.text_expansion[slot];
    total_expansion += fragP->tc_frag_data.text_expansion[slot];
 
 
  return total_expansion;
  return total_expansion;
}
}
 
 
 
 
/* Emit a vliw instruction to the current fragment.  */
/* Emit a vliw instruction to the current fragment.  */
 
 
static void
static void
xg_assemble_vliw_tokens (vliw_insn *vinsn)
xg_assemble_vliw_tokens (vliw_insn *vinsn)
{
{
  bfd_boolean finish_frag;
  bfd_boolean finish_frag;
  bfd_boolean is_jump = FALSE;
  bfd_boolean is_jump = FALSE;
  bfd_boolean is_branch = FALSE;
  bfd_boolean is_branch = FALSE;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int insn_size;
  int insn_size;
  int extra_space;
  int extra_space;
  char *f = NULL;
  char *f = NULL;
  int slot;
  int slot;
  struct dwarf2_line_info debug_line;
  struct dwarf2_line_info debug_line;
  bfd_boolean loc_directive_seen = FALSE;
  bfd_boolean loc_directive_seen = FALSE;
  TInsn *tinsn;
  TInsn *tinsn;
 
 
  memset (&debug_line, 0, sizeof (struct dwarf2_line_info));
  memset (&debug_line, 0, sizeof (struct dwarf2_line_info));
 
 
  if (generating_literals)
  if (generating_literals)
    {
    {
      static int reported = 0;
      static int reported = 0;
      if (reported < 4)
      if (reported < 4)
        as_bad_where (frag_now->fr_file, frag_now->fr_line,
        as_bad_where (frag_now->fr_file, frag_now->fr_line,
                      _("cannot assemble into a literal fragment"));
                      _("cannot assemble into a literal fragment"));
      if (reported == 3)
      if (reported == 3)
        as_bad (_("..."));
        as_bad (_("..."));
      reported++;
      reported++;
      return;
      return;
    }
    }
 
 
  if (frag_now_fix () != 0
  if (frag_now_fix () != 0
      && (! frag_now->tc_frag_data.is_insn
      && (! frag_now->tc_frag_data.is_insn
          || (vinsn_has_specific_opcodes (vinsn) && use_transform ())
          || (vinsn_has_specific_opcodes (vinsn) && use_transform ())
          || !use_transform () != frag_now->tc_frag_data.is_no_transform
          || !use_transform () != frag_now->tc_frag_data.is_no_transform
          || (directive_state[directive_longcalls]
          || (directive_state[directive_longcalls]
              != frag_now->tc_frag_data.use_longcalls)
              != frag_now->tc_frag_data.use_longcalls)
          || (directive_state[directive_absolute_literals]
          || (directive_state[directive_absolute_literals]
              != frag_now->tc_frag_data.use_absolute_literals)))
              != frag_now->tc_frag_data.use_absolute_literals)))
    {
    {
      frag_wane (frag_now);
      frag_wane (frag_now);
      frag_new (0);
      frag_new (0);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
    }
    }
 
 
  if (workaround_a0_b_retw
  if (workaround_a0_b_retw
      && vinsn->num_slots == 1
      && vinsn->num_slots == 1
      && (get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
      && (get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
      && xtensa_opcode_is_branch (isa, vinsn->slots[0].opcode) == 1
      && xtensa_opcode_is_branch (isa, vinsn->slots[0].opcode) == 1
      && use_transform ())
      && use_transform ())
    {
    {
      has_a0_b_retw = TRUE;
      has_a0_b_retw = TRUE;
 
 
      /* Mark this fragment with the special RELAX_ADD_NOP_IF_A0_B_RETW.
      /* Mark this fragment with the special RELAX_ADD_NOP_IF_A0_B_RETW.
         After the first assembly pass we will check all of them and
         After the first assembly pass we will check all of them and
         add a nop if needed.  */
         add a nop if needed.  */
      frag_now->tc_frag_data.is_insn = TRUE;
      frag_now->tc_frag_data.is_insn = TRUE;
      frag_var (rs_machine_dependent, 4, 4,
      frag_var (rs_machine_dependent, 4, 4,
                RELAX_ADD_NOP_IF_A0_B_RETW,
                RELAX_ADD_NOP_IF_A0_B_RETW,
                frag_now->fr_symbol,
                frag_now->fr_symbol,
                frag_now->fr_offset,
                frag_now->fr_offset,
                NULL);
                NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      frag_now->tc_frag_data.is_insn = TRUE;
      frag_now->tc_frag_data.is_insn = TRUE;
      frag_var (rs_machine_dependent, 4, 4,
      frag_var (rs_machine_dependent, 4, 4,
                RELAX_ADD_NOP_IF_A0_B_RETW,
                RELAX_ADD_NOP_IF_A0_B_RETW,
                frag_now->fr_symbol,
                frag_now->fr_symbol,
                frag_now->fr_offset,
                frag_now->fr_offset,
                NULL);
                NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
    }
    }
 
 
  for (slot = 0; slot < vinsn->num_slots; slot++)
  for (slot = 0; slot < vinsn->num_slots; slot++)
    {
    {
      tinsn = &vinsn->slots[slot];
      tinsn = &vinsn->slots[slot];
 
 
      /* See if the instruction implies an aligned section.  */
      /* See if the instruction implies an aligned section.  */
      if (xtensa_opcode_is_loop (isa, tinsn->opcode) == 1)
      if (xtensa_opcode_is_loop (isa, tinsn->opcode) == 1)
        record_alignment (now_seg, 2);
        record_alignment (now_seg, 2);
 
 
      /* Determine the best line number for debug info.  */
      /* Determine the best line number for debug info.  */
      if ((tinsn->loc_directive_seen || !loc_directive_seen)
      if ((tinsn->loc_directive_seen || !loc_directive_seen)
          && (tinsn->debug_line.filenum != debug_line.filenum
          && (tinsn->debug_line.filenum != debug_line.filenum
              || tinsn->debug_line.line < debug_line.line
              || tinsn->debug_line.line < debug_line.line
              || tinsn->debug_line.column < debug_line.column))
              || tinsn->debug_line.column < debug_line.column))
        debug_line = tinsn->debug_line;
        debug_line = tinsn->debug_line;
      if (tinsn->loc_directive_seen)
      if (tinsn->loc_directive_seen)
        loc_directive_seen = TRUE;
        loc_directive_seen = TRUE;
    }
    }
 
 
  /* Special cases for instructions that force an alignment... */
  /* Special cases for instructions that force an alignment... */
  /* None of these opcodes are bundle-able.  */
  /* None of these opcodes are bundle-able.  */
  if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1)
  if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1)
    {
    {
      int max_fill;
      int max_fill;
 
 
      /* Remember the symbol that marks the end of the loop in the frag
      /* Remember the symbol that marks the end of the loop in the frag
         that marks the start of the loop.  This way we can easily find
         that marks the start of the loop.  This way we can easily find
         the end of the loop at the beginning, without adding special code
         the end of the loop at the beginning, without adding special code
         to mark the loop instructions themselves.  */
         to mark the loop instructions themselves.  */
      symbolS *target_sym = NULL;
      symbolS *target_sym = NULL;
      if (vinsn->slots[0].tok[1].X_op == O_symbol)
      if (vinsn->slots[0].tok[1].X_op == O_symbol)
        target_sym = vinsn->slots[0].tok[1].X_add_symbol;
        target_sym = vinsn->slots[0].tok[1].X_add_symbol;
 
 
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      frag_now->tc_frag_data.is_insn = TRUE;
      frag_now->tc_frag_data.is_insn = TRUE;
 
 
      max_fill = get_text_align_max_fill_size
      max_fill = get_text_align_max_fill_size
        (get_text_align_power (xtensa_fetch_width),
        (get_text_align_power (xtensa_fetch_width),
         TRUE, frag_now->tc_frag_data.is_no_density);
         TRUE, frag_now->tc_frag_data.is_no_density);
 
 
      if (use_transform ())
      if (use_transform ())
        frag_var (rs_machine_dependent, max_fill, max_fill,
        frag_var (rs_machine_dependent, max_fill, max_fill,
                  RELAX_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
                  RELAX_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
      else
      else
        frag_var (rs_machine_dependent, 0, 0,
        frag_var (rs_machine_dependent, 0, 0,
                  RELAX_CHECK_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
                  RELAX_CHECK_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
    }
    }
 
 
  if (vinsn->slots[0].opcode == xtensa_entry_opcode
  if (vinsn->slots[0].opcode == xtensa_entry_opcode
      && !vinsn->slots[0].is_specific_opcode)
      && !vinsn->slots[0].is_specific_opcode)
    {
    {
      xtensa_mark_literal_pool_location ();
      xtensa_mark_literal_pool_location ();
      xtensa_move_labels (frag_now, 0);
      xtensa_move_labels (frag_now, 0);
      frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
      frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
    }
    }
 
 
  if (vinsn->num_slots == 1)
  if (vinsn->num_slots == 1)
    {
    {
      if (workaround_a0_b_retw && use_transform ())
      if (workaround_a0_b_retw && use_transform ())
        set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
        set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
                             is_register_writer (&vinsn->slots[0], "a", 0));
                             is_register_writer (&vinsn->slots[0], "a", 0));
 
 
      set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
      set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
                           is_bad_loopend_opcode (&vinsn->slots[0]));
                           is_bad_loopend_opcode (&vinsn->slots[0]));
    }
    }
  else
  else
    set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND, FALSE);
    set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND, FALSE);
 
 
  insn_size = xtensa_format_length (isa, vinsn->format);
  insn_size = xtensa_format_length (isa, vinsn->format);
 
 
  extra_space = relaxation_requirements (vinsn, &finish_frag);
  extra_space = relaxation_requirements (vinsn, &finish_frag);
 
 
  /* vinsn_to_insnbuf will produce the error.  */
  /* vinsn_to_insnbuf will produce the error.  */
  if (vinsn->format != XTENSA_UNDEFINED)
  if (vinsn->format != XTENSA_UNDEFINED)
    {
    {
      f = frag_more (insn_size + extra_space);
      f = frag_more (insn_size + extra_space);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      frag_now->tc_frag_data.is_insn = TRUE;
      frag_now->tc_frag_data.is_insn = TRUE;
    }
    }
 
 
  vinsn_to_insnbuf (vinsn, f, frag_now, FALSE);
  vinsn_to_insnbuf (vinsn, f, frag_now, FALSE);
  if (vinsn->format == XTENSA_UNDEFINED)
  if (vinsn->format == XTENSA_UNDEFINED)
    return;
    return;
 
 
  xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, (unsigned char *) f, 0);
  xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, (unsigned char *) f, 0);
 
 
  if (debug_type == DEBUG_DWARF2 || loc_directive_seen)
  if (debug_type == DEBUG_DWARF2 || loc_directive_seen)
    dwarf2_gen_line_info (frag_now_fix () - (insn_size + extra_space),
    dwarf2_gen_line_info (frag_now_fix () - (insn_size + extra_space),
                          &debug_line);
                          &debug_line);
 
 
  for (slot = 0; slot < vinsn->num_slots; slot++)
  for (slot = 0; slot < vinsn->num_slots; slot++)
    {
    {
      tinsn = &vinsn->slots[slot];
      tinsn = &vinsn->slots[slot];
      frag_now->tc_frag_data.slot_subtypes[slot] = tinsn->subtype;
      frag_now->tc_frag_data.slot_subtypes[slot] = tinsn->subtype;
      frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
      frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
      frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
      frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
      frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
      frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
      if (tinsn->literal_space != 0)
      if (tinsn->literal_space != 0)
        xg_assemble_literal_space (tinsn->literal_space, slot);
        xg_assemble_literal_space (tinsn->literal_space, slot);
      frag_now->tc_frag_data.free_reg[slot] = tinsn->extra_arg;
      frag_now->tc_frag_data.free_reg[slot] = tinsn->extra_arg;
 
 
      if (tinsn->subtype == RELAX_NARROW)
      if (tinsn->subtype == RELAX_NARROW)
        gas_assert (vinsn->num_slots == 1);
        gas_assert (vinsn->num_slots == 1);
      if (xtensa_opcode_is_jump (isa, tinsn->opcode) == 1)
      if (xtensa_opcode_is_jump (isa, tinsn->opcode) == 1)
        is_jump = TRUE;
        is_jump = TRUE;
      if (xtensa_opcode_is_branch (isa, tinsn->opcode) == 1)
      if (xtensa_opcode_is_branch (isa, tinsn->opcode) == 1)
        is_branch = TRUE;
        is_branch = TRUE;
 
 
      if (tinsn->subtype || tinsn->symbol || tinsn->offset
      if (tinsn->subtype || tinsn->symbol || tinsn->offset
          || tinsn->literal_frag || is_jump || is_branch)
          || tinsn->literal_frag || is_jump || is_branch)
        finish_frag = TRUE;
        finish_frag = TRUE;
    }
    }
 
 
  if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
  if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
    frag_now->tc_frag_data.is_specific_opcode = TRUE;
    frag_now->tc_frag_data.is_specific_opcode = TRUE;
 
 
  if (finish_frag)
  if (finish_frag)
    {
    {
      frag_variant (rs_machine_dependent,
      frag_variant (rs_machine_dependent,
                    extra_space, extra_space, RELAX_SLOTS,
                    extra_space, extra_space, RELAX_SLOTS,
                    frag_now->fr_symbol, frag_now->fr_offset, f);
                    frag_now->fr_symbol, frag_now->fr_offset, f);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
    }
    }
 
 
  /* Special cases for loops:
  /* Special cases for loops:
     close_loop_end should be inserted AFTER short_loop.
     close_loop_end should be inserted AFTER short_loop.
     Make sure that CLOSE loops are processed BEFORE short_loops
     Make sure that CLOSE loops are processed BEFORE short_loops
     when converting them.  */
     when converting them.  */
 
 
  /* "short_loop": Add a NOP if the loop is < 4 bytes.  */
  /* "short_loop": Add a NOP if the loop is < 4 bytes.  */
  if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1
  if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1
      && !vinsn->slots[0].is_specific_opcode)
      && !vinsn->slots[0].is_specific_opcode)
    {
    {
      if (workaround_short_loop && use_transform ())
      if (workaround_short_loop && use_transform ())
        {
        {
          maybe_has_short_loop = TRUE;
          maybe_has_short_loop = TRUE;
          frag_now->tc_frag_data.is_insn = TRUE;
          frag_now->tc_frag_data.is_insn = TRUE;
          frag_var (rs_machine_dependent, 4, 4,
          frag_var (rs_machine_dependent, 4, 4,
                    RELAX_ADD_NOP_IF_SHORT_LOOP,
                    RELAX_ADD_NOP_IF_SHORT_LOOP,
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
          frag_now->tc_frag_data.is_insn = TRUE;
          frag_now->tc_frag_data.is_insn = TRUE;
          frag_var (rs_machine_dependent, 4, 4,
          frag_var (rs_machine_dependent, 4, 4,
                    RELAX_ADD_NOP_IF_SHORT_LOOP,
                    RELAX_ADD_NOP_IF_SHORT_LOOP,
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
        }
        }
 
 
      /* "close_loop_end": Add up to 12 bytes of NOPs to keep a
      /* "close_loop_end": Add up to 12 bytes of NOPs to keep a
         loop at least 12 bytes away from another loop's end.  */
         loop at least 12 bytes away from another loop's end.  */
      if (workaround_close_loop_end && use_transform ())
      if (workaround_close_loop_end && use_transform ())
        {
        {
          maybe_has_close_loop_end = TRUE;
          maybe_has_close_loop_end = TRUE;
          frag_now->tc_frag_data.is_insn = TRUE;
          frag_now->tc_frag_data.is_insn = TRUE;
          frag_var (rs_machine_dependent, 12, 12,
          frag_var (rs_machine_dependent, 12, 12,
                    RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
                    RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
        }
        }
    }
    }
 
 
  if (use_transform ())
  if (use_transform ())
    {
    {
      if (is_jump)
      if (is_jump)
        {
        {
          gas_assert (finish_frag);
          gas_assert (finish_frag);
          frag_var (rs_machine_dependent,
          frag_var (rs_machine_dependent,
                    UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
                    UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
                    RELAX_UNREACHABLE,
                    RELAX_UNREACHABLE,
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
          xtensa_set_frag_assembly_state (frag_now);
          xtensa_set_frag_assembly_state (frag_now);
        }
        }
      else if (is_branch && do_align_targets ())
      else if (is_branch && do_align_targets ())
        {
        {
          gas_assert (finish_frag);
          gas_assert (finish_frag);
          frag_var (rs_machine_dependent,
          frag_var (rs_machine_dependent,
                    UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
                    UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
                    RELAX_MAYBE_UNREACHABLE,
                    RELAX_MAYBE_UNREACHABLE,
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
          xtensa_set_frag_assembly_state (frag_now);
          xtensa_set_frag_assembly_state (frag_now);
          frag_var (rs_machine_dependent,
          frag_var (rs_machine_dependent,
                    0, 0,
                    0, 0,
                    RELAX_MAYBE_DESIRE_ALIGN,
                    RELAX_MAYBE_DESIRE_ALIGN,
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
                    frag_now->fr_symbol, frag_now->fr_offset, NULL);
          xtensa_set_frag_assembly_state (frag_now);
          xtensa_set_frag_assembly_state (frag_now);
        }
        }
    }
    }
 
 
  /* Now, if the original opcode was a call...  */
  /* Now, if the original opcode was a call...  */
  if (do_align_targets ()
  if (do_align_targets ()
      && xtensa_opcode_is_call (isa, vinsn->slots[0].opcode) == 1)
      && xtensa_opcode_is_call (isa, vinsn->slots[0].opcode) == 1)
    {
    {
      float freq = get_subseg_total_freq (now_seg, now_subseg);
      float freq = get_subseg_total_freq (now_seg, now_subseg);
      frag_now->tc_frag_data.is_insn = TRUE;
      frag_now->tc_frag_data.is_insn = TRUE;
      frag_var (rs_machine_dependent, 4, (int) freq, RELAX_DESIRE_ALIGN,
      frag_var (rs_machine_dependent, 4, (int) freq, RELAX_DESIRE_ALIGN,
                frag_now->fr_symbol, frag_now->fr_offset, NULL);
                frag_now->fr_symbol, frag_now->fr_offset, NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
    }
    }
 
 
  if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
  if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
    {
    {
      frag_wane (frag_now);
      frag_wane (frag_now);
      frag_new (0);
      frag_new (0);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
    }
    }
}
}
 
 


/* xtensa_end and helper functions.  */
/* xtensa_end and helper functions.  */
 
 
static void xtensa_cleanup_align_frags (void);
static void xtensa_cleanup_align_frags (void);
static void xtensa_fix_target_frags (void);
static void xtensa_fix_target_frags (void);
static void xtensa_mark_narrow_branches (void);
static void xtensa_mark_narrow_branches (void);
static void xtensa_mark_zcl_first_insns (void);
static void xtensa_mark_zcl_first_insns (void);
static void xtensa_mark_difference_of_two_symbols (void);
static void xtensa_mark_difference_of_two_symbols (void);
static void xtensa_fix_a0_b_retw_frags (void);
static void xtensa_fix_a0_b_retw_frags (void);
static void xtensa_fix_b_j_loop_end_frags (void);
static void xtensa_fix_b_j_loop_end_frags (void);
static void xtensa_fix_close_loop_end_frags (void);
static void xtensa_fix_close_loop_end_frags (void);
static void xtensa_fix_short_loop_frags (void);
static void xtensa_fix_short_loop_frags (void);
static void xtensa_sanity_check (void);
static void xtensa_sanity_check (void);
static void xtensa_add_config_info (void);
static void xtensa_add_config_info (void);
 
 
void
void
xtensa_end (void)
xtensa_end (void)
{
{
  directive_balance ();
  directive_balance ();
  xtensa_flush_pending_output ();
  xtensa_flush_pending_output ();
 
 
  past_xtensa_end = TRUE;
  past_xtensa_end = TRUE;
 
 
  xtensa_move_literals ();
  xtensa_move_literals ();
 
 
  xtensa_reorder_segments ();
  xtensa_reorder_segments ();
  xtensa_cleanup_align_frags ();
  xtensa_cleanup_align_frags ();
  xtensa_fix_target_frags ();
  xtensa_fix_target_frags ();
  if (workaround_a0_b_retw && has_a0_b_retw)
  if (workaround_a0_b_retw && has_a0_b_retw)
    xtensa_fix_a0_b_retw_frags ();
    xtensa_fix_a0_b_retw_frags ();
  if (workaround_b_j_loop_end)
  if (workaround_b_j_loop_end)
    xtensa_fix_b_j_loop_end_frags ();
    xtensa_fix_b_j_loop_end_frags ();
 
 
  /* "close_loop_end" should be processed BEFORE "short_loop".  */
  /* "close_loop_end" should be processed BEFORE "short_loop".  */
  if (workaround_close_loop_end && maybe_has_close_loop_end)
  if (workaround_close_loop_end && maybe_has_close_loop_end)
    xtensa_fix_close_loop_end_frags ();
    xtensa_fix_close_loop_end_frags ();
 
 
  if (workaround_short_loop && maybe_has_short_loop)
  if (workaround_short_loop && maybe_has_short_loop)
    xtensa_fix_short_loop_frags ();
    xtensa_fix_short_loop_frags ();
  if (align_targets)
  if (align_targets)
    xtensa_mark_narrow_branches ();
    xtensa_mark_narrow_branches ();
  xtensa_mark_zcl_first_insns ();
  xtensa_mark_zcl_first_insns ();
 
 
  xtensa_sanity_check ();
  xtensa_sanity_check ();
 
 
  xtensa_add_config_info ();
  xtensa_add_config_info ();
}
}
 
 
 
 
static void
static void
xtensa_cleanup_align_frags (void)
xtensa_cleanup_align_frags (void)
{
{
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if ((fragP->fr_type == rs_align
            if ((fragP->fr_type == rs_align
                 || fragP->fr_type == rs_align_code
                 || fragP->fr_type == rs_align_code
                 || (fragP->fr_type == rs_machine_dependent
                 || (fragP->fr_type == rs_machine_dependent
                     && (fragP->fr_subtype == RELAX_DESIRE_ALIGN
                     && (fragP->fr_subtype == RELAX_DESIRE_ALIGN
                         || fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
                         || fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
                && fragP->fr_fix == 0)
                && fragP->fr_fix == 0)
              {
              {
                fragS *next = fragP->fr_next;
                fragS *next = fragP->fr_next;
 
 
                while (next
                while (next
                       && next->fr_fix == 0
                       && next->fr_fix == 0
                       && next->fr_type == rs_machine_dependent
                       && next->fr_type == rs_machine_dependent
                       && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
                       && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
                  {
                  {
                    frag_wane (next);
                    frag_wane (next);
                    next = next->fr_next;
                    next = next->fr_next;
                  }
                  }
              }
              }
            /* If we don't widen branch targets, then they
            /* If we don't widen branch targets, then they
               will be easier to align.  */
               will be easier to align.  */
            if (fragP->tc_frag_data.is_branch_target
            if (fragP->tc_frag_data.is_branch_target
                && fragP->fr_opcode == fragP->fr_literal
                && fragP->fr_opcode == fragP->fr_literal
                && fragP->fr_type == rs_machine_dependent
                && fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_SLOTS
                && fragP->fr_subtype == RELAX_SLOTS
                && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
                && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
              frag_wane (fragP);
              frag_wane (fragP);
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_UNREACHABLE)
                && fragP->fr_subtype == RELAX_UNREACHABLE)
              fragP->tc_frag_data.is_unreachable = TRUE;
              fragP->tc_frag_data.is_unreachable = TRUE;
          }
          }
      }
      }
}
}
 
 
 
 
/* Re-process all of the fragments looking to convert all of the
/* Re-process all of the fragments looking to convert all of the
   RELAX_DESIRE_ALIGN_IF_TARGET fragments.  If there is a branch
   RELAX_DESIRE_ALIGN_IF_TARGET fragments.  If there is a branch
   target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
   target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
   Otherwise, convert to a .fill 0.  */
   Otherwise, convert to a .fill 0.  */
 
 
static void
static void
xtensa_fix_target_frags (void)
xtensa_fix_target_frags (void)
{
{
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  /* When this routine is called, all of the subsections are still intact
  /* When this routine is called, all of the subsections are still intact
     so we walk over subsections instead of sections.  */
     so we walk over subsections instead of sections.  */
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
 
 
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
                && fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
              {
              {
                if (next_frag_is_branch_target (fragP))
                if (next_frag_is_branch_target (fragP))
                  fragP->fr_subtype = RELAX_DESIRE_ALIGN;
                  fragP->fr_subtype = RELAX_DESIRE_ALIGN;
                else
                else
                  frag_wane (fragP);
                  frag_wane (fragP);
              }
              }
          }
          }
      }
      }
}
}
 
 
 
 
static bfd_boolean is_narrow_branch_guaranteed_in_range (fragS *, TInsn *);
static bfd_boolean is_narrow_branch_guaranteed_in_range (fragS *, TInsn *);
 
 
static void
static void
xtensa_mark_narrow_branches (void)
xtensa_mark_narrow_branches (void)
{
{
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_SLOTS
                && fragP->fr_subtype == RELAX_SLOTS
                && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
                && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
              {
              {
                vliw_insn vinsn;
                vliw_insn vinsn;
 
 
                vinsn_from_chars (&vinsn, fragP->fr_opcode);
                vinsn_from_chars (&vinsn, fragP->fr_opcode);
                tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
                tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
 
 
                if (vinsn.num_slots == 1
                if (vinsn.num_slots == 1
                    && xtensa_opcode_is_branch (xtensa_default_isa,
                    && xtensa_opcode_is_branch (xtensa_default_isa,
                                                vinsn.slots[0].opcode) == 1
                                                vinsn.slots[0].opcode) == 1
                    && xg_get_single_size (vinsn.slots[0].opcode) == 2
                    && xg_get_single_size (vinsn.slots[0].opcode) == 2
                    && is_narrow_branch_guaranteed_in_range (fragP,
                    && is_narrow_branch_guaranteed_in_range (fragP,
                                                             &vinsn.slots[0]))
                                                             &vinsn.slots[0]))
                  {
                  {
                    fragP->fr_subtype = RELAX_SLOTS;
                    fragP->fr_subtype = RELAX_SLOTS;
                    fragP->tc_frag_data.slot_subtypes[0] = RELAX_NARROW;
                    fragP->tc_frag_data.slot_subtypes[0] = RELAX_NARROW;
                    fragP->tc_frag_data.is_aligning_branch = 1;
                    fragP->tc_frag_data.is_aligning_branch = 1;
                  }
                  }
              }
              }
          }
          }
      }
      }
}
}
 
 
 
 
/* A branch is typically widened only when its target is out of
/* A branch is typically widened only when its target is out of
   range.  However, we would like to widen them to align a subsequent
   range.  However, we would like to widen them to align a subsequent
   branch target when possible.
   branch target when possible.
 
 
   Because the branch relaxation code is so convoluted, the optimal solution
   Because the branch relaxation code is so convoluted, the optimal solution
   (combining the two cases) is difficult to get right in all circumstances.
   (combining the two cases) is difficult to get right in all circumstances.
   We therefore go with an "almost as good" solution, where we only
   We therefore go with an "almost as good" solution, where we only
   use for alignment narrow branches that definitely will not expand to a
   use for alignment narrow branches that definitely will not expand to a
   jump and a branch.  These functions find and mark these cases.  */
   jump and a branch.  These functions find and mark these cases.  */
 
 
/* The range in bytes of BNEZ.N and BEQZ.N.  The target operand is encoded
/* The range in bytes of BNEZ.N and BEQZ.N.  The target operand is encoded
   as PC + 4 + imm6, where imm6 is a 6-bit immediate ranging from 0 to 63.
   as PC + 4 + imm6, where imm6 is a 6-bit immediate ranging from 0 to 63.
   We start counting beginning with the frag after the 2-byte branch, so the
   We start counting beginning with the frag after the 2-byte branch, so the
   maximum offset is (4 - 2) + 63 = 65.  */
   maximum offset is (4 - 2) + 63 = 65.  */
#define MAX_IMMED6 65
#define MAX_IMMED6 65
 
 
static offsetT unrelaxed_frag_max_size (fragS *);
static offsetT unrelaxed_frag_max_size (fragS *);
 
 
static bfd_boolean
static bfd_boolean
is_narrow_branch_guaranteed_in_range (fragS *fragP, TInsn *tinsn)
is_narrow_branch_guaranteed_in_range (fragS *fragP, TInsn *tinsn)
{
{
  const expressionS *expr = &tinsn->tok[1];
  const expressionS *expr = &tinsn->tok[1];
  symbolS *symbolP = expr->X_add_symbol;
  symbolS *symbolP = expr->X_add_symbol;
  offsetT max_distance = expr->X_add_number;
  offsetT max_distance = expr->X_add_number;
  fragS *target_frag;
  fragS *target_frag;
 
 
  if (expr->X_op != O_symbol)
  if (expr->X_op != O_symbol)
    return FALSE;
    return FALSE;
 
 
  target_frag = symbol_get_frag (symbolP);
  target_frag = symbol_get_frag (symbolP);
 
 
  max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
  max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
  if (is_branch_jmp_to_next (tinsn, fragP))
  if (is_branch_jmp_to_next (tinsn, fragP))
    return FALSE;
    return FALSE;
 
 
  /* The branch doesn't branch over it's own frag,
  /* The branch doesn't branch over it's own frag,
     but over the subsequent ones.  */
     but over the subsequent ones.  */
  fragP = fragP->fr_next;
  fragP = fragP->fr_next;
  while (fragP != NULL && fragP != target_frag && max_distance <= MAX_IMMED6)
  while (fragP != NULL && fragP != target_frag && max_distance <= MAX_IMMED6)
    {
    {
      max_distance += unrelaxed_frag_max_size (fragP);
      max_distance += unrelaxed_frag_max_size (fragP);
      fragP = fragP->fr_next;
      fragP = fragP->fr_next;
    }
    }
  if (max_distance <= MAX_IMMED6 && fragP == target_frag)
  if (max_distance <= MAX_IMMED6 && fragP == target_frag)
    return TRUE;
    return TRUE;
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static void
static void
xtensa_mark_zcl_first_insns (void)
xtensa_mark_zcl_first_insns (void)
{
{
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
                && (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
                    || fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE))
                    || fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE))
              {
              {
                /* Find the loop frag.  */
                /* Find the loop frag.  */
                fragS *targ_frag = next_non_empty_frag (fragP);
                fragS *targ_frag = next_non_empty_frag (fragP);
                /* Find the first insn frag.  */
                /* Find the first insn frag.  */
                targ_frag = next_non_empty_frag (targ_frag);
                targ_frag = next_non_empty_frag (targ_frag);
 
 
                /* Of course, sometimes (mostly for toy test cases) a
                /* Of course, sometimes (mostly for toy test cases) a
                   zero-cost loop instruction is the last in a section.  */
                   zero-cost loop instruction is the last in a section.  */
                if (targ_frag)
                if (targ_frag)
                  {
                  {
                    targ_frag->tc_frag_data.is_first_loop_insn = TRUE;
                    targ_frag->tc_frag_data.is_first_loop_insn = TRUE;
                    /* Do not widen a frag that is the first instruction of a
                    /* Do not widen a frag that is the first instruction of a
                       zero-cost loop.  It makes that loop harder to align.  */
                       zero-cost loop.  It makes that loop harder to align.  */
                    if (targ_frag->fr_type == rs_machine_dependent
                    if (targ_frag->fr_type == rs_machine_dependent
                        && targ_frag->fr_subtype == RELAX_SLOTS
                        && targ_frag->fr_subtype == RELAX_SLOTS
                        && (targ_frag->tc_frag_data.slot_subtypes[0]
                        && (targ_frag->tc_frag_data.slot_subtypes[0]
                            == RELAX_NARROW))
                            == RELAX_NARROW))
                      {
                      {
                        if (targ_frag->tc_frag_data.is_aligning_branch)
                        if (targ_frag->tc_frag_data.is_aligning_branch)
                          targ_frag->tc_frag_data.slot_subtypes[0] = RELAX_IMMED;
                          targ_frag->tc_frag_data.slot_subtypes[0] = RELAX_IMMED;
                        else
                        else
                          {
                          {
                            frag_wane (targ_frag);
                            frag_wane (targ_frag);
                            targ_frag->tc_frag_data.slot_subtypes[0] = 0;
                            targ_frag->tc_frag_data.slot_subtypes[0] = 0;
                          }
                          }
                      }
                      }
                  }
                  }
                if (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)
                if (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)
                  frag_wane (fragP);
                  frag_wane (fragP);
              }
              }
          }
          }
      }
      }
}
}
 
 
 
 
/* When a difference-of-symbols expression is encoded as a uleb128 or
/* When a difference-of-symbols expression is encoded as a uleb128 or
   sleb128 value, the linker is unable to adjust that value to account for
   sleb128 value, the linker is unable to adjust that value to account for
   link-time relaxation.  Mark all the code between such symbols so that
   link-time relaxation.  Mark all the code between such symbols so that
   its size cannot be changed by linker relaxation.  */
   its size cannot be changed by linker relaxation.  */
 
 
static void
static void
xtensa_mark_difference_of_two_symbols (void)
xtensa_mark_difference_of_two_symbols (void)
{
{
  symbolS *expr_sym;
  symbolS *expr_sym;
 
 
  for (expr_sym = expr_symbols; expr_sym;
  for (expr_sym = expr_symbols; expr_sym;
       expr_sym = symbol_get_tc (expr_sym)->next_expr_symbol)
       expr_sym = symbol_get_tc (expr_sym)->next_expr_symbol)
    {
    {
      expressionS *expr = symbol_get_value_expression (expr_sym);
      expressionS *expr = symbol_get_value_expression (expr_sym);
 
 
      if (expr->X_op == O_subtract)
      if (expr->X_op == O_subtract)
        {
        {
          symbolS *left = expr->X_add_symbol;
          symbolS *left = expr->X_add_symbol;
          symbolS *right = expr->X_op_symbol;
          symbolS *right = expr->X_op_symbol;
 
 
          /* Difference of two symbols not in the same section
          /* Difference of two symbols not in the same section
             are handled with relocations in the linker.  */
             are handled with relocations in the linker.  */
          if (S_GET_SEGMENT (left) == S_GET_SEGMENT (right))
          if (S_GET_SEGMENT (left) == S_GET_SEGMENT (right))
            {
            {
              fragS *start;
              fragS *start;
              fragS *end;
              fragS *end;
              fragS *walk;
              fragS *walk;
 
 
              if (symbol_get_frag (left)->fr_address
              if (symbol_get_frag (left)->fr_address
                  <= symbol_get_frag (right)->fr_address)
                  <= symbol_get_frag (right)->fr_address)
                {
                {
                  start = symbol_get_frag (left);
                  start = symbol_get_frag (left);
                  end = symbol_get_frag (right);
                  end = symbol_get_frag (right);
                }
                }
              else
              else
                {
                {
                  start = symbol_get_frag (right);
                  start = symbol_get_frag (right);
                  end = symbol_get_frag (left);
                  end = symbol_get_frag (left);
                }
                }
 
 
              if (start->tc_frag_data.no_transform_end != NULL)
              if (start->tc_frag_data.no_transform_end != NULL)
                walk = start->tc_frag_data.no_transform_end;
                walk = start->tc_frag_data.no_transform_end;
              else
              else
                walk = start;
                walk = start;
              do
              do
                {
                {
                  walk->tc_frag_data.is_no_transform = 1;
                  walk->tc_frag_data.is_no_transform = 1;
                  walk = walk->fr_next;
                  walk = walk->fr_next;
                }
                }
              while (walk && walk->fr_address < end->fr_address);
              while (walk && walk->fr_address < end->fr_address);
 
 
              start->tc_frag_data.no_transform_end = walk;
              start->tc_frag_data.no_transform_end = walk;
            }
            }
        }
        }
    }
    }
}
}
 
 
 
 
/* Re-process all of the fragments looking to convert all of the
/* Re-process all of the fragments looking to convert all of the
   RELAX_ADD_NOP_IF_A0_B_RETW.  If the next instruction is a
   RELAX_ADD_NOP_IF_A0_B_RETW.  If the next instruction is a
   conditional branch or a retw/retw.n, convert this frag to one that
   conditional branch or a retw/retw.n, convert this frag to one that
   will generate a NOP.  In any case close it off with a .fill 0.  */
   will generate a NOP.  In any case close it off with a .fill 0.  */
 
 
static bfd_boolean next_instrs_are_b_retw (fragS *);
static bfd_boolean next_instrs_are_b_retw (fragS *);
 
 
static void
static void
xtensa_fix_a0_b_retw_frags (void)
xtensa_fix_a0_b_retw_frags (void)
{
{
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  /* When this routine is called, all of the subsections are still intact
  /* When this routine is called, all of the subsections are still intact
     so we walk over subsections instead of sections.  */
     so we walk over subsections instead of sections.  */
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
 
 
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW)
                && fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW)
              {
              {
                if (next_instrs_are_b_retw (fragP))
                if (next_instrs_are_b_retw (fragP))
                  {
                  {
                    if (fragP->tc_frag_data.is_no_transform)
                    if (fragP->tc_frag_data.is_no_transform)
                      as_bad (_("instruction sequence (write a0, branch, retw) may trigger hardware errata"));
                      as_bad (_("instruction sequence (write a0, branch, retw) may trigger hardware errata"));
                    else
                    else
                      relax_frag_add_nop (fragP);
                      relax_frag_add_nop (fragP);
                  }
                  }
                frag_wane (fragP);
                frag_wane (fragP);
              }
              }
          }
          }
      }
      }
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
next_instrs_are_b_retw (fragS *fragP)
next_instrs_are_b_retw (fragS *fragP)
{
{
  xtensa_opcode opcode;
  xtensa_opcode opcode;
  xtensa_format fmt;
  xtensa_format fmt;
  const fragS *next_fragP = next_non_empty_frag (fragP);
  const fragS *next_fragP = next_non_empty_frag (fragP);
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int offset = 0;
  int offset = 0;
  int slot;
  int slot;
  bfd_boolean branch_seen = FALSE;
  bfd_boolean branch_seen = FALSE;
 
 
  if (!insnbuf)
  if (!insnbuf)
    {
    {
      insnbuf = xtensa_insnbuf_alloc (isa);
      insnbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
    }
    }
 
 
  if (next_fragP == NULL)
  if (next_fragP == NULL)
    return FALSE;
    return FALSE;
 
 
  /* Check for the conditional branch.  */
  /* Check for the conditional branch.  */
  xtensa_insnbuf_from_chars
  xtensa_insnbuf_from_chars
    (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
    (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
  fmt = xtensa_format_decode (isa, insnbuf);
  fmt = xtensa_format_decode (isa, insnbuf);
  if (fmt == XTENSA_UNDEFINED)
  if (fmt == XTENSA_UNDEFINED)
    return FALSE;
    return FALSE;
 
 
  for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
  for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
    {
    {
      xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
      xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
      opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
      opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
 
 
      branch_seen = (branch_seen
      branch_seen = (branch_seen
                     || xtensa_opcode_is_branch (isa, opcode) == 1);
                     || xtensa_opcode_is_branch (isa, opcode) == 1);
    }
    }
 
 
  if (!branch_seen)
  if (!branch_seen)
    return FALSE;
    return FALSE;
 
 
  offset += xtensa_format_length (isa, fmt);
  offset += xtensa_format_length (isa, fmt);
  if (offset == next_fragP->fr_fix)
  if (offset == next_fragP->fr_fix)
    {
    {
      next_fragP = next_non_empty_frag (next_fragP);
      next_fragP = next_non_empty_frag (next_fragP);
      offset = 0;
      offset = 0;
    }
    }
 
 
  if (next_fragP == NULL)
  if (next_fragP == NULL)
    return FALSE;
    return FALSE;
 
 
  /* Check for the retw/retw.n.  */
  /* Check for the retw/retw.n.  */
  xtensa_insnbuf_from_chars
  xtensa_insnbuf_from_chars
    (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
    (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
  fmt = xtensa_format_decode (isa, insnbuf);
  fmt = xtensa_format_decode (isa, insnbuf);
 
 
  /* Because RETW[.N] is not bundleable, a VLIW bundle here means that we
  /* Because RETW[.N] is not bundleable, a VLIW bundle here means that we
     have no problems.  */
     have no problems.  */
  if (fmt == XTENSA_UNDEFINED
  if (fmt == XTENSA_UNDEFINED
      || xtensa_format_num_slots (isa, fmt) != 1)
      || xtensa_format_num_slots (isa, fmt) != 1)
    return FALSE;
    return FALSE;
 
 
  xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf);
  xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf);
  opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
  opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
 
 
  if (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode)
  if (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode)
    return TRUE;
    return TRUE;
 
 
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Re-process all of the fragments looking to convert all of the
/* Re-process all of the fragments looking to convert all of the
   RELAX_ADD_NOP_IF_PRE_LOOP_END.  If there is one instruction and a
   RELAX_ADD_NOP_IF_PRE_LOOP_END.  If there is one instruction and a
   loop end label, convert this frag to one that will generate a NOP.
   loop end label, convert this frag to one that will generate a NOP.
   In any case close it off with a .fill 0.  */
   In any case close it off with a .fill 0.  */
 
 
static bfd_boolean next_instr_is_loop_end (fragS *);
static bfd_boolean next_instr_is_loop_end (fragS *);
 
 
static void
static void
xtensa_fix_b_j_loop_end_frags (void)
xtensa_fix_b_j_loop_end_frags (void)
{
{
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  /* When this routine is called, all of the subsections are still intact
  /* When this routine is called, all of the subsections are still intact
     so we walk over subsections instead of sections.  */
     so we walk over subsections instead of sections.  */
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
 
 
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END)
                && fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END)
              {
              {
                if (next_instr_is_loop_end (fragP))
                if (next_instr_is_loop_end (fragP))
                  {
                  {
                    if (fragP->tc_frag_data.is_no_transform)
                    if (fragP->tc_frag_data.is_no_transform)
                      as_bad (_("branching or jumping to a loop end may trigger hardware errata"));
                      as_bad (_("branching or jumping to a loop end may trigger hardware errata"));
                    else
                    else
                      relax_frag_add_nop (fragP);
                      relax_frag_add_nop (fragP);
                  }
                  }
                frag_wane (fragP);
                frag_wane (fragP);
              }
              }
          }
          }
      }
      }
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
next_instr_is_loop_end (fragS *fragP)
next_instr_is_loop_end (fragS *fragP)
{
{
  const fragS *next_fragP;
  const fragS *next_fragP;
 
 
  if (next_frag_is_loop_target (fragP))
  if (next_frag_is_loop_target (fragP))
    return FALSE;
    return FALSE;
 
 
  next_fragP = next_non_empty_frag (fragP);
  next_fragP = next_non_empty_frag (fragP);
  if (next_fragP == NULL)
  if (next_fragP == NULL)
    return FALSE;
    return FALSE;
 
 
  if (!next_frag_is_loop_target (next_fragP))
  if (!next_frag_is_loop_target (next_fragP))
    return FALSE;
    return FALSE;
 
 
  /* If the size is >= 3 then there is more than one instruction here.
  /* If the size is >= 3 then there is more than one instruction here.
     The hardware bug will not fire.  */
     The hardware bug will not fire.  */
  if (next_fragP->fr_fix > 3)
  if (next_fragP->fr_fix > 3)
    return FALSE;
    return FALSE;
 
 
  return TRUE;
  return TRUE;
}
}
 
 
 
 
/* Re-process all of the fragments looking to convert all of the
/* Re-process all of the fragments looking to convert all of the
   RELAX_ADD_NOP_IF_CLOSE_LOOP_END.  If there is an loop end that is
   RELAX_ADD_NOP_IF_CLOSE_LOOP_END.  If there is an loop end that is
   not MY loop's loop end within 12 bytes, add enough nops here to
   not MY loop's loop end within 12 bytes, add enough nops here to
   make it at least 12 bytes away.  In any case close it off with a
   make it at least 12 bytes away.  In any case close it off with a
   .fill 0.  */
   .fill 0.  */
 
 
static offsetT min_bytes_to_other_loop_end
static offsetT min_bytes_to_other_loop_end
  (fragS *, fragS *, offsetT);
  (fragS *, fragS *, offsetT);
 
 
static void
static void
xtensa_fix_close_loop_end_frags (void)
xtensa_fix_close_loop_end_frags (void)
{
{
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  /* When this routine is called, all of the subsections are still intact
  /* When this routine is called, all of the subsections are still intact
     so we walk over subsections instead of sections.  */
     so we walk over subsections instead of sections.  */
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
 
 
        fragS *current_target = NULL;
        fragS *current_target = NULL;
 
 
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && ((fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
                && ((fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
                    || (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)))
                    || (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)))
              current_target = symbol_get_frag (fragP->fr_symbol);
              current_target = symbol_get_frag (fragP->fr_symbol);
 
 
            if (current_target
            if (current_target
                && fragP->fr_type == rs_machine_dependent
                && fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_ADD_NOP_IF_CLOSE_LOOP_END)
                && fragP->fr_subtype == RELAX_ADD_NOP_IF_CLOSE_LOOP_END)
              {
              {
                offsetT min_bytes;
                offsetT min_bytes;
                int bytes_added = 0;
                int bytes_added = 0;
 
 
#define REQUIRED_LOOP_DIVIDING_BYTES 12
#define REQUIRED_LOOP_DIVIDING_BYTES 12
                /* Max out at 12.  */
                /* Max out at 12.  */
                min_bytes = min_bytes_to_other_loop_end
                min_bytes = min_bytes_to_other_loop_end
                  (fragP->fr_next, current_target, REQUIRED_LOOP_DIVIDING_BYTES);
                  (fragP->fr_next, current_target, REQUIRED_LOOP_DIVIDING_BYTES);
 
 
                if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
                if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
                  {
                  {
                    if (fragP->tc_frag_data.is_no_transform)
                    if (fragP->tc_frag_data.is_no_transform)
                      as_bad (_("loop end too close to another loop end may trigger hardware errata"));
                      as_bad (_("loop end too close to another loop end may trigger hardware errata"));
                    else
                    else
                      {
                      {
                        while (min_bytes + bytes_added
                        while (min_bytes + bytes_added
                               < REQUIRED_LOOP_DIVIDING_BYTES)
                               < REQUIRED_LOOP_DIVIDING_BYTES)
                          {
                          {
                            int length = 3;
                            int length = 3;
 
 
                            if (fragP->fr_var < length)
                            if (fragP->fr_var < length)
                              as_fatal (_("fr_var %lu < length %d"),
                              as_fatal (_("fr_var %lu < length %d"),
                                        (long) fragP->fr_var, length);
                                        (long) fragP->fr_var, length);
                            else
                            else
                              {
                              {
                                assemble_nop (length,
                                assemble_nop (length,
                                              fragP->fr_literal + fragP->fr_fix);
                                              fragP->fr_literal + fragP->fr_fix);
                                fragP->fr_fix += length;
                                fragP->fr_fix += length;
                                fragP->fr_var -= length;
                                fragP->fr_var -= length;
                              }
                              }
                            bytes_added += length;
                            bytes_added += length;
                          }
                          }
                      }
                      }
                  }
                  }
                frag_wane (fragP);
                frag_wane (fragP);
              }
              }
            gas_assert (fragP->fr_type != rs_machine_dependent
            gas_assert (fragP->fr_type != rs_machine_dependent
                    || fragP->fr_subtype != RELAX_ADD_NOP_IF_CLOSE_LOOP_END);
                    || fragP->fr_subtype != RELAX_ADD_NOP_IF_CLOSE_LOOP_END);
          }
          }
      }
      }
}
}
 
 
 
 
static offsetT unrelaxed_frag_min_size (fragS *);
static offsetT unrelaxed_frag_min_size (fragS *);
 
 
static offsetT
static offsetT
min_bytes_to_other_loop_end (fragS *fragP,
min_bytes_to_other_loop_end (fragS *fragP,
                             fragS *current_target,
                             fragS *current_target,
                             offsetT max_size)
                             offsetT max_size)
{
{
  offsetT offset = 0;
  offsetT offset = 0;
  fragS *current_fragP;
  fragS *current_fragP;
 
 
  for (current_fragP = fragP;
  for (current_fragP = fragP;
       current_fragP;
       current_fragP;
       current_fragP = current_fragP->fr_next)
       current_fragP = current_fragP->fr_next)
    {
    {
      if (current_fragP->tc_frag_data.is_loop_target
      if (current_fragP->tc_frag_data.is_loop_target
          && current_fragP != current_target)
          && current_fragP != current_target)
        return offset;
        return offset;
 
 
      offset += unrelaxed_frag_min_size (current_fragP);
      offset += unrelaxed_frag_min_size (current_fragP);
 
 
      if (offset >= max_size)
      if (offset >= max_size)
        return max_size;
        return max_size;
    }
    }
  return max_size;
  return max_size;
}
}
 
 
 
 
static offsetT
static offsetT
unrelaxed_frag_min_size (fragS *fragP)
unrelaxed_frag_min_size (fragS *fragP)
{
{
  offsetT size = fragP->fr_fix;
  offsetT size = fragP->fr_fix;
 
 
  /* Add fill size.  */
  /* Add fill size.  */
  if (fragP->fr_type == rs_fill)
  if (fragP->fr_type == rs_fill)
    size += fragP->fr_offset;
    size += fragP->fr_offset;
 
 
  return size;
  return size;
}
}
 
 
 
 
static offsetT
static offsetT
unrelaxed_frag_max_size (fragS *fragP)
unrelaxed_frag_max_size (fragS *fragP)
{
{
  offsetT size = fragP->fr_fix;
  offsetT size = fragP->fr_fix;
  switch (fragP->fr_type)
  switch (fragP->fr_type)
    {
    {
    case 0:
    case 0:
      /* Empty frags created by the obstack allocation scheme
      /* Empty frags created by the obstack allocation scheme
         end up with type 0.  */
         end up with type 0.  */
      break;
      break;
    case rs_fill:
    case rs_fill:
    case rs_org:
    case rs_org:
    case rs_space:
    case rs_space:
      size += fragP->fr_offset;
      size += fragP->fr_offset;
      break;
      break;
    case rs_align:
    case rs_align:
    case rs_align_code:
    case rs_align_code:
    case rs_align_test:
    case rs_align_test:
    case rs_leb128:
    case rs_leb128:
    case rs_cfa:
    case rs_cfa:
    case rs_dwarf2dbg:
    case rs_dwarf2dbg:
      /* No further adjustments needed.  */
      /* No further adjustments needed.  */
      break;
      break;
    case rs_machine_dependent:
    case rs_machine_dependent:
      if (fragP->fr_subtype != RELAX_DESIRE_ALIGN)
      if (fragP->fr_subtype != RELAX_DESIRE_ALIGN)
        size += fragP->fr_var;
        size += fragP->fr_var;
      break;
      break;
    default:
    default:
      /* We had darn well better know how big it is.  */
      /* We had darn well better know how big it is.  */
      gas_assert (0);
      gas_assert (0);
      break;
      break;
    }
    }
 
 
  return size;
  return size;
}
}
 
 
 
 
/* Re-process all of the fragments looking to convert all
/* Re-process all of the fragments looking to convert all
   of the RELAX_ADD_NOP_IF_SHORT_LOOP.  If:
   of the RELAX_ADD_NOP_IF_SHORT_LOOP.  If:
 
 
   A)
   A)
     1) the instruction size count to the loop end label
     1) the instruction size count to the loop end label
        is too short (<= 2 instructions),
        is too short (<= 2 instructions),
     2) loop has a jump or branch in it
     2) loop has a jump or branch in it
 
 
   or B)
   or B)
     1) workaround_all_short_loops is TRUE
     1) workaround_all_short_loops is TRUE
     2) The generating loop was a  'loopgtz' or 'loopnez'
     2) The generating loop was a  'loopgtz' or 'loopnez'
     3) the instruction size count to the loop end label is too short
     3) the instruction size count to the loop end label is too short
        (<= 2 instructions)
        (<= 2 instructions)
   then convert this frag (and maybe the next one) to generate a NOP.
   then convert this frag (and maybe the next one) to generate a NOP.
   In any case close it off with a .fill 0.  */
   In any case close it off with a .fill 0.  */
 
 
static int count_insns_to_loop_end (fragS *, bfd_boolean, int);
static int count_insns_to_loop_end (fragS *, bfd_boolean, int);
static bfd_boolean branch_before_loop_end (fragS *);
static bfd_boolean branch_before_loop_end (fragS *);
 
 
static void
static void
xtensa_fix_short_loop_frags (void)
xtensa_fix_short_loop_frags (void)
{
{
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  /* When this routine is called, all of the subsections are still intact
  /* When this routine is called, all of the subsections are still intact
     so we walk over subsections instead of sections.  */
     so we walk over subsections instead of sections.  */
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
        fragS *current_target = NULL;
        fragS *current_target = NULL;
        xtensa_opcode current_opcode = XTENSA_UNDEFINED;
        xtensa_opcode current_opcode = XTENSA_UNDEFINED;
 
 
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && ((fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
                && ((fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
                    || (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)))
                    || (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)))
              {
              {
                TInsn t_insn;
                TInsn t_insn;
                fragS *loop_frag = next_non_empty_frag (fragP);
                fragS *loop_frag = next_non_empty_frag (fragP);
                tinsn_from_chars (&t_insn, loop_frag->fr_opcode, 0);
                tinsn_from_chars (&t_insn, loop_frag->fr_opcode, 0);
                current_target = symbol_get_frag (fragP->fr_symbol);
                current_target = symbol_get_frag (fragP->fr_symbol);
                current_opcode = t_insn.opcode;
                current_opcode = t_insn.opcode;
                gas_assert (xtensa_opcode_is_loop (xtensa_default_isa,
                gas_assert (xtensa_opcode_is_loop (xtensa_default_isa,
                                               current_opcode) == 1);
                                               current_opcode) == 1);
              }
              }
 
 
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
                && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
              {
              {
                if (count_insns_to_loop_end (fragP->fr_next, TRUE, 3) < 3
                if (count_insns_to_loop_end (fragP->fr_next, TRUE, 3) < 3
                    && (branch_before_loop_end (fragP->fr_next)
                    && (branch_before_loop_end (fragP->fr_next)
                        || (workaround_all_short_loops
                        || (workaround_all_short_loops
                            && current_opcode != XTENSA_UNDEFINED
                            && current_opcode != XTENSA_UNDEFINED
                            && current_opcode != xtensa_loop_opcode)))
                            && current_opcode != xtensa_loop_opcode)))
                  {
                  {
                    if (fragP->tc_frag_data.is_no_transform)
                    if (fragP->tc_frag_data.is_no_transform)
                      as_bad (_("loop containing less than three instructions may trigger hardware errata"));
                      as_bad (_("loop containing less than three instructions may trigger hardware errata"));
                    else
                    else
                      relax_frag_add_nop (fragP);
                      relax_frag_add_nop (fragP);
                  }
                  }
                frag_wane (fragP);
                frag_wane (fragP);
              }
              }
          }
          }
      }
      }
}
}
 
 
 
 
static int unrelaxed_frag_min_insn_count (fragS *);
static int unrelaxed_frag_min_insn_count (fragS *);
 
 
static int
static int
count_insns_to_loop_end (fragS *base_fragP,
count_insns_to_loop_end (fragS *base_fragP,
                         bfd_boolean count_relax_add,
                         bfd_boolean count_relax_add,
                         int max_count)
                         int max_count)
{
{
  fragS *fragP = NULL;
  fragS *fragP = NULL;
  int insn_count = 0;
  int insn_count = 0;
 
 
  fragP = base_fragP;
  fragP = base_fragP;
 
 
  for (; fragP && !fragP->tc_frag_data.is_loop_target; fragP = fragP->fr_next)
  for (; fragP && !fragP->tc_frag_data.is_loop_target; fragP = fragP->fr_next)
    {
    {
      insn_count += unrelaxed_frag_min_insn_count (fragP);
      insn_count += unrelaxed_frag_min_insn_count (fragP);
      if (insn_count >= max_count)
      if (insn_count >= max_count)
        return max_count;
        return max_count;
 
 
      if (count_relax_add)
      if (count_relax_add)
        {
        {
          if (fragP->fr_type == rs_machine_dependent
          if (fragP->fr_type == rs_machine_dependent
              && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
              && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
            {
            {
              /* In order to add the appropriate number of
              /* In order to add the appropriate number of
                 NOPs, we count an instruction for downstream
                 NOPs, we count an instruction for downstream
                 occurrences.  */
                 occurrences.  */
              insn_count++;
              insn_count++;
              if (insn_count >= max_count)
              if (insn_count >= max_count)
                return max_count;
                return max_count;
            }
            }
        }
        }
    }
    }
  return insn_count;
  return insn_count;
}
}
 
 
 
 
static int
static int
unrelaxed_frag_min_insn_count (fragS *fragP)
unrelaxed_frag_min_insn_count (fragS *fragP)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  int insn_count = 0;
  int insn_count = 0;
  int offset = 0;
  int offset = 0;
 
 
  if (!fragP->tc_frag_data.is_insn)
  if (!fragP->tc_frag_data.is_insn)
    return insn_count;
    return insn_count;
 
 
  if (!insnbuf)
  if (!insnbuf)
    insnbuf = xtensa_insnbuf_alloc (isa);
    insnbuf = xtensa_insnbuf_alloc (isa);
 
 
  /* Decode the fixed instructions.  */
  /* Decode the fixed instructions.  */
  while (offset < fragP->fr_fix)
  while (offset < fragP->fr_fix)
    {
    {
      xtensa_format fmt;
      xtensa_format fmt;
 
 
      xtensa_insnbuf_from_chars
      xtensa_insnbuf_from_chars
        (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
        (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
      fmt = xtensa_format_decode (isa, insnbuf);
      fmt = xtensa_format_decode (isa, insnbuf);
 
 
      if (fmt == XTENSA_UNDEFINED)
      if (fmt == XTENSA_UNDEFINED)
        {
        {
          as_fatal (_("undecodable instruction in instruction frag"));
          as_fatal (_("undecodable instruction in instruction frag"));
          return insn_count;
          return insn_count;
        }
        }
      offset += xtensa_format_length (isa, fmt);
      offset += xtensa_format_length (isa, fmt);
      insn_count++;
      insn_count++;
    }
    }
 
 
  return insn_count;
  return insn_count;
}
}
 
 
 
 
static bfd_boolean unrelaxed_frag_has_b_j (fragS *);
static bfd_boolean unrelaxed_frag_has_b_j (fragS *);
 
 
static bfd_boolean
static bfd_boolean
branch_before_loop_end (fragS *base_fragP)
branch_before_loop_end (fragS *base_fragP)
{
{
  fragS *fragP;
  fragS *fragP;
 
 
  for (fragP = base_fragP;
  for (fragP = base_fragP;
       fragP && !fragP->tc_frag_data.is_loop_target;
       fragP && !fragP->tc_frag_data.is_loop_target;
       fragP = fragP->fr_next)
       fragP = fragP->fr_next)
    {
    {
      if (unrelaxed_frag_has_b_j (fragP))
      if (unrelaxed_frag_has_b_j (fragP))
        return TRUE;
        return TRUE;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
unrelaxed_frag_has_b_j (fragS *fragP)
unrelaxed_frag_has_b_j (fragS *fragP)
{
{
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int offset = 0;
  int offset = 0;
 
 
  if (!fragP->tc_frag_data.is_insn)
  if (!fragP->tc_frag_data.is_insn)
    return FALSE;
    return FALSE;
 
 
  if (!insnbuf)
  if (!insnbuf)
    insnbuf = xtensa_insnbuf_alloc (isa);
    insnbuf = xtensa_insnbuf_alloc (isa);
 
 
  /* Decode the fixed instructions.  */
  /* Decode the fixed instructions.  */
  while (offset < fragP->fr_fix)
  while (offset < fragP->fr_fix)
    {
    {
      xtensa_format fmt;
      xtensa_format fmt;
      int slot;
      int slot;
 
 
      xtensa_insnbuf_from_chars
      xtensa_insnbuf_from_chars
        (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
        (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
      fmt = xtensa_format_decode (isa, insnbuf);
      fmt = xtensa_format_decode (isa, insnbuf);
      if (fmt == XTENSA_UNDEFINED)
      if (fmt == XTENSA_UNDEFINED)
        return FALSE;
        return FALSE;
 
 
      for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
      for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
        {
        {
          xtensa_opcode opcode =
          xtensa_opcode opcode =
            get_opcode_from_buf (fragP->fr_literal + offset, slot);
            get_opcode_from_buf (fragP->fr_literal + offset, slot);
          if (xtensa_opcode_is_branch (isa, opcode) == 1
          if (xtensa_opcode_is_branch (isa, opcode) == 1
              || xtensa_opcode_is_jump (isa, opcode) == 1)
              || xtensa_opcode_is_jump (isa, opcode) == 1)
            return TRUE;
            return TRUE;
        }
        }
      offset += xtensa_format_length (isa, fmt);
      offset += xtensa_format_length (isa, fmt);
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Checks to be made after initial assembly but before relaxation.  */
/* Checks to be made after initial assembly but before relaxation.  */
 
 
static bfd_boolean is_empty_loop (const TInsn *, fragS *);
static bfd_boolean is_empty_loop (const TInsn *, fragS *);
static bfd_boolean is_local_forward_loop (const TInsn *, fragS *);
static bfd_boolean is_local_forward_loop (const TInsn *, fragS *);
 
 
static void
static void
xtensa_sanity_check (void)
xtensa_sanity_check (void)
{
{
  char *file_name;
  char *file_name;
  unsigned line;
  unsigned line;
  frchainS *frchP;
  frchainS *frchP;
  asection *s;
  asection *s;
 
 
  as_where (&file_name, &line);
  as_where (&file_name, &line);
  for (s = stdoutput->sections; s; s = s->next)
  for (s = stdoutput->sections; s; s = s->next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
      {
      {
        fragS *fragP;
        fragS *fragP;
 
 
        /* Walk over all of the fragments in a subsection.  */
        /* Walk over all of the fragments in a subsection.  */
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
        for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
          {
          {
            if (fragP->fr_type == rs_machine_dependent
            if (fragP->fr_type == rs_machine_dependent
                && fragP->fr_subtype == RELAX_SLOTS
                && fragP->fr_subtype == RELAX_SLOTS
                && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
                && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
              {
              {
                static xtensa_insnbuf insnbuf = NULL;
                static xtensa_insnbuf insnbuf = NULL;
                TInsn t_insn;
                TInsn t_insn;
 
 
                if (fragP->fr_opcode != NULL)
                if (fragP->fr_opcode != NULL)
                  {
                  {
                    if (!insnbuf)
                    if (!insnbuf)
                      insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
                      insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
                    tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
                    tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
                    tinsn_immed_from_frag (&t_insn, fragP, 0);
                    tinsn_immed_from_frag (&t_insn, fragP, 0);
 
 
                    if (xtensa_opcode_is_loop (xtensa_default_isa,
                    if (xtensa_opcode_is_loop (xtensa_default_isa,
                                               t_insn.opcode) == 1)
                                               t_insn.opcode) == 1)
                      {
                      {
                        if (is_empty_loop (&t_insn, fragP))
                        if (is_empty_loop (&t_insn, fragP))
                          {
                          {
                            new_logical_line (fragP->fr_file, fragP->fr_line);
                            new_logical_line (fragP->fr_file, fragP->fr_line);
                            as_bad (_("invalid empty loop"));
                            as_bad (_("invalid empty loop"));
                          }
                          }
                        if (!is_local_forward_loop (&t_insn, fragP))
                        if (!is_local_forward_loop (&t_insn, fragP))
                          {
                          {
                            new_logical_line (fragP->fr_file, fragP->fr_line);
                            new_logical_line (fragP->fr_file, fragP->fr_line);
                            as_bad (_("loop target does not follow "
                            as_bad (_("loop target does not follow "
                                      "loop instruction in section"));
                                      "loop instruction in section"));
                          }
                          }
                      }
                      }
                  }
                  }
              }
              }
          }
          }
      }
      }
  new_logical_line (file_name, line);
  new_logical_line (file_name, line);
}
}
 
 
 
 
#define LOOP_IMMED_OPN 1
#define LOOP_IMMED_OPN 1
 
 
/* Return TRUE if the loop target is the next non-zero fragment.  */
/* Return TRUE if the loop target is the next non-zero fragment.  */
 
 
static bfd_boolean
static bfd_boolean
is_empty_loop (const TInsn *insn, fragS *fragP)
is_empty_loop (const TInsn *insn, fragS *fragP)
{
{
  const expressionS *expr;
  const expressionS *expr;
  symbolS *symbolP;
  symbolS *symbolP;
  fragS *next_fragP;
  fragS *next_fragP;
 
 
  if (insn->insn_type != ITYPE_INSN)
  if (insn->insn_type != ITYPE_INSN)
    return FALSE;
    return FALSE;
 
 
  if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
  if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
    return FALSE;
    return FALSE;
 
 
  if (insn->ntok <= LOOP_IMMED_OPN)
  if (insn->ntok <= LOOP_IMMED_OPN)
    return FALSE;
    return FALSE;
 
 
  expr = &insn->tok[LOOP_IMMED_OPN];
  expr = &insn->tok[LOOP_IMMED_OPN];
 
 
  if (expr->X_op != O_symbol)
  if (expr->X_op != O_symbol)
    return FALSE;
    return FALSE;
 
 
  symbolP = expr->X_add_symbol;
  symbolP = expr->X_add_symbol;
  if (!symbolP)
  if (!symbolP)
    return FALSE;
    return FALSE;
 
 
  if (symbol_get_frag (symbolP) == NULL)
  if (symbol_get_frag (symbolP) == NULL)
    return FALSE;
    return FALSE;
 
 
  if (S_GET_VALUE (symbolP) != 0)
  if (S_GET_VALUE (symbolP) != 0)
    return FALSE;
    return FALSE;
 
 
  /* Walk through the zero-size fragments from this one.  If we find
  /* Walk through the zero-size fragments from this one.  If we find
     the target fragment, then this is a zero-size loop.  */
     the target fragment, then this is a zero-size loop.  */
 
 
  for (next_fragP = fragP->fr_next;
  for (next_fragP = fragP->fr_next;
       next_fragP != NULL;
       next_fragP != NULL;
       next_fragP = next_fragP->fr_next)
       next_fragP = next_fragP->fr_next)
    {
    {
      if (next_fragP == symbol_get_frag (symbolP))
      if (next_fragP == symbol_get_frag (symbolP))
        return TRUE;
        return TRUE;
      if (next_fragP->fr_fix != 0)
      if (next_fragP->fr_fix != 0)
        return FALSE;
        return FALSE;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
is_local_forward_loop (const TInsn *insn, fragS *fragP)
is_local_forward_loop (const TInsn *insn, fragS *fragP)
{
{
  const expressionS *expr;
  const expressionS *expr;
  symbolS *symbolP;
  symbolS *symbolP;
  fragS *next_fragP;
  fragS *next_fragP;
 
 
  if (insn->insn_type != ITYPE_INSN)
  if (insn->insn_type != ITYPE_INSN)
    return FALSE;
    return FALSE;
 
 
  if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
  if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
    return FALSE;
    return FALSE;
 
 
  if (insn->ntok <= LOOP_IMMED_OPN)
  if (insn->ntok <= LOOP_IMMED_OPN)
    return FALSE;
    return FALSE;
 
 
  expr = &insn->tok[LOOP_IMMED_OPN];
  expr = &insn->tok[LOOP_IMMED_OPN];
 
 
  if (expr->X_op != O_symbol)
  if (expr->X_op != O_symbol)
    return FALSE;
    return FALSE;
 
 
  symbolP = expr->X_add_symbol;
  symbolP = expr->X_add_symbol;
  if (!symbolP)
  if (!symbolP)
    return FALSE;
    return FALSE;
 
 
  if (symbol_get_frag (symbolP) == NULL)
  if (symbol_get_frag (symbolP) == NULL)
    return FALSE;
    return FALSE;
 
 
  /* Walk through fragments until we find the target.
  /* Walk through fragments until we find the target.
     If we do not find the target, then this is an invalid loop.  */
     If we do not find the target, then this is an invalid loop.  */
 
 
  for (next_fragP = fragP->fr_next;
  for (next_fragP = fragP->fr_next;
       next_fragP != NULL;
       next_fragP != NULL;
       next_fragP = next_fragP->fr_next)
       next_fragP = next_fragP->fr_next)
    {
    {
      if (next_fragP == symbol_get_frag (symbolP))
      if (next_fragP == symbol_get_frag (symbolP))
        return TRUE;
        return TRUE;
    }
    }
 
 
  return FALSE;
  return FALSE;
}
}
 
 
 
 
#define XTINFO_NAME "Xtensa_Info"
#define XTINFO_NAME "Xtensa_Info"
#define XTINFO_NAMESZ 12
#define XTINFO_NAMESZ 12
#define XTINFO_TYPE 1
#define XTINFO_TYPE 1
 
 
static void
static void
xtensa_add_config_info (void)
xtensa_add_config_info (void)
{
{
  asection *info_sec;
  asection *info_sec;
  char *data, *p;
  char *data, *p;
  int sz;
  int sz;
 
 
  info_sec = subseg_new (".xtensa.info", 0);
  info_sec = subseg_new (".xtensa.info", 0);
  bfd_set_section_flags (stdoutput, info_sec, SEC_HAS_CONTENTS | SEC_READONLY);
  bfd_set_section_flags (stdoutput, info_sec, SEC_HAS_CONTENTS | SEC_READONLY);
 
 
  data = xmalloc (100);
  data = xmalloc (100);
  sprintf (data, "USE_ABSOLUTE_LITERALS=%d\nABI=%d\n",
  sprintf (data, "USE_ABSOLUTE_LITERALS=%d\nABI=%d\n",
           XSHAL_USE_ABSOLUTE_LITERALS, XSHAL_ABI);
           XSHAL_USE_ABSOLUTE_LITERALS, XSHAL_ABI);
  sz = strlen (data) + 1;
  sz = strlen (data) + 1;
 
 
  /* Add enough null terminators to pad to a word boundary.  */
  /* Add enough null terminators to pad to a word boundary.  */
  do
  do
    data[sz++] = 0;
    data[sz++] = 0;
  while ((sz & 3) != 0);
  while ((sz & 3) != 0);
 
 
  /* Follow the standard note section layout:
  /* Follow the standard note section layout:
     First write the length of the name string.  */
     First write the length of the name string.  */
  p = frag_more (4);
  p = frag_more (4);
  md_number_to_chars (p, (valueT) XTINFO_NAMESZ, 4);
  md_number_to_chars (p, (valueT) XTINFO_NAMESZ, 4);
 
 
  /* Next comes the length of the "descriptor", i.e., the actual data.  */
  /* Next comes the length of the "descriptor", i.e., the actual data.  */
  p = frag_more (4);
  p = frag_more (4);
  md_number_to_chars (p, (valueT) sz, 4);
  md_number_to_chars (p, (valueT) sz, 4);
 
 
  /* Write the note type.  */
  /* Write the note type.  */
  p = frag_more (4);
  p = frag_more (4);
  md_number_to_chars (p, (valueT) XTINFO_TYPE, 4);
  md_number_to_chars (p, (valueT) XTINFO_TYPE, 4);
 
 
  /* Write the name field.  */
  /* Write the name field.  */
  p = frag_more (XTINFO_NAMESZ);
  p = frag_more (XTINFO_NAMESZ);
  memcpy (p, XTINFO_NAME, XTINFO_NAMESZ);
  memcpy (p, XTINFO_NAME, XTINFO_NAMESZ);
 
 
  /* Finally, write the descriptor.  */
  /* Finally, write the descriptor.  */
  p = frag_more (sz);
  p = frag_more (sz);
  memcpy (p, data, sz);
  memcpy (p, data, sz);
 
 
  free (data);
  free (data);
}
}
 
 


/* Alignment Functions.  */
/* Alignment Functions.  */
 
 
static int
static int
get_text_align_power (unsigned target_size)
get_text_align_power (unsigned target_size)
{
{
  if (target_size <= 4)
  if (target_size <= 4)
    return 2;
    return 2;
  gas_assert (target_size == 8);
  gas_assert (target_size == 8);
  return 3;
  return 3;
}
}
 
 
 
 
static int
static int
get_text_align_max_fill_size (int align_pow,
get_text_align_max_fill_size (int align_pow,
                              bfd_boolean use_nops,
                              bfd_boolean use_nops,
                              bfd_boolean use_no_density)
                              bfd_boolean use_no_density)
{
{
  if (!use_nops)
  if (!use_nops)
    return (1 << align_pow);
    return (1 << align_pow);
  if (use_no_density)
  if (use_no_density)
    return 3 * (1 << align_pow);
    return 3 * (1 << align_pow);
 
 
  return 1 + (1 << align_pow);
  return 1 + (1 << align_pow);
}
}
 
 
 
 
/* Calculate the minimum bytes of fill needed at "address" to align a
/* Calculate the minimum bytes of fill needed at "address" to align a
   target instruction of size "target_size" so that it does not cross a
   target instruction of size "target_size" so that it does not cross a
   power-of-two boundary specified by "align_pow".  If "use_nops" is FALSE,
   power-of-two boundary specified by "align_pow".  If "use_nops" is FALSE,
   the fill can be an arbitrary number of bytes.  Otherwise, the space must
   the fill can be an arbitrary number of bytes.  Otherwise, the space must
   be filled by NOP instructions.  */
   be filled by NOP instructions.  */
 
 
static int
static int
get_text_align_fill_size (addressT address,
get_text_align_fill_size (addressT address,
                          int align_pow,
                          int align_pow,
                          int target_size,
                          int target_size,
                          bfd_boolean use_nops,
                          bfd_boolean use_nops,
                          bfd_boolean use_no_density)
                          bfd_boolean use_no_density)
{
{
  addressT alignment, fill, fill_limit, fill_step;
  addressT alignment, fill, fill_limit, fill_step;
  bfd_boolean skip_one = FALSE;
  bfd_boolean skip_one = FALSE;
 
 
  alignment = (1 << align_pow);
  alignment = (1 << align_pow);
  gas_assert (target_size > 0 && alignment >= (addressT) target_size);
  gas_assert (target_size > 0 && alignment >= (addressT) target_size);
 
 
  if (!use_nops)
  if (!use_nops)
    {
    {
      fill_limit = alignment;
      fill_limit = alignment;
      fill_step = 1;
      fill_step = 1;
    }
    }
  else if (!use_no_density)
  else if (!use_no_density)
    {
    {
      /* Combine 2- and 3-byte NOPs to fill anything larger than one.  */
      /* Combine 2- and 3-byte NOPs to fill anything larger than one.  */
      fill_limit = alignment * 2;
      fill_limit = alignment * 2;
      fill_step = 1;
      fill_step = 1;
      skip_one = TRUE;
      skip_one = TRUE;
    }
    }
  else
  else
    {
    {
      /* Fill with 3-byte NOPs -- can only fill multiples of 3.  */
      /* Fill with 3-byte NOPs -- can only fill multiples of 3.  */
      fill_limit = alignment * 3;
      fill_limit = alignment * 3;
      fill_step = 3;
      fill_step = 3;
    }
    }
 
 
  /* Try all fill sizes until finding one that works.  */
  /* Try all fill sizes until finding one that works.  */
  for (fill = 0; fill < fill_limit; fill += fill_step)
  for (fill = 0; fill < fill_limit; fill += fill_step)
    {
    {
      if (skip_one && fill == 1)
      if (skip_one && fill == 1)
        continue;
        continue;
      if ((address + fill) >> align_pow
      if ((address + fill) >> align_pow
          == (address + fill + target_size - 1) >> align_pow)
          == (address + fill + target_size - 1) >> align_pow)
        return fill;
        return fill;
    }
    }
  gas_assert (0);
  gas_assert (0);
  return 0;
  return 0;
}
}
 
 
 
 
static int
static int
branch_align_power (segT sec)
branch_align_power (segT sec)
{
{
  /* If the Xtensa processor has a fetch width of 8 bytes, and the section
  /* If the Xtensa processor has a fetch width of 8 bytes, and the section
     is aligned to at least an 8-byte boundary, then a branch target need
     is aligned to at least an 8-byte boundary, then a branch target need
     only fit within an 8-byte aligned block of memory to avoid a stall.
     only fit within an 8-byte aligned block of memory to avoid a stall.
     Otherwise, try to fit branch targets within 4-byte aligned blocks
     Otherwise, try to fit branch targets within 4-byte aligned blocks
     (which may be insufficient, e.g., if the section has no alignment, but
     (which may be insufficient, e.g., if the section has no alignment, but
     it's good enough).  */
     it's good enough).  */
  if (xtensa_fetch_width == 8)
  if (xtensa_fetch_width == 8)
    {
    {
      if (get_recorded_alignment (sec) >= 3)
      if (get_recorded_alignment (sec) >= 3)
        return 3;
        return 3;
    }
    }
  else
  else
    gas_assert (xtensa_fetch_width == 4);
    gas_assert (xtensa_fetch_width == 4);
 
 
  return 2;
  return 2;
}
}
 
 
 
 
/* This will assert if it is not possible.  */
/* This will assert if it is not possible.  */
 
 
static int
static int
get_text_align_nop_count (offsetT fill_size, bfd_boolean use_no_density)
get_text_align_nop_count (offsetT fill_size, bfd_boolean use_no_density)
{
{
  int count = 0;
  int count = 0;
 
 
  if (use_no_density)
  if (use_no_density)
    {
    {
      gas_assert (fill_size % 3 == 0);
      gas_assert (fill_size % 3 == 0);
      return (fill_size / 3);
      return (fill_size / 3);
    }
    }
 
 
  gas_assert (fill_size != 1);  /* Bad argument.  */
  gas_assert (fill_size != 1);  /* Bad argument.  */
 
 
  while (fill_size > 1)
  while (fill_size > 1)
    {
    {
      int insn_size = 3;
      int insn_size = 3;
      if (fill_size == 2 || fill_size == 4)
      if (fill_size == 2 || fill_size == 4)
        insn_size = 2;
        insn_size = 2;
      fill_size -= insn_size;
      fill_size -= insn_size;
      count++;
      count++;
    }
    }
  gas_assert (fill_size != 1);  /* Bad algorithm.  */
  gas_assert (fill_size != 1);  /* Bad algorithm.  */
  return count;
  return count;
}
}
 
 
 
 
static int
static int
get_text_align_nth_nop_size (offsetT fill_size,
get_text_align_nth_nop_size (offsetT fill_size,
                             int n,
                             int n,
                             bfd_boolean use_no_density)
                             bfd_boolean use_no_density)
{
{
  int count = 0;
  int count = 0;
 
 
  if (use_no_density)
  if (use_no_density)
    return 3;
    return 3;
 
 
  gas_assert (fill_size != 1);  /* Bad argument.  */
  gas_assert (fill_size != 1);  /* Bad argument.  */
 
 
  while (fill_size > 1)
  while (fill_size > 1)
    {
    {
      int insn_size = 3;
      int insn_size = 3;
      if (fill_size == 2 || fill_size == 4)
      if (fill_size == 2 || fill_size == 4)
        insn_size = 2;
        insn_size = 2;
      fill_size -= insn_size;
      fill_size -= insn_size;
      count++;
      count++;
      if (n + 1 == count)
      if (n + 1 == count)
        return insn_size;
        return insn_size;
    }
    }
  gas_assert (0);
  gas_assert (0);
  return 0;
  return 0;
}
}
 
 
 
 
/* For the given fragment, find the appropriate address
/* For the given fragment, find the appropriate address
   for it to begin at if we are using NOPs to align it.  */
   for it to begin at if we are using NOPs to align it.  */
 
 
static addressT
static addressT
get_noop_aligned_address (fragS *fragP, addressT address)
get_noop_aligned_address (fragS *fragP, addressT address)
{
{
  /* The rule is: get next fragment's FIRST instruction.  Find
  /* The rule is: get next fragment's FIRST instruction.  Find
     the smallest number of bytes that need to be added to
     the smallest number of bytes that need to be added to
     ensure that the next fragment's FIRST instruction will fit
     ensure that the next fragment's FIRST instruction will fit
     in a single word.
     in a single word.
 
 
     E.G.,   2 bytes : 0, 1, 2 mod 4
     E.G.,   2 bytes : 0, 1, 2 mod 4
             3 bytes: 0, 1 mod 4
             3 bytes: 0, 1 mod 4
 
 
     If the FIRST instruction MIGHT be relaxed,
     If the FIRST instruction MIGHT be relaxed,
     assume that it will become a 3-byte instruction.
     assume that it will become a 3-byte instruction.
 
 
     Note again here that LOOP instructions are not bundleable,
     Note again here that LOOP instructions are not bundleable,
     and this relaxation only applies to LOOP opcodes.  */
     and this relaxation only applies to LOOP opcodes.  */
 
 
  int fill_size = 0;
  int fill_size = 0;
  int first_insn_size;
  int first_insn_size;
  int loop_insn_size;
  int loop_insn_size;
  addressT pre_opcode_bytes;
  addressT pre_opcode_bytes;
  int align_power;
  int align_power;
  fragS *first_insn;
  fragS *first_insn;
  xtensa_opcode opcode;
  xtensa_opcode opcode;
  bfd_boolean is_loop;
  bfd_boolean is_loop;
 
 
  gas_assert (fragP->fr_type == rs_machine_dependent);
  gas_assert (fragP->fr_type == rs_machine_dependent);
  gas_assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
  gas_assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
 
 
  /* Find the loop frag.  */
  /* Find the loop frag.  */
  first_insn = next_non_empty_frag (fragP);
  first_insn = next_non_empty_frag (fragP);
  /* Now find the first insn frag.  */
  /* Now find the first insn frag.  */
  first_insn = next_non_empty_frag (first_insn);
  first_insn = next_non_empty_frag (first_insn);
 
 
  is_loop = next_frag_opcode_is_loop (fragP, &opcode);
  is_loop = next_frag_opcode_is_loop (fragP, &opcode);
  gas_assert (is_loop);
  gas_assert (is_loop);
  loop_insn_size = xg_get_single_size (opcode);
  loop_insn_size = xg_get_single_size (opcode);
 
 
  pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
  pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
  pre_opcode_bytes += loop_insn_size;
  pre_opcode_bytes += loop_insn_size;
 
 
  /* For loops, the alignment depends on the size of the
  /* For loops, the alignment depends on the size of the
     instruction following the loop, not the LOOP instruction.  */
     instruction following the loop, not the LOOP instruction.  */
 
 
  if (first_insn == NULL)
  if (first_insn == NULL)
    first_insn_size = xtensa_fetch_width;
    first_insn_size = xtensa_fetch_width;
  else
  else
    first_insn_size = get_loop_align_size (frag_format_size (first_insn));
    first_insn_size = get_loop_align_size (frag_format_size (first_insn));
 
 
  /* If it was 8, then we'll need a larger alignment for the section.  */
  /* If it was 8, then we'll need a larger alignment for the section.  */
  align_power = get_text_align_power (first_insn_size);
  align_power = get_text_align_power (first_insn_size);
  record_alignment (now_seg, align_power);
  record_alignment (now_seg, align_power);
 
 
  fill_size = get_text_align_fill_size
  fill_size = get_text_align_fill_size
    (address + pre_opcode_bytes, align_power, first_insn_size, TRUE,
    (address + pre_opcode_bytes, align_power, first_insn_size, TRUE,
     fragP->tc_frag_data.is_no_density);
     fragP->tc_frag_data.is_no_density);
 
 
  return address + fill_size;
  return address + fill_size;
}
}
 
 
 
 
/* 3 mechanisms for relaxing an alignment:
/* 3 mechanisms for relaxing an alignment:
 
 
   Align to a power of 2.
   Align to a power of 2.
   Align so the next fragment's instruction does not cross a word boundary.
   Align so the next fragment's instruction does not cross a word boundary.
   Align the current instruction so that if the next instruction
   Align the current instruction so that if the next instruction
       were 3 bytes, it would not cross a word boundary.
       were 3 bytes, it would not cross a word boundary.
 
 
   We can align with:
   We can align with:
 
 
   zeros    - This is easy; always insert zeros.
   zeros    - This is easy; always insert zeros.
   nops     - 3-byte and 2-byte instructions
   nops     - 3-byte and 2-byte instructions
              2 - 2-byte nop
              2 - 2-byte nop
              3 - 3-byte nop
              3 - 3-byte nop
              4 - 2 2-byte nops
              4 - 2 2-byte nops
              >=5 : 3-byte instruction + fn (n-3)
              >=5 : 3-byte instruction + fn (n-3)
   widening - widen previous instructions.  */
   widening - widen previous instructions.  */
 
 
static offsetT
static offsetT
get_aligned_diff (fragS *fragP, addressT address, offsetT *max_diff)
get_aligned_diff (fragS *fragP, addressT address, offsetT *max_diff)
{
{
  addressT target_address, loop_insn_offset;
  addressT target_address, loop_insn_offset;
  int target_size;
  int target_size;
  xtensa_opcode loop_opcode;
  xtensa_opcode loop_opcode;
  bfd_boolean is_loop;
  bfd_boolean is_loop;
  int align_power;
  int align_power;
  offsetT opt_diff;
  offsetT opt_diff;
  offsetT branch_align;
  offsetT branch_align;
  fragS *loop_frag;
  fragS *loop_frag;
 
 
  gas_assert (fragP->fr_type == rs_machine_dependent);
  gas_assert (fragP->fr_type == rs_machine_dependent);
  switch (fragP->fr_subtype)
  switch (fragP->fr_subtype)
    {
    {
    case RELAX_DESIRE_ALIGN:
    case RELAX_DESIRE_ALIGN:
      target_size = next_frag_format_size (fragP);
      target_size = next_frag_format_size (fragP);
      if (target_size == XTENSA_UNDEFINED)
      if (target_size == XTENSA_UNDEFINED)
        target_size = 3;
        target_size = 3;
      align_power = branch_align_power (now_seg);
      align_power = branch_align_power (now_seg);
      branch_align = 1 << align_power;
      branch_align = 1 << align_power;
      /* Don't count on the section alignment being as large as the target.  */
      /* Don't count on the section alignment being as large as the target.  */
      if (target_size > branch_align)
      if (target_size > branch_align)
        target_size = branch_align;
        target_size = branch_align;
      opt_diff = get_text_align_fill_size (address, align_power,
      opt_diff = get_text_align_fill_size (address, align_power,
                                           target_size, FALSE, FALSE);
                                           target_size, FALSE, FALSE);
 
 
      *max_diff = (opt_diff + branch_align
      *max_diff = (opt_diff + branch_align
                   - (target_size + ((address + opt_diff) % branch_align)));
                   - (target_size + ((address + opt_diff) % branch_align)));
      gas_assert (*max_diff >= opt_diff);
      gas_assert (*max_diff >= opt_diff);
      return opt_diff;
      return opt_diff;
 
 
    case RELAX_ALIGN_NEXT_OPCODE:
    case RELAX_ALIGN_NEXT_OPCODE:
      /* The next non-empty frag after this one holds the LOOP instruction
      /* The next non-empty frag after this one holds the LOOP instruction
         that needs to be aligned.  The required alignment depends on the
         that needs to be aligned.  The required alignment depends on the
         size of the next non-empty frag after the loop frag, i.e., the
         size of the next non-empty frag after the loop frag, i.e., the
         first instruction in the loop.  */
         first instruction in the loop.  */
      loop_frag = next_non_empty_frag (fragP);
      loop_frag = next_non_empty_frag (fragP);
      target_size = get_loop_align_size (next_frag_format_size (loop_frag));
      target_size = get_loop_align_size (next_frag_format_size (loop_frag));
      loop_insn_offset = 0;
      loop_insn_offset = 0;
      is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
      is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
      gas_assert (is_loop);
      gas_assert (is_loop);
 
 
      /* If the loop has been expanded then the LOOP instruction
      /* If the loop has been expanded then the LOOP instruction
         could be at an offset from this fragment.  */
         could be at an offset from this fragment.  */
      if (loop_frag->tc_frag_data.slot_subtypes[0] != RELAX_IMMED)
      if (loop_frag->tc_frag_data.slot_subtypes[0] != RELAX_IMMED)
        loop_insn_offset = get_expanded_loop_offset (loop_opcode);
        loop_insn_offset = get_expanded_loop_offset (loop_opcode);
 
 
      /* In an ideal world, which is what we are shooting for here,
      /* In an ideal world, which is what we are shooting for here,
         we wouldn't need to use any NOPs immediately prior to the
         we wouldn't need to use any NOPs immediately prior to the
         LOOP instruction.  If this approach fails, relax_frag_loop_align
         LOOP instruction.  If this approach fails, relax_frag_loop_align
         will call get_noop_aligned_address.  */
         will call get_noop_aligned_address.  */
      target_address =
      target_address =
        address + loop_insn_offset + xg_get_single_size (loop_opcode);
        address + loop_insn_offset + xg_get_single_size (loop_opcode);
      align_power = get_text_align_power (target_size);
      align_power = get_text_align_power (target_size);
      opt_diff = get_text_align_fill_size (target_address, align_power,
      opt_diff = get_text_align_fill_size (target_address, align_power,
                                           target_size, FALSE, FALSE);
                                           target_size, FALSE, FALSE);
 
 
      *max_diff = xtensa_fetch_width
      *max_diff = xtensa_fetch_width
        - ((target_address + opt_diff) % xtensa_fetch_width)
        - ((target_address + opt_diff) % xtensa_fetch_width)
        - target_size + opt_diff;
        - target_size + opt_diff;
      gas_assert (*max_diff >= opt_diff);
      gas_assert (*max_diff >= opt_diff);
      return opt_diff;
      return opt_diff;
 
 
    default:
    default:
      break;
      break;
    }
    }
  gas_assert (0);
  gas_assert (0);
  return 0;
  return 0;
}
}
 
 


/* md_relax_frag Hook and Helper Functions.  */
/* md_relax_frag Hook and Helper Functions.  */
 
 
static long relax_frag_loop_align (fragS *, long);
static long relax_frag_loop_align (fragS *, long);
static long relax_frag_for_align (fragS *, long);
static long relax_frag_for_align (fragS *, long);
static long relax_frag_immed
static long relax_frag_immed
  (segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
  (segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
 
 
 
 
/* Return the number of bytes added to this fragment, given that the
/* Return the number of bytes added to this fragment, given that the
   input has been stretched already by "stretch".  */
   input has been stretched already by "stretch".  */
 
 
long
long
xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int unreported = fragP->tc_frag_data.unreported_expansion;
  int unreported = fragP->tc_frag_data.unreported_expansion;
  long new_stretch = 0;
  long new_stretch = 0;
  char *file_name;
  char *file_name;
  unsigned line;
  unsigned line;
  int lit_size;
  int lit_size;
  static xtensa_insnbuf vbuf = NULL;
  static xtensa_insnbuf vbuf = NULL;
  int slot, num_slots;
  int slot, num_slots;
  xtensa_format fmt;
  xtensa_format fmt;
 
 
  as_where (&file_name, &line);
  as_where (&file_name, &line);
  new_logical_line (fragP->fr_file, fragP->fr_line);
  new_logical_line (fragP->fr_file, fragP->fr_line);
 
 
  fragP->tc_frag_data.unreported_expansion = 0;
  fragP->tc_frag_data.unreported_expansion = 0;
 
 
  switch (fragP->fr_subtype)
  switch (fragP->fr_subtype)
    {
    {
    case RELAX_ALIGN_NEXT_OPCODE:
    case RELAX_ALIGN_NEXT_OPCODE:
      /* Always convert.  */
      /* Always convert.  */
      if (fragP->tc_frag_data.relax_seen)
      if (fragP->tc_frag_data.relax_seen)
        new_stretch = relax_frag_loop_align (fragP, stretch);
        new_stretch = relax_frag_loop_align (fragP, stretch);
      break;
      break;
 
 
    case RELAX_LOOP_END:
    case RELAX_LOOP_END:
      /* Do nothing.  */
      /* Do nothing.  */
      break;
      break;
 
 
    case RELAX_LOOP_END_ADD_NOP:
    case RELAX_LOOP_END_ADD_NOP:
      /* Add a NOP and switch to .fill 0.  */
      /* Add a NOP and switch to .fill 0.  */
      new_stretch = relax_frag_add_nop (fragP);
      new_stretch = relax_frag_add_nop (fragP);
      frag_wane (fragP);
      frag_wane (fragP);
      break;
      break;
 
 
    case RELAX_DESIRE_ALIGN:
    case RELAX_DESIRE_ALIGN:
      /* Do nothing. The narrowing before this frag will either align
      /* Do nothing. The narrowing before this frag will either align
         it or not.  */
         it or not.  */
      break;
      break;
 
 
    case RELAX_LITERAL:
    case RELAX_LITERAL:
    case RELAX_LITERAL_FINAL:
    case RELAX_LITERAL_FINAL:
      return 0;
      return 0;
 
 
    case RELAX_LITERAL_NR:
    case RELAX_LITERAL_NR:
      lit_size = 4;
      lit_size = 4;
      fragP->fr_subtype = RELAX_LITERAL_FINAL;
      fragP->fr_subtype = RELAX_LITERAL_FINAL;
      gas_assert (unreported == lit_size);
      gas_assert (unreported == lit_size);
      memset (&fragP->fr_literal[fragP->fr_fix], 0, 4);
      memset (&fragP->fr_literal[fragP->fr_fix], 0, 4);
      fragP->fr_var -= lit_size;
      fragP->fr_var -= lit_size;
      fragP->fr_fix += lit_size;
      fragP->fr_fix += lit_size;
      new_stretch = 4;
      new_stretch = 4;
      break;
      break;
 
 
    case RELAX_SLOTS:
    case RELAX_SLOTS:
      if (vbuf == NULL)
      if (vbuf == NULL)
        vbuf = xtensa_insnbuf_alloc (isa);
        vbuf = xtensa_insnbuf_alloc (isa);
 
 
      xtensa_insnbuf_from_chars
      xtensa_insnbuf_from_chars
        (isa, vbuf, (unsigned char *) fragP->fr_opcode, 0);
        (isa, vbuf, (unsigned char *) fragP->fr_opcode, 0);
      fmt = xtensa_format_decode (isa, vbuf);
      fmt = xtensa_format_decode (isa, vbuf);
      num_slots = xtensa_format_num_slots (isa, fmt);
      num_slots = xtensa_format_num_slots (isa, fmt);
 
 
      for (slot = 0; slot < num_slots; slot++)
      for (slot = 0; slot < num_slots; slot++)
        {
        {
          switch (fragP->tc_frag_data.slot_subtypes[slot])
          switch (fragP->tc_frag_data.slot_subtypes[slot])
            {
            {
            case RELAX_NARROW:
            case RELAX_NARROW:
              if (fragP->tc_frag_data.relax_seen)
              if (fragP->tc_frag_data.relax_seen)
                new_stretch += relax_frag_for_align (fragP, stretch);
                new_stretch += relax_frag_for_align (fragP, stretch);
              break;
              break;
 
 
            case RELAX_IMMED:
            case RELAX_IMMED:
            case RELAX_IMMED_STEP1:
            case RELAX_IMMED_STEP1:
            case RELAX_IMMED_STEP2:
            case RELAX_IMMED_STEP2:
            case RELAX_IMMED_STEP3:
            case RELAX_IMMED_STEP3:
              /* Place the immediate.  */
              /* Place the immediate.  */
              new_stretch += relax_frag_immed
              new_stretch += relax_frag_immed
                (now_seg, fragP, stretch,
                (now_seg, fragP, stretch,
                 fragP->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
                 fragP->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
                 fmt, slot, stretched_p, FALSE);
                 fmt, slot, stretched_p, FALSE);
              break;
              break;
 
 
            default:
            default:
              /* This is OK; see the note in xg_assemble_vliw_tokens.  */
              /* This is OK; see the note in xg_assemble_vliw_tokens.  */
              break;
              break;
            }
            }
        }
        }
      break;
      break;
 
 
    case RELAX_LITERAL_POOL_BEGIN:
    case RELAX_LITERAL_POOL_BEGIN:
    case RELAX_LITERAL_POOL_END:
    case RELAX_LITERAL_POOL_END:
    case RELAX_MAYBE_UNREACHABLE:
    case RELAX_MAYBE_UNREACHABLE:
    case RELAX_MAYBE_DESIRE_ALIGN:
    case RELAX_MAYBE_DESIRE_ALIGN:
      /* No relaxation required.  */
      /* No relaxation required.  */
      break;
      break;
 
 
    case RELAX_FILL_NOP:
    case RELAX_FILL_NOP:
    case RELAX_UNREACHABLE:
    case RELAX_UNREACHABLE:
      if (fragP->tc_frag_data.relax_seen)
      if (fragP->tc_frag_data.relax_seen)
        new_stretch += relax_frag_for_align (fragP, stretch);
        new_stretch += relax_frag_for_align (fragP, stretch);
      break;
      break;
 
 
    default:
    default:
      as_bad (_("bad relaxation state"));
      as_bad (_("bad relaxation state"));
    }
    }
 
 
  /* Tell gas we need another relaxation pass.  */
  /* Tell gas we need another relaxation pass.  */
  if (! fragP->tc_frag_data.relax_seen)
  if (! fragP->tc_frag_data.relax_seen)
    {
    {
      fragP->tc_frag_data.relax_seen = TRUE;
      fragP->tc_frag_data.relax_seen = TRUE;
      *stretched_p = 1;
      *stretched_p = 1;
    }
    }
 
 
  new_logical_line (file_name, line);
  new_logical_line (file_name, line);
  return new_stretch;
  return new_stretch;
}
}
 
 
 
 
static long
static long
relax_frag_loop_align (fragS *fragP, long stretch)
relax_frag_loop_align (fragS *fragP, long stretch)
{
{
  addressT old_address, old_next_address, old_size;
  addressT old_address, old_next_address, old_size;
  addressT new_address, new_next_address, new_size;
  addressT new_address, new_next_address, new_size;
  addressT growth;
  addressT growth;
 
 
  /* All the frags with relax_frag_for_alignment prior to this one in the
  /* All the frags with relax_frag_for_alignment prior to this one in the
     section have been done, hopefully eliminating the need for a NOP here.
     section have been done, hopefully eliminating the need for a NOP here.
     But, this will put it in if necessary.  */
     But, this will put it in if necessary.  */
 
 
  /* Calculate the old address of this fragment and the next fragment.  */
  /* Calculate the old address of this fragment and the next fragment.  */
  old_address = fragP->fr_address - stretch;
  old_address = fragP->fr_address - stretch;
  old_next_address = (fragP->fr_address - stretch + fragP->fr_fix +
  old_next_address = (fragP->fr_address - stretch + fragP->fr_fix +
                      fragP->tc_frag_data.text_expansion[0]);
                      fragP->tc_frag_data.text_expansion[0]);
  old_size = old_next_address - old_address;
  old_size = old_next_address - old_address;
 
 
  /* Calculate the new address of this fragment and the next fragment.  */
  /* Calculate the new address of this fragment and the next fragment.  */
  new_address = fragP->fr_address;
  new_address = fragP->fr_address;
  new_next_address =
  new_next_address =
    get_noop_aligned_address (fragP, fragP->fr_address + fragP->fr_fix);
    get_noop_aligned_address (fragP, fragP->fr_address + fragP->fr_fix);
  new_size = new_next_address - new_address;
  new_size = new_next_address - new_address;
 
 
  growth = new_size - old_size;
  growth = new_size - old_size;
 
 
  /* Fix up the text_expansion field and return the new growth.  */
  /* Fix up the text_expansion field and return the new growth.  */
  fragP->tc_frag_data.text_expansion[0] += growth;
  fragP->tc_frag_data.text_expansion[0] += growth;
  return growth;
  return growth;
}
}
 
 
 
 
/* Add a NOP instruction.  */
/* Add a NOP instruction.  */
 
 
static long
static long
relax_frag_add_nop (fragS *fragP)
relax_frag_add_nop (fragS *fragP)
{
{
  char *nop_buf = fragP->fr_literal + fragP->fr_fix;
  char *nop_buf = fragP->fr_literal + fragP->fr_fix;
  int length = fragP->tc_frag_data.is_no_density ? 3 : 2;
  int length = fragP->tc_frag_data.is_no_density ? 3 : 2;
  assemble_nop (length, nop_buf);
  assemble_nop (length, nop_buf);
  fragP->tc_frag_data.is_insn = TRUE;
  fragP->tc_frag_data.is_insn = TRUE;
 
 
  if (fragP->fr_var < length)
  if (fragP->fr_var < length)
    {
    {
      as_fatal (_("fr_var (%ld) < length (%d)"), (long) fragP->fr_var, length);
      as_fatal (_("fr_var (%ld) < length (%d)"), (long) fragP->fr_var, length);
      return 0;
      return 0;
    }
    }
 
 
  fragP->fr_fix += length;
  fragP->fr_fix += length;
  fragP->fr_var -= length;
  fragP->fr_var -= length;
  return length;
  return length;
}
}
 
 
 
 
static long future_alignment_required (fragS *, long);
static long future_alignment_required (fragS *, long);
 
 
static long
static long
relax_frag_for_align (fragS *fragP, long stretch)
relax_frag_for_align (fragS *fragP, long stretch)
{
{
  /* Overview of the relaxation procedure for alignment:
  /* Overview of the relaxation procedure for alignment:
     We can widen with NOPs or by widening instructions or by filling
     We can widen with NOPs or by widening instructions or by filling
     bytes after jump instructions.  Find the opportune places and widen
     bytes after jump instructions.  Find the opportune places and widen
     them if necessary.  */
     them if necessary.  */
 
 
  long stretch_me;
  long stretch_me;
  long diff;
  long diff;
 
 
  gas_assert (fragP->fr_subtype == RELAX_FILL_NOP
  gas_assert (fragP->fr_subtype == RELAX_FILL_NOP
          || fragP->fr_subtype == RELAX_UNREACHABLE
          || fragP->fr_subtype == RELAX_UNREACHABLE
          || (fragP->fr_subtype == RELAX_SLOTS
          || (fragP->fr_subtype == RELAX_SLOTS
              && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW));
              && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW));
 
 
  stretch_me = future_alignment_required (fragP, stretch);
  stretch_me = future_alignment_required (fragP, stretch);
  diff = stretch_me - fragP->tc_frag_data.text_expansion[0];
  diff = stretch_me - fragP->tc_frag_data.text_expansion[0];
  if (diff == 0)
  if (diff == 0)
    return 0;
    return 0;
 
 
  if (diff < 0)
  if (diff < 0)
    {
    {
      /* We expanded on a previous pass.  Can we shrink now?  */
      /* We expanded on a previous pass.  Can we shrink now?  */
      long shrink = fragP->tc_frag_data.text_expansion[0] - stretch_me;
      long shrink = fragP->tc_frag_data.text_expansion[0] - stretch_me;
      if (shrink <= stretch && stretch > 0)
      if (shrink <= stretch && stretch > 0)
        {
        {
          fragP->tc_frag_data.text_expansion[0] = stretch_me;
          fragP->tc_frag_data.text_expansion[0] = stretch_me;
          return -shrink;
          return -shrink;
        }
        }
      return 0;
      return 0;
    }
    }
 
 
  /* Below here, diff > 0.  */
  /* Below here, diff > 0.  */
  fragP->tc_frag_data.text_expansion[0] = stretch_me;
  fragP->tc_frag_data.text_expansion[0] = stretch_me;
 
 
  return diff;
  return diff;
}
}
 
 
 
 
/* Return the address of the next frag that should be aligned.
/* Return the address of the next frag that should be aligned.
 
 
   By "address" we mean the address it _would_ be at if there
   By "address" we mean the address it _would_ be at if there
   is no action taken to align it between here and the target frag.
   is no action taken to align it between here and the target frag.
   In other words, if no narrows and no fill nops are used between
   In other words, if no narrows and no fill nops are used between
   here and the frag to align, _even_if_ some of the frags we use
   here and the frag to align, _even_if_ some of the frags we use
   to align targets have already expanded on a previous relaxation
   to align targets have already expanded on a previous relaxation
   pass.
   pass.
 
 
   Also, count each frag that may be used to help align the target.
   Also, count each frag that may be used to help align the target.
 
 
   Return 0 if there are no frags left in the chain that need to be
   Return 0 if there are no frags left in the chain that need to be
   aligned.  */
   aligned.  */
 
 
static addressT
static addressT
find_address_of_next_align_frag (fragS **fragPP,
find_address_of_next_align_frag (fragS **fragPP,
                                 int *wide_nops,
                                 int *wide_nops,
                                 int *narrow_nops,
                                 int *narrow_nops,
                                 int *widens,
                                 int *widens,
                                 bfd_boolean *paddable)
                                 bfd_boolean *paddable)
{
{
  fragS *fragP = *fragPP;
  fragS *fragP = *fragPP;
  addressT address = fragP->fr_address;
  addressT address = fragP->fr_address;
 
 
  /* Do not reset the counts to 0.  */
  /* Do not reset the counts to 0.  */
 
 
  while (fragP)
  while (fragP)
    {
    {
      /* Limit this to a small search.  */
      /* Limit this to a small search.  */
      if (*widens >= (int) xtensa_fetch_width)
      if (*widens >= (int) xtensa_fetch_width)
        {
        {
          *fragPP = fragP;
          *fragPP = fragP;
          return 0;
          return 0;
        }
        }
      address += fragP->fr_fix;
      address += fragP->fr_fix;
 
 
      if (fragP->fr_type == rs_fill)
      if (fragP->fr_type == rs_fill)
        address += fragP->fr_offset * fragP->fr_var;
        address += fragP->fr_offset * fragP->fr_var;
      else if (fragP->fr_type == rs_machine_dependent)
      else if (fragP->fr_type == rs_machine_dependent)
        {
        {
          switch (fragP->fr_subtype)
          switch (fragP->fr_subtype)
            {
            {
            case RELAX_UNREACHABLE:
            case RELAX_UNREACHABLE:
              *paddable = TRUE;
              *paddable = TRUE;
              break;
              break;
 
 
            case RELAX_FILL_NOP:
            case RELAX_FILL_NOP:
              (*wide_nops)++;
              (*wide_nops)++;
              if (!fragP->tc_frag_data.is_no_density)
              if (!fragP->tc_frag_data.is_no_density)
                (*narrow_nops)++;
                (*narrow_nops)++;
              break;
              break;
 
 
            case RELAX_SLOTS:
            case RELAX_SLOTS:
              if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
              if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
                {
                {
                  (*widens)++;
                  (*widens)++;
                  break;
                  break;
                }
                }
              address += total_frag_text_expansion (fragP);;
              address += total_frag_text_expansion (fragP);;
              break;
              break;
 
 
            case RELAX_IMMED:
            case RELAX_IMMED:
              address += fragP->tc_frag_data.text_expansion[0];
              address += fragP->tc_frag_data.text_expansion[0];
              break;
              break;
 
 
            case RELAX_ALIGN_NEXT_OPCODE:
            case RELAX_ALIGN_NEXT_OPCODE:
            case RELAX_DESIRE_ALIGN:
            case RELAX_DESIRE_ALIGN:
              *fragPP = fragP;
              *fragPP = fragP;
              return address;
              return address;
 
 
            case RELAX_MAYBE_UNREACHABLE:
            case RELAX_MAYBE_UNREACHABLE:
            case RELAX_MAYBE_DESIRE_ALIGN:
            case RELAX_MAYBE_DESIRE_ALIGN:
              /* Do nothing.  */
              /* Do nothing.  */
              break;
              break;
 
 
            default:
            default:
              /* Just punt if we don't know the type.  */
              /* Just punt if we don't know the type.  */
              *fragPP = fragP;
              *fragPP = fragP;
              return 0;
              return 0;
            }
            }
        }
        }
      else
      else
        {
        {
          /* Just punt if we don't know the type.  */
          /* Just punt if we don't know the type.  */
          *fragPP = fragP;
          *fragPP = fragP;
          return 0;
          return 0;
        }
        }
      fragP = fragP->fr_next;
      fragP = fragP->fr_next;
    }
    }
 
 
  *fragPP = fragP;
  *fragPP = fragP;
  return 0;
  return 0;
}
}
 
 
 
 
static long bytes_to_stretch (fragS *, int, int, int, int);
static long bytes_to_stretch (fragS *, int, int, int, int);
 
 
static long
static long
future_alignment_required (fragS *fragP, long stretch ATTRIBUTE_UNUSED)
future_alignment_required (fragS *fragP, long stretch ATTRIBUTE_UNUSED)
{
{
  fragS *this_frag = fragP;
  fragS *this_frag = fragP;
  long address;
  long address;
  int num_widens = 0;
  int num_widens = 0;
  int wide_nops = 0;
  int wide_nops = 0;
  int narrow_nops = 0;
  int narrow_nops = 0;
  bfd_boolean paddable = FALSE;
  bfd_boolean paddable = FALSE;
  offsetT local_opt_diff;
  offsetT local_opt_diff;
  offsetT opt_diff;
  offsetT opt_diff;
  offsetT max_diff;
  offsetT max_diff;
  int stretch_amount = 0;
  int stretch_amount = 0;
  int local_stretch_amount;
  int local_stretch_amount;
  int global_stretch_amount;
  int global_stretch_amount;
 
 
  address = find_address_of_next_align_frag
  address = find_address_of_next_align_frag
    (&fragP, &wide_nops, &narrow_nops, &num_widens, &paddable);
    (&fragP, &wide_nops, &narrow_nops, &num_widens, &paddable);
 
 
  if (!address)
  if (!address)
    {
    {
      if (this_frag->tc_frag_data.is_aligning_branch)
      if (this_frag->tc_frag_data.is_aligning_branch)
        this_frag->tc_frag_data.slot_subtypes[0] = RELAX_IMMED;
        this_frag->tc_frag_data.slot_subtypes[0] = RELAX_IMMED;
      else
      else
        frag_wane (this_frag);
        frag_wane (this_frag);
    }
    }
  else
  else
    {
    {
      local_opt_diff = get_aligned_diff (fragP, address, &max_diff);
      local_opt_diff = get_aligned_diff (fragP, address, &max_diff);
      opt_diff = local_opt_diff;
      opt_diff = local_opt_diff;
      gas_assert (opt_diff >= 0);
      gas_assert (opt_diff >= 0);
      gas_assert (max_diff >= opt_diff);
      gas_assert (max_diff >= opt_diff);
      if (max_diff == 0)
      if (max_diff == 0)
        return 0;
        return 0;
 
 
      if (fragP)
      if (fragP)
        fragP = fragP->fr_next;
        fragP = fragP->fr_next;
 
 
      while (fragP && opt_diff < max_diff && address)
      while (fragP && opt_diff < max_diff && address)
        {
        {
          /* We only use these to determine if we can exit early
          /* We only use these to determine if we can exit early
             because there will be plenty of ways to align future
             because there will be plenty of ways to align future
             align frags.  */
             align frags.  */
          int glob_widens = 0;
          int glob_widens = 0;
          int dnn = 0;
          int dnn = 0;
          int dw = 0;
          int dw = 0;
          bfd_boolean glob_pad = 0;
          bfd_boolean glob_pad = 0;
          address = find_address_of_next_align_frag
          address = find_address_of_next_align_frag
            (&fragP, &glob_widens, &dnn, &dw, &glob_pad);
            (&fragP, &glob_widens, &dnn, &dw, &glob_pad);
          /* If there is a padable portion, then skip.  */
          /* If there is a padable portion, then skip.  */
          if (glob_pad || glob_widens >= (1 << branch_align_power (now_seg)))
          if (glob_pad || glob_widens >= (1 << branch_align_power (now_seg)))
            address = 0;
            address = 0;
 
 
          if (address)
          if (address)
            {
            {
              offsetT next_m_diff;
              offsetT next_m_diff;
              offsetT next_o_diff;
              offsetT next_o_diff;
 
 
              /* Downrange frags haven't had stretch added to them yet.  */
              /* Downrange frags haven't had stretch added to them yet.  */
              address += stretch;
              address += stretch;
 
 
              /* The address also includes any text expansion from this
              /* The address also includes any text expansion from this
                 frag in a previous pass, but we don't want that.  */
                 frag in a previous pass, but we don't want that.  */
              address -= this_frag->tc_frag_data.text_expansion[0];
              address -= this_frag->tc_frag_data.text_expansion[0];
 
 
              /* Assume we are going to move at least opt_diff.  In
              /* Assume we are going to move at least opt_diff.  In
                 reality, we might not be able to, but assuming that
                 reality, we might not be able to, but assuming that
                 we will helps catch cases where moving opt_diff pushes
                 we will helps catch cases where moving opt_diff pushes
                 the next target from aligned to unaligned.  */
                 the next target from aligned to unaligned.  */
              address += opt_diff;
              address += opt_diff;
 
 
              next_o_diff = get_aligned_diff (fragP, address, &next_m_diff);
              next_o_diff = get_aligned_diff (fragP, address, &next_m_diff);
 
 
              /* Now cleanup for the adjustments to address.  */
              /* Now cleanup for the adjustments to address.  */
              next_o_diff += opt_diff;
              next_o_diff += opt_diff;
              next_m_diff += opt_diff;
              next_m_diff += opt_diff;
              if (next_o_diff <= max_diff && next_o_diff > opt_diff)
              if (next_o_diff <= max_diff && next_o_diff > opt_diff)
                opt_diff = next_o_diff;
                opt_diff = next_o_diff;
              if (next_m_diff < max_diff)
              if (next_m_diff < max_diff)
                max_diff = next_m_diff;
                max_diff = next_m_diff;
              fragP = fragP->fr_next;
              fragP = fragP->fr_next;
            }
            }
        }
        }
 
 
      /* If there are enough wideners in between, do it.  */
      /* If there are enough wideners in between, do it.  */
      if (paddable)
      if (paddable)
        {
        {
          if (this_frag->fr_subtype == RELAX_UNREACHABLE)
          if (this_frag->fr_subtype == RELAX_UNREACHABLE)
            {
            {
              gas_assert (opt_diff <= UNREACHABLE_MAX_WIDTH);
              gas_assert (opt_diff <= UNREACHABLE_MAX_WIDTH);
              return opt_diff;
              return opt_diff;
            }
            }
          return 0;
          return 0;
        }
        }
      local_stretch_amount
      local_stretch_amount
        = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
        = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
                            num_widens, local_opt_diff);
                            num_widens, local_opt_diff);
      global_stretch_amount
      global_stretch_amount
        = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
        = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
                            num_widens, opt_diff);
                            num_widens, opt_diff);
      /* If the condition below is true, then the frag couldn't
      /* If the condition below is true, then the frag couldn't
         stretch the correct amount for the global case, so we just
         stretch the correct amount for the global case, so we just
         optimize locally.  We'll rely on the subsequent frags to get
         optimize locally.  We'll rely on the subsequent frags to get
         the correct alignment in the global case.  */
         the correct alignment in the global case.  */
      if (global_stretch_amount < local_stretch_amount)
      if (global_stretch_amount < local_stretch_amount)
        stretch_amount = local_stretch_amount;
        stretch_amount = local_stretch_amount;
      else
      else
        stretch_amount = global_stretch_amount;
        stretch_amount = global_stretch_amount;
 
 
      if (this_frag->fr_subtype == RELAX_SLOTS
      if (this_frag->fr_subtype == RELAX_SLOTS
          && this_frag->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
          && this_frag->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
        gas_assert (stretch_amount <= 1);
        gas_assert (stretch_amount <= 1);
      else if (this_frag->fr_subtype == RELAX_FILL_NOP)
      else if (this_frag->fr_subtype == RELAX_FILL_NOP)
        {
        {
          if (this_frag->tc_frag_data.is_no_density)
          if (this_frag->tc_frag_data.is_no_density)
            gas_assert (stretch_amount == 3 || stretch_amount == 0);
            gas_assert (stretch_amount == 3 || stretch_amount == 0);
          else
          else
            gas_assert (stretch_amount <= 3);
            gas_assert (stretch_amount <= 3);
        }
        }
    }
    }
  return stretch_amount;
  return stretch_amount;
}
}
 
 
 
 
/* The idea: widen everything you can to get a target or loop aligned,
/* The idea: widen everything you can to get a target or loop aligned,
   then start using NOPs.
   then start using NOPs.
 
 
   When we must have a NOP, here is a table of how we decide
   When we must have a NOP, here is a table of how we decide
   (so you don't have to fight through the control flow below):
   (so you don't have to fight through the control flow below):
 
 
   wide_nops   = the number of wide NOPs available for aligning
   wide_nops   = the number of wide NOPs available for aligning
   narrow_nops = the number of narrow NOPs available for aligning
   narrow_nops = the number of narrow NOPs available for aligning
                 (a subset of wide_nops)
                 (a subset of wide_nops)
   widens      = the number of narrow instructions that should be widened
   widens      = the number of narrow instructions that should be widened
 
 
   Desired   wide   narrow
   Desired   wide   narrow
   Diff      nop    nop      widens
   Diff      nop    nop      widens
   1           0      0         1
   1           0      0         1
   2           0      1         0
   2           0      1         0
   3a          1      0         0
   3a          1      0         0
    b          0      1         1 (case 3a makes this case unnecessary)
    b          0      1         1 (case 3a makes this case unnecessary)
   4a          1      0         1
   4a          1      0         1
    b          0      2         0
    b          0      2         0
    c          0      1         2 (case 4a makes this case unnecessary)
    c          0      1         2 (case 4a makes this case unnecessary)
   5a          1      0         2
   5a          1      0         2
    b          1      1         0
    b          1      1         0
    c          0      2         1 (case 5b makes this case unnecessary)
    c          0      2         1 (case 5b makes this case unnecessary)
   6a          2      0         0
   6a          2      0         0
    b          1      0         3
    b          1      0         3
    c          0      1         4 (case 6b makes this case unnecessary)
    c          0      1         4 (case 6b makes this case unnecessary)
    d          1      1         1 (case 6a makes this case unnecessary)
    d          1      1         1 (case 6a makes this case unnecessary)
    e          0      2         2 (case 6a makes this case unnecessary)
    e          0      2         2 (case 6a makes this case unnecessary)
    f          0      3         0 (case 6a makes this case unnecessary)
    f          0      3         0 (case 6a makes this case unnecessary)
   7a          1      0         4
   7a          1      0         4
    b          2      0         1
    b          2      0         1
    c          1      1         2 (case 7b makes this case unnecessary)
    c          1      1         2 (case 7b makes this case unnecessary)
    d          0      1         5 (case 7a makes this case unnecessary)
    d          0      1         5 (case 7a makes this case unnecessary)
    e          0      2         3 (case 7b makes this case unnecessary)
    e          0      2         3 (case 7b makes this case unnecessary)
    f          0      3         1 (case 7b makes this case unnecessary)
    f          0      3         1 (case 7b makes this case unnecessary)
    g          1      2         1 (case 7b makes this case unnecessary)
    g          1      2         1 (case 7b makes this case unnecessary)
*/
*/
 
 
static long
static long
bytes_to_stretch (fragS *this_frag,
bytes_to_stretch (fragS *this_frag,
                  int wide_nops,
                  int wide_nops,
                  int narrow_nops,
                  int narrow_nops,
                  int num_widens,
                  int num_widens,
                  int desired_diff)
                  int desired_diff)
{
{
  int bytes_short = desired_diff - num_widens;
  int bytes_short = desired_diff - num_widens;
 
 
  gas_assert (desired_diff >= 0 && desired_diff < 8);
  gas_assert (desired_diff >= 0 && desired_diff < 8);
  if (desired_diff == 0)
  if (desired_diff == 0)
    return 0;
    return 0;
 
 
  gas_assert (wide_nops > 0 || num_widens > 0);
  gas_assert (wide_nops > 0 || num_widens > 0);
 
 
  /* Always prefer widening to NOP-filling.  */
  /* Always prefer widening to NOP-filling.  */
  if (bytes_short < 0)
  if (bytes_short < 0)
    {
    {
      /* There are enough RELAX_NARROW frags after this one
      /* There are enough RELAX_NARROW frags after this one
         to align the target without widening this frag in any way.  */
         to align the target without widening this frag in any way.  */
      return 0;
      return 0;
    }
    }
 
 
  if (bytes_short == 0)
  if (bytes_short == 0)
    {
    {
      /* Widen every narrow between here and the align target
      /* Widen every narrow between here and the align target
         and the align target will be properly aligned.  */
         and the align target will be properly aligned.  */
      if (this_frag->fr_subtype == RELAX_FILL_NOP)
      if (this_frag->fr_subtype == RELAX_FILL_NOP)
        return 0;
        return 0;
      else
      else
        return 1;
        return 1;
    }
    }
 
 
  /* From here we will need at least one NOP to get an alignment.
  /* From here we will need at least one NOP to get an alignment.
     However, we may not be able to align at all, in which case,
     However, we may not be able to align at all, in which case,
     don't widen.  */
     don't widen.  */
  if (this_frag->fr_subtype == RELAX_FILL_NOP)
  if (this_frag->fr_subtype == RELAX_FILL_NOP)
    {
    {
      switch (desired_diff)
      switch (desired_diff)
        {
        {
        case 1:
        case 1:
          return 0;
          return 0;
        case 2:
        case 2:
          if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 1)
          if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 1)
            return 2; /* case 2 */
            return 2; /* case 2 */
          return 0;
          return 0;
        case 3:
        case 3:
          if (wide_nops > 1)
          if (wide_nops > 1)
            return 0;
            return 0;
          else
          else
            return 3; /* case 3a */
            return 3; /* case 3a */
        case 4:
        case 4:
          if (num_widens >= 1 && wide_nops == 1)
          if (num_widens >= 1 && wide_nops == 1)
            return 3; /* case 4a */
            return 3; /* case 4a */
          if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 2)
          if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 2)
            return 2; /* case 4b */
            return 2; /* case 4b */
          return 0;
          return 0;
        case 5:
        case 5:
          if (num_widens >= 2 && wide_nops == 1)
          if (num_widens >= 2 && wide_nops == 1)
            return 3; /* case 5a */
            return 3; /* case 5a */
          /* We will need two nops.  Are there enough nops
          /* We will need two nops.  Are there enough nops
             between here and the align target?  */
             between here and the align target?  */
          if (wide_nops < 2 || narrow_nops == 0)
          if (wide_nops < 2 || narrow_nops == 0)
            return 0;
            return 0;
          /* Are there other nops closer that can serve instead?  */
          /* Are there other nops closer that can serve instead?  */
          if (wide_nops > 2 && narrow_nops > 1)
          if (wide_nops > 2 && narrow_nops > 1)
            return 0;
            return 0;
          /* Take the density one first, because there might not be
          /* Take the density one first, because there might not be
             another density one available.  */
             another density one available.  */
          if (!this_frag->tc_frag_data.is_no_density)
          if (!this_frag->tc_frag_data.is_no_density)
            return 2; /* case 5b narrow */
            return 2; /* case 5b narrow */
          else
          else
            return 3; /* case 5b wide */
            return 3; /* case 5b wide */
          return 0;
          return 0;
        case 6:
        case 6:
          if (wide_nops == 2)
          if (wide_nops == 2)
            return 3; /* case 6a */
            return 3; /* case 6a */
          else if (num_widens >= 3 && wide_nops == 1)
          else if (num_widens >= 3 && wide_nops == 1)
            return 3; /* case 6b */
            return 3; /* case 6b */
          return 0;
          return 0;
        case 7:
        case 7:
          if (wide_nops == 1 && num_widens >= 4)
          if (wide_nops == 1 && num_widens >= 4)
            return 3; /* case 7a */
            return 3; /* case 7a */
          else if (wide_nops == 2 && num_widens >= 1)
          else if (wide_nops == 2 && num_widens >= 1)
            return 3; /* case 7b */
            return 3; /* case 7b */
          return 0;
          return 0;
        default:
        default:
          gas_assert (0);
          gas_assert (0);
        }
        }
    }
    }
  else
  else
    {
    {
      /* We will need a NOP no matter what, but should we widen
      /* We will need a NOP no matter what, but should we widen
         this instruction to help?
         this instruction to help?
 
 
         This is a RELAX_NARROW frag.  */
         This is a RELAX_NARROW frag.  */
      switch (desired_diff)
      switch (desired_diff)
        {
        {
        case 1:
        case 1:
          gas_assert (0);
          gas_assert (0);
          return 0;
          return 0;
        case 2:
        case 2:
        case 3:
        case 3:
          return 0;
          return 0;
        case 4:
        case 4:
          if (wide_nops >= 1 && num_widens == 1)
          if (wide_nops >= 1 && num_widens == 1)
            return 1; /* case 4a */
            return 1; /* case 4a */
          return 0;
          return 0;
        case 5:
        case 5:
          if (wide_nops >= 1 && num_widens == 2)
          if (wide_nops >= 1 && num_widens == 2)
            return 1; /* case 5a */
            return 1; /* case 5a */
          return 0;
          return 0;
        case 6:
        case 6:
          if (wide_nops >= 2)
          if (wide_nops >= 2)
            return 0; /* case 6a */
            return 0; /* case 6a */
          else if (wide_nops >= 1 && num_widens == 3)
          else if (wide_nops >= 1 && num_widens == 3)
            return 1; /* case 6b */
            return 1; /* case 6b */
          return 0;
          return 0;
        case 7:
        case 7:
          if (wide_nops >= 1 && num_widens == 4)
          if (wide_nops >= 1 && num_widens == 4)
            return 1; /* case 7a */
            return 1; /* case 7a */
          else if (wide_nops >= 2 && num_widens == 1)
          else if (wide_nops >= 2 && num_widens == 1)
            return 1; /* case 7b */
            return 1; /* case 7b */
          return 0;
          return 0;
        default:
        default:
          gas_assert (0);
          gas_assert (0);
          return 0;
          return 0;
        }
        }
    }
    }
  gas_assert (0);
  gas_assert (0);
  return 0;
  return 0;
}
}
 
 
 
 
static long
static long
relax_frag_immed (segT segP,
relax_frag_immed (segT segP,
                  fragS *fragP,
                  fragS *fragP,
                  long stretch,
                  long stretch,
                  int min_steps,
                  int min_steps,
                  xtensa_format fmt,
                  xtensa_format fmt,
                  int slot,
                  int slot,
                  int *stretched_p,
                  int *stretched_p,
                  bfd_boolean estimate_only)
                  bfd_boolean estimate_only)
{
{
  TInsn tinsn;
  TInsn tinsn;
  int old_size;
  int old_size;
  bfd_boolean negatable_branch = FALSE;
  bfd_boolean negatable_branch = FALSE;
  bfd_boolean branch_jmp_to_next = FALSE;
  bfd_boolean branch_jmp_to_next = FALSE;
  bfd_boolean from_wide_insn = FALSE;
  bfd_boolean from_wide_insn = FALSE;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  IStack istack;
  IStack istack;
  offsetT frag_offset;
  offsetT frag_offset;
  int num_steps;
  int num_steps;
  int num_text_bytes, num_literal_bytes;
  int num_text_bytes, num_literal_bytes;
  int literal_diff, total_text_diff, this_text_diff;
  int literal_diff, total_text_diff, this_text_diff;
 
 
  gas_assert (fragP->fr_opcode != NULL);
  gas_assert (fragP->fr_opcode != NULL);
 
 
  xg_clear_vinsn (&cur_vinsn);
  xg_clear_vinsn (&cur_vinsn);
  vinsn_from_chars (&cur_vinsn, fragP->fr_opcode);
  vinsn_from_chars (&cur_vinsn, fragP->fr_opcode);
  if (cur_vinsn.num_slots > 1)
  if (cur_vinsn.num_slots > 1)
    from_wide_insn = TRUE;
    from_wide_insn = TRUE;
 
 
  tinsn = cur_vinsn.slots[slot];
  tinsn = cur_vinsn.slots[slot];
  tinsn_immed_from_frag (&tinsn, fragP, slot);
  tinsn_immed_from_frag (&tinsn, fragP, slot);
 
 
  if (estimate_only && xtensa_opcode_is_loop (isa, tinsn.opcode) == 1)
  if (estimate_only && xtensa_opcode_is_loop (isa, tinsn.opcode) == 1)
    return 0;
    return 0;
 
 
  if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
  if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
    branch_jmp_to_next = is_branch_jmp_to_next (&tinsn, fragP);
    branch_jmp_to_next = is_branch_jmp_to_next (&tinsn, fragP);
 
 
  negatable_branch = (xtensa_opcode_is_branch (isa, tinsn.opcode) == 1);
  negatable_branch = (xtensa_opcode_is_branch (isa, tinsn.opcode) == 1);
 
 
  old_size = xtensa_format_length (isa, fmt);
  old_size = xtensa_format_length (isa, fmt);
 
 
  /* Special case: replace a branch to the next instruction with a NOP.
  /* Special case: replace a branch to the next instruction with a NOP.
     This is required to work around a hardware bug in T1040.0 and also
     This is required to work around a hardware bug in T1040.0 and also
     serves as an optimization.  */
     serves as an optimization.  */
 
 
  if (branch_jmp_to_next
  if (branch_jmp_to_next
      && ((old_size == 2) || (old_size == 3))
      && ((old_size == 2) || (old_size == 3))
      && !next_frag_is_loop_target (fragP))
      && !next_frag_is_loop_target (fragP))
    return 0;
    return 0;
 
 
  /* Here is the fun stuff: Get the immediate field from this
  /* Here is the fun stuff: Get the immediate field from this
     instruction.  If it fits, we are done.  If not, find the next
     instruction.  If it fits, we are done.  If not, find the next
     instruction sequence that fits.  */
     instruction sequence that fits.  */
 
 
  frag_offset = fragP->fr_opcode - fragP->fr_literal;
  frag_offset = fragP->fr_opcode - fragP->fr_literal;
  istack_init (&istack);
  istack_init (&istack);
  num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP, frag_offset,
  num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP, frag_offset,
                                 min_steps, stretch);
                                 min_steps, stretch);
  gas_assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
  gas_assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
 
 
  fragP->tc_frag_data.slot_subtypes[slot] = (int) RELAX_IMMED + num_steps;
  fragP->tc_frag_data.slot_subtypes[slot] = (int) RELAX_IMMED + num_steps;
 
 
  /* Figure out the number of bytes needed.  */
  /* Figure out the number of bytes needed.  */
  num_literal_bytes = get_num_stack_literal_bytes (&istack);
  num_literal_bytes = get_num_stack_literal_bytes (&istack);
  literal_diff
  literal_diff
    = num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
    = num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
  num_text_bytes = get_num_stack_text_bytes (&istack);
  num_text_bytes = get_num_stack_text_bytes (&istack);
 
 
  if (from_wide_insn)
  if (from_wide_insn)
    {
    {
      int first = 0;
      int first = 0;
      while (istack.insn[first].opcode == XTENSA_UNDEFINED)
      while (istack.insn[first].opcode == XTENSA_UNDEFINED)
        first++;
        first++;
 
 
      num_text_bytes += old_size;
      num_text_bytes += old_size;
      if (opcode_fits_format_slot (istack.insn[first].opcode, fmt, slot))
      if (opcode_fits_format_slot (istack.insn[first].opcode, fmt, slot))
        num_text_bytes -= xg_get_single_size (istack.insn[first].opcode);
        num_text_bytes -= xg_get_single_size (istack.insn[first].opcode);
      else
      else
        {
        {
          /* The first instruction in the relaxed sequence will go after
          /* The first instruction in the relaxed sequence will go after
             the current wide instruction, and thus its symbolic immediates
             the current wide instruction, and thus its symbolic immediates
             might not fit.  */
             might not fit.  */
 
 
          istack_init (&istack);
          istack_init (&istack);
          num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP,
          num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP,
                                         frag_offset + old_size,
                                         frag_offset + old_size,
                                         min_steps, stretch + old_size);
                                         min_steps, stretch + old_size);
          gas_assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
          gas_assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
 
 
          fragP->tc_frag_data.slot_subtypes[slot]
          fragP->tc_frag_data.slot_subtypes[slot]
            = (int) RELAX_IMMED + num_steps;
            = (int) RELAX_IMMED + num_steps;
 
 
          num_literal_bytes = get_num_stack_literal_bytes (&istack);
          num_literal_bytes = get_num_stack_literal_bytes (&istack);
          literal_diff
          literal_diff
            = num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
            = num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
 
 
          num_text_bytes = get_num_stack_text_bytes (&istack) + old_size;
          num_text_bytes = get_num_stack_text_bytes (&istack) + old_size;
        }
        }
    }
    }
 
 
  total_text_diff = num_text_bytes - old_size;
  total_text_diff = num_text_bytes - old_size;
  this_text_diff = total_text_diff - fragP->tc_frag_data.text_expansion[slot];
  this_text_diff = total_text_diff - fragP->tc_frag_data.text_expansion[slot];
 
 
  /* It MUST get larger.  If not, we could get an infinite loop.  */
  /* It MUST get larger.  If not, we could get an infinite loop.  */
  gas_assert (num_text_bytes >= 0);
  gas_assert (num_text_bytes >= 0);
  gas_assert (literal_diff >= 0);
  gas_assert (literal_diff >= 0);
  gas_assert (total_text_diff >= 0);
  gas_assert (total_text_diff >= 0);
 
 
  fragP->tc_frag_data.text_expansion[slot] = total_text_diff;
  fragP->tc_frag_data.text_expansion[slot] = total_text_diff;
  fragP->tc_frag_data.literal_expansion[slot] = num_literal_bytes;
  fragP->tc_frag_data.literal_expansion[slot] = num_literal_bytes;
  gas_assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
  gas_assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
  gas_assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
  gas_assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
 
 
  /* Find the associated expandable literal for this.  */
  /* Find the associated expandable literal for this.  */
  if (literal_diff != 0)
  if (literal_diff != 0)
    {
    {
      fragS *lit_fragP = fragP->tc_frag_data.literal_frags[slot];
      fragS *lit_fragP = fragP->tc_frag_data.literal_frags[slot];
      if (lit_fragP)
      if (lit_fragP)
        {
        {
          gas_assert (literal_diff == 4);
          gas_assert (literal_diff == 4);
          lit_fragP->tc_frag_data.unreported_expansion += literal_diff;
          lit_fragP->tc_frag_data.unreported_expansion += literal_diff;
 
 
          /* We expect that the literal section state has NOT been
          /* We expect that the literal section state has NOT been
             modified yet.  */
             modified yet.  */
          gas_assert (lit_fragP->fr_type == rs_machine_dependent
          gas_assert (lit_fragP->fr_type == rs_machine_dependent
                  && lit_fragP->fr_subtype == RELAX_LITERAL);
                  && lit_fragP->fr_subtype == RELAX_LITERAL);
          lit_fragP->fr_subtype = RELAX_LITERAL_NR;
          lit_fragP->fr_subtype = RELAX_LITERAL_NR;
 
 
          /* We need to mark this section for another iteration
          /* We need to mark this section for another iteration
             of relaxation.  */
             of relaxation.  */
          (*stretched_p)++;
          (*stretched_p)++;
        }
        }
    }
    }
 
 
  if (negatable_branch && istack.ninsn > 1)
  if (negatable_branch && istack.ninsn > 1)
    update_next_frag_state (fragP);
    update_next_frag_state (fragP);
 
 
  return this_text_diff;
  return this_text_diff;
}
}
 
 


/* md_convert_frag Hook and Helper Functions.  */
/* md_convert_frag Hook and Helper Functions.  */
 
 
static void convert_frag_align_next_opcode (fragS *);
static void convert_frag_align_next_opcode (fragS *);
static void convert_frag_narrow (segT, fragS *, xtensa_format, int);
static void convert_frag_narrow (segT, fragS *, xtensa_format, int);
static void convert_frag_fill_nop (fragS *);
static void convert_frag_fill_nop (fragS *);
static void convert_frag_immed (segT, fragS *, int, xtensa_format, int);
static void convert_frag_immed (segT, fragS *, int, xtensa_format, int);
 
 
void
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragp)
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragp)
{
{
  static xtensa_insnbuf vbuf = NULL;
  static xtensa_insnbuf vbuf = NULL;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int slot;
  int slot;
  int num_slots;
  int num_slots;
  xtensa_format fmt;
  xtensa_format fmt;
  char *file_name;
  char *file_name;
  unsigned line;
  unsigned line;
 
 
  as_where (&file_name, &line);
  as_where (&file_name, &line);
  new_logical_line (fragp->fr_file, fragp->fr_line);
  new_logical_line (fragp->fr_file, fragp->fr_line);
 
 
  switch (fragp->fr_subtype)
  switch (fragp->fr_subtype)
    {
    {
    case RELAX_ALIGN_NEXT_OPCODE:
    case RELAX_ALIGN_NEXT_OPCODE:
      /* Always convert.  */
      /* Always convert.  */
      convert_frag_align_next_opcode (fragp);
      convert_frag_align_next_opcode (fragp);
      break;
      break;
 
 
    case RELAX_DESIRE_ALIGN:
    case RELAX_DESIRE_ALIGN:
      /* Do nothing.  If not aligned already, too bad.  */
      /* Do nothing.  If not aligned already, too bad.  */
      break;
      break;
 
 
    case RELAX_LITERAL:
    case RELAX_LITERAL:
    case RELAX_LITERAL_FINAL:
    case RELAX_LITERAL_FINAL:
      break;
      break;
 
 
    case RELAX_SLOTS:
    case RELAX_SLOTS:
      if (vbuf == NULL)
      if (vbuf == NULL)
        vbuf = xtensa_insnbuf_alloc (isa);
        vbuf = xtensa_insnbuf_alloc (isa);
 
 
      xtensa_insnbuf_from_chars
      xtensa_insnbuf_from_chars
        (isa, vbuf, (unsigned char *) fragp->fr_opcode, 0);
        (isa, vbuf, (unsigned char *) fragp->fr_opcode, 0);
      fmt = xtensa_format_decode (isa, vbuf);
      fmt = xtensa_format_decode (isa, vbuf);
      num_slots = xtensa_format_num_slots (isa, fmt);
      num_slots = xtensa_format_num_slots (isa, fmt);
 
 
      for (slot = 0; slot < num_slots; slot++)
      for (slot = 0; slot < num_slots; slot++)
        {
        {
          switch (fragp->tc_frag_data.slot_subtypes[slot])
          switch (fragp->tc_frag_data.slot_subtypes[slot])
            {
            {
            case RELAX_NARROW:
            case RELAX_NARROW:
              convert_frag_narrow (sec, fragp, fmt, slot);
              convert_frag_narrow (sec, fragp, fmt, slot);
              break;
              break;
 
 
            case RELAX_IMMED:
            case RELAX_IMMED:
            case RELAX_IMMED_STEP1:
            case RELAX_IMMED_STEP1:
            case RELAX_IMMED_STEP2:
            case RELAX_IMMED_STEP2:
            case RELAX_IMMED_STEP3:
            case RELAX_IMMED_STEP3:
              /* Place the immediate.  */
              /* Place the immediate.  */
              convert_frag_immed
              convert_frag_immed
                (sec, fragp,
                (sec, fragp,
                 fragp->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
                 fragp->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
                 fmt, slot);
                 fmt, slot);
              break;
              break;
 
 
            default:
            default:
              /* This is OK because some slots could have
              /* This is OK because some slots could have
                 relaxations and others have none.  */
                 relaxations and others have none.  */
              break;
              break;
            }
            }
        }
        }
      break;
      break;
 
 
    case RELAX_UNREACHABLE:
    case RELAX_UNREACHABLE:
      memset (&fragp->fr_literal[fragp->fr_fix], 0, fragp->fr_var);
      memset (&fragp->fr_literal[fragp->fr_fix], 0, fragp->fr_var);
      fragp->fr_fix += fragp->tc_frag_data.text_expansion[0];
      fragp->fr_fix += fragp->tc_frag_data.text_expansion[0];
      fragp->fr_var -= fragp->tc_frag_data.text_expansion[0];
      fragp->fr_var -= fragp->tc_frag_data.text_expansion[0];
      frag_wane (fragp);
      frag_wane (fragp);
      break;
      break;
 
 
    case RELAX_MAYBE_UNREACHABLE:
    case RELAX_MAYBE_UNREACHABLE:
    case RELAX_MAYBE_DESIRE_ALIGN:
    case RELAX_MAYBE_DESIRE_ALIGN:
      frag_wane (fragp);
      frag_wane (fragp);
      break;
      break;
 
 
    case RELAX_FILL_NOP:
    case RELAX_FILL_NOP:
      convert_frag_fill_nop (fragp);
      convert_frag_fill_nop (fragp);
      break;
      break;
 
 
    case RELAX_LITERAL_NR:
    case RELAX_LITERAL_NR:
      if (use_literal_section)
      if (use_literal_section)
        {
        {
          /* This should have been handled during relaxation.  When
          /* This should have been handled during relaxation.  When
             relaxing a code segment, literals sometimes need to be
             relaxing a code segment, literals sometimes need to be
             added to the corresponding literal segment.  If that
             added to the corresponding literal segment.  If that
             literal segment has already been relaxed, then we end up
             literal segment has already been relaxed, then we end up
             in this situation.  Marking the literal segments as data
             in this situation.  Marking the literal segments as data
             would make this happen less often (since GAS always relaxes
             would make this happen less often (since GAS always relaxes
             code before data), but we could still get into trouble if
             code before data), but we could still get into trouble if
             there are instructions in a segment that is not marked as
             there are instructions in a segment that is not marked as
             containing code.  Until we can implement a better solution,
             containing code.  Until we can implement a better solution,
             cheat and adjust the addresses of all the following frags.
             cheat and adjust the addresses of all the following frags.
             This could break subsequent alignments, but the linker's
             This could break subsequent alignments, but the linker's
             literal coalescing will do that anyway.  */
             literal coalescing will do that anyway.  */
 
 
          fragS *f;
          fragS *f;
          fragp->fr_subtype = RELAX_LITERAL_FINAL;
          fragp->fr_subtype = RELAX_LITERAL_FINAL;
          gas_assert (fragp->tc_frag_data.unreported_expansion == 4);
          gas_assert (fragp->tc_frag_data.unreported_expansion == 4);
          memset (&fragp->fr_literal[fragp->fr_fix], 0, 4);
          memset (&fragp->fr_literal[fragp->fr_fix], 0, 4);
          fragp->fr_var -= 4;
          fragp->fr_var -= 4;
          fragp->fr_fix += 4;
          fragp->fr_fix += 4;
          for (f = fragp->fr_next; f; f = f->fr_next)
          for (f = fragp->fr_next; f; f = f->fr_next)
            f->fr_address += 4;
            f->fr_address += 4;
        }
        }
      else
      else
        as_bad (_("invalid relaxation fragment result"));
        as_bad (_("invalid relaxation fragment result"));
      break;
      break;
    }
    }
 
 
  fragp->fr_var = 0;
  fragp->fr_var = 0;
  new_logical_line (file_name, line);
  new_logical_line (file_name, line);
}
}
 
 
 
 
static void
static void
convert_frag_align_next_opcode (fragS *fragp)
convert_frag_align_next_opcode (fragS *fragp)
{
{
  char *nop_buf;                /* Location for Writing.  */
  char *nop_buf;                /* Location for Writing.  */
  bfd_boolean use_no_density = fragp->tc_frag_data.is_no_density;
  bfd_boolean use_no_density = fragp->tc_frag_data.is_no_density;
  addressT aligned_address;
  addressT aligned_address;
  offsetT fill_size;
  offsetT fill_size;
  int nop, nop_count;
  int nop, nop_count;
 
 
  aligned_address = get_noop_aligned_address (fragp, fragp->fr_address +
  aligned_address = get_noop_aligned_address (fragp, fragp->fr_address +
                                              fragp->fr_fix);
                                              fragp->fr_fix);
  fill_size = aligned_address - (fragp->fr_address + fragp->fr_fix);
  fill_size = aligned_address - (fragp->fr_address + fragp->fr_fix);
  nop_count = get_text_align_nop_count (fill_size, use_no_density);
  nop_count = get_text_align_nop_count (fill_size, use_no_density);
  nop_buf = fragp->fr_literal + fragp->fr_fix;
  nop_buf = fragp->fr_literal + fragp->fr_fix;
 
 
  for (nop = 0; nop < nop_count; nop++)
  for (nop = 0; nop < nop_count; nop++)
    {
    {
      int nop_size;
      int nop_size;
      nop_size = get_text_align_nth_nop_size (fill_size, nop, use_no_density);
      nop_size = get_text_align_nth_nop_size (fill_size, nop, use_no_density);
 
 
      assemble_nop (nop_size, nop_buf);
      assemble_nop (nop_size, nop_buf);
      nop_buf += nop_size;
      nop_buf += nop_size;
    }
    }
 
 
  fragp->fr_fix += fill_size;
  fragp->fr_fix += fill_size;
  fragp->fr_var -= fill_size;
  fragp->fr_var -= fill_size;
}
}
 
 
 
 
static void
static void
convert_frag_narrow (segT segP, fragS *fragP, xtensa_format fmt, int slot)
convert_frag_narrow (segT segP, fragS *fragP, xtensa_format fmt, int slot)
{
{
  TInsn tinsn, single_target;
  TInsn tinsn, single_target;
  int size, old_size, diff;
  int size, old_size, diff;
  offsetT frag_offset;
  offsetT frag_offset;
 
 
  gas_assert (slot == 0);
  gas_assert (slot == 0);
  tinsn_from_chars (&tinsn, fragP->fr_opcode, 0);
  tinsn_from_chars (&tinsn, fragP->fr_opcode, 0);
 
 
  if (fragP->tc_frag_data.is_aligning_branch == 1)
  if (fragP->tc_frag_data.is_aligning_branch == 1)
    {
    {
      gas_assert (fragP->tc_frag_data.text_expansion[0] == 1
      gas_assert (fragP->tc_frag_data.text_expansion[0] == 1
              || fragP->tc_frag_data.text_expansion[0] == 0);
              || fragP->tc_frag_data.text_expansion[0] == 0);
      convert_frag_immed (segP, fragP, fragP->tc_frag_data.text_expansion[0],
      convert_frag_immed (segP, fragP, fragP->tc_frag_data.text_expansion[0],
                          fmt, slot);
                          fmt, slot);
      return;
      return;
    }
    }
 
 
  if (fragP->tc_frag_data.text_expansion[0] == 0)
  if (fragP->tc_frag_data.text_expansion[0] == 0)
    {
    {
      /* No conversion.  */
      /* No conversion.  */
      fragP->fr_var = 0;
      fragP->fr_var = 0;
      return;
      return;
    }
    }
 
 
  gas_assert (fragP->fr_opcode != NULL);
  gas_assert (fragP->fr_opcode != NULL);
 
 
  /* Frags in this relaxation state should only contain
  /* Frags in this relaxation state should only contain
     single instruction bundles.  */
     single instruction bundles.  */
  tinsn_immed_from_frag (&tinsn, fragP, 0);
  tinsn_immed_from_frag (&tinsn, fragP, 0);
 
 
  /* Just convert it to a wide form....  */
  /* Just convert it to a wide form....  */
  size = 0;
  size = 0;
  old_size = xg_get_single_size (tinsn.opcode);
  old_size = xg_get_single_size (tinsn.opcode);
 
 
  tinsn_init (&single_target);
  tinsn_init (&single_target);
  frag_offset = fragP->fr_opcode - fragP->fr_literal;
  frag_offset = fragP->fr_opcode - fragP->fr_literal;
 
 
  if (! xg_is_single_relaxable_insn (&tinsn, &single_target, FALSE))
  if (! xg_is_single_relaxable_insn (&tinsn, &single_target, FALSE))
    {
    {
      as_bad (_("unable to widen instruction"));
      as_bad (_("unable to widen instruction"));
      return;
      return;
    }
    }
 
 
  size = xg_get_single_size (single_target.opcode);
  size = xg_get_single_size (single_target.opcode);
  xg_emit_insn_to_buf (&single_target, fragP->fr_opcode, fragP,
  xg_emit_insn_to_buf (&single_target, fragP->fr_opcode, fragP,
                       frag_offset, TRUE);
                       frag_offset, TRUE);
 
 
  diff = size - old_size;
  diff = size - old_size;
  gas_assert (diff >= 0);
  gas_assert (diff >= 0);
  gas_assert (diff <= fragP->fr_var);
  gas_assert (diff <= fragP->fr_var);
  fragP->fr_var -= diff;
  fragP->fr_var -= diff;
  fragP->fr_fix += diff;
  fragP->fr_fix += diff;
 
 
  /* clean it up */
  /* clean it up */
  fragP->fr_var = 0;
  fragP->fr_var = 0;
}
}
 
 
 
 
static void
static void
convert_frag_fill_nop (fragS *fragP)
convert_frag_fill_nop (fragS *fragP)
{
{
  char *loc = &fragP->fr_literal[fragP->fr_fix];
  char *loc = &fragP->fr_literal[fragP->fr_fix];
  int size = fragP->tc_frag_data.text_expansion[0];
  int size = fragP->tc_frag_data.text_expansion[0];
  gas_assert ((unsigned) size == (fragP->fr_next->fr_address
  gas_assert ((unsigned) size == (fragP->fr_next->fr_address
                              - fragP->fr_address - fragP->fr_fix));
                              - fragP->fr_address - fragP->fr_fix));
  if (size == 0)
  if (size == 0)
    {
    {
      /* No conversion.  */
      /* No conversion.  */
      fragP->fr_var = 0;
      fragP->fr_var = 0;
      return;
      return;
    }
    }
  assemble_nop (size, loc);
  assemble_nop (size, loc);
  fragP->tc_frag_data.is_insn = TRUE;
  fragP->tc_frag_data.is_insn = TRUE;
  fragP->fr_var -= size;
  fragP->fr_var -= size;
  fragP->fr_fix += size;
  fragP->fr_fix += size;
  frag_wane (fragP);
  frag_wane (fragP);
}
}
 
 
 
 
static fixS *fix_new_exp_in_seg
static fixS *fix_new_exp_in_seg
  (segT, subsegT, fragS *, int, int, expressionS *, int,
  (segT, subsegT, fragS *, int, int, expressionS *, int,
   bfd_reloc_code_real_type);
   bfd_reloc_code_real_type);
static void convert_frag_immed_finish_loop (segT, fragS *, TInsn *);
static void convert_frag_immed_finish_loop (segT, fragS *, TInsn *);
 
 
static void
static void
convert_frag_immed (segT segP,
convert_frag_immed (segT segP,
                    fragS *fragP,
                    fragS *fragP,
                    int min_steps,
                    int min_steps,
                    xtensa_format fmt,
                    xtensa_format fmt,
                    int slot)
                    int slot)
{
{
  char *immed_instr = fragP->fr_opcode;
  char *immed_instr = fragP->fr_opcode;
  TInsn orig_tinsn;
  TInsn orig_tinsn;
  bfd_boolean expanded = FALSE;
  bfd_boolean expanded = FALSE;
  bfd_boolean branch_jmp_to_next = FALSE;
  bfd_boolean branch_jmp_to_next = FALSE;
  char *fr_opcode = fragP->fr_opcode;
  char *fr_opcode = fragP->fr_opcode;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  bfd_boolean from_wide_insn = FALSE;
  bfd_boolean from_wide_insn = FALSE;
  int bytes;
  int bytes;
  bfd_boolean is_loop;
  bfd_boolean is_loop;
 
 
  gas_assert (fr_opcode != NULL);
  gas_assert (fr_opcode != NULL);
 
 
  xg_clear_vinsn (&cur_vinsn);
  xg_clear_vinsn (&cur_vinsn);
 
 
  vinsn_from_chars (&cur_vinsn, fr_opcode);
  vinsn_from_chars (&cur_vinsn, fr_opcode);
  if (cur_vinsn.num_slots > 1)
  if (cur_vinsn.num_slots > 1)
    from_wide_insn = TRUE;
    from_wide_insn = TRUE;
 
 
  orig_tinsn = cur_vinsn.slots[slot];
  orig_tinsn = cur_vinsn.slots[slot];
  tinsn_immed_from_frag (&orig_tinsn, fragP, slot);
  tinsn_immed_from_frag (&orig_tinsn, fragP, slot);
 
 
  is_loop = xtensa_opcode_is_loop (xtensa_default_isa, orig_tinsn.opcode) == 1;
  is_loop = xtensa_opcode_is_loop (xtensa_default_isa, orig_tinsn.opcode) == 1;
 
 
  if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
  if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
    branch_jmp_to_next = is_branch_jmp_to_next (&orig_tinsn, fragP);
    branch_jmp_to_next = is_branch_jmp_to_next (&orig_tinsn, fragP);
 
 
  if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
  if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
    {
    {
      /* Conversion just inserts a NOP and marks the fix as completed.  */
      /* Conversion just inserts a NOP and marks the fix as completed.  */
      bytes = xtensa_format_length (isa, fmt);
      bytes = xtensa_format_length (isa, fmt);
      if (bytes >= 4)
      if (bytes >= 4)
        {
        {
          cur_vinsn.slots[slot].opcode =
          cur_vinsn.slots[slot].opcode =
            xtensa_format_slot_nop_opcode (isa, cur_vinsn.format, slot);
            xtensa_format_slot_nop_opcode (isa, cur_vinsn.format, slot);
          cur_vinsn.slots[slot].ntok = 0;
          cur_vinsn.slots[slot].ntok = 0;
        }
        }
      else
      else
        {
        {
          bytes += fragP->tc_frag_data.text_expansion[0];
          bytes += fragP->tc_frag_data.text_expansion[0];
          gas_assert (bytes == 2 || bytes == 3);
          gas_assert (bytes == 2 || bytes == 3);
          build_nop (&cur_vinsn.slots[0], bytes);
          build_nop (&cur_vinsn.slots[0], bytes);
          fragP->fr_fix += fragP->tc_frag_data.text_expansion[0];
          fragP->fr_fix += fragP->tc_frag_data.text_expansion[0];
        }
        }
      vinsn_to_insnbuf (&cur_vinsn, fr_opcode, frag_now, TRUE);
      vinsn_to_insnbuf (&cur_vinsn, fr_opcode, frag_now, TRUE);
      xtensa_insnbuf_to_chars
      xtensa_insnbuf_to_chars
        (isa, cur_vinsn.insnbuf, (unsigned char *) fr_opcode, 0);
        (isa, cur_vinsn.insnbuf, (unsigned char *) fr_opcode, 0);
      fragP->fr_var = 0;
      fragP->fr_var = 0;
    }
    }
  else
  else
    {
    {
      /* Here is the fun stuff:  Get the immediate field from this
      /* Here is the fun stuff:  Get the immediate field from this
         instruction.  If it fits, we're done.  If not, find the next
         instruction.  If it fits, we're done.  If not, find the next
         instruction sequence that fits.  */
         instruction sequence that fits.  */
 
 
      IStack istack;
      IStack istack;
      int i;
      int i;
      symbolS *lit_sym = NULL;
      symbolS *lit_sym = NULL;
      int total_size = 0;
      int total_size = 0;
      int target_offset = 0;
      int target_offset = 0;
      int old_size;
      int old_size;
      int diff;
      int diff;
      symbolS *gen_label = NULL;
      symbolS *gen_label = NULL;
      offsetT frag_offset;
      offsetT frag_offset;
      bfd_boolean first = TRUE;
      bfd_boolean first = TRUE;
      bfd_boolean last_is_jump;
      bfd_boolean last_is_jump;
 
 
      /* It does not fit.  Find something that does and
      /* It does not fit.  Find something that does and
         convert immediately.  */
         convert immediately.  */
      frag_offset = fr_opcode - fragP->fr_literal;
      frag_offset = fr_opcode - fragP->fr_literal;
      istack_init (&istack);
      istack_init (&istack);
      xg_assembly_relax (&istack, &orig_tinsn,
      xg_assembly_relax (&istack, &orig_tinsn,
                         segP, fragP, frag_offset, min_steps, 0);
                         segP, fragP, frag_offset, min_steps, 0);
 
 
      old_size = xtensa_format_length (isa, fmt);
      old_size = xtensa_format_length (isa, fmt);
 
 
      /* Assemble this right inline.  */
      /* Assemble this right inline.  */
 
 
      /* First, create the mapping from a label name to the REAL label.  */
      /* First, create the mapping from a label name to the REAL label.  */
      target_offset = 0;
      target_offset = 0;
      for (i = 0; i < istack.ninsn; i++)
      for (i = 0; i < istack.ninsn; i++)
        {
        {
          TInsn *tinsn = &istack.insn[i];
          TInsn *tinsn = &istack.insn[i];
          fragS *lit_frag;
          fragS *lit_frag;
 
 
          switch (tinsn->insn_type)
          switch (tinsn->insn_type)
            {
            {
            case ITYPE_LITERAL:
            case ITYPE_LITERAL:
              if (lit_sym != NULL)
              if (lit_sym != NULL)
                as_bad (_("multiple literals in expansion"));
                as_bad (_("multiple literals in expansion"));
              /* First find the appropriate space in the literal pool.  */
              /* First find the appropriate space in the literal pool.  */
              lit_frag = fragP->tc_frag_data.literal_frags[slot];
              lit_frag = fragP->tc_frag_data.literal_frags[slot];
              if (lit_frag == NULL)
              if (lit_frag == NULL)
                as_bad (_("no registered fragment for literal"));
                as_bad (_("no registered fragment for literal"));
              if (tinsn->ntok != 1)
              if (tinsn->ntok != 1)
                as_bad (_("number of literal tokens != 1"));
                as_bad (_("number of literal tokens != 1"));
 
 
              /* Set the literal symbol and add a fixup.  */
              /* Set the literal symbol and add a fixup.  */
              lit_sym = lit_frag->fr_symbol;
              lit_sym = lit_frag->fr_symbol;
              break;
              break;
 
 
            case ITYPE_LABEL:
            case ITYPE_LABEL:
              if (align_targets && !is_loop)
              if (align_targets && !is_loop)
                {
                {
                  fragS *unreach = fragP->fr_next;
                  fragS *unreach = fragP->fr_next;
                  while (!(unreach->fr_type == rs_machine_dependent
                  while (!(unreach->fr_type == rs_machine_dependent
                           && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
                           && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
                               || unreach->fr_subtype == RELAX_UNREACHABLE)))
                               || unreach->fr_subtype == RELAX_UNREACHABLE)))
                    {
                    {
                      unreach = unreach->fr_next;
                      unreach = unreach->fr_next;
                    }
                    }
 
 
                  gas_assert (unreach->fr_type == rs_machine_dependent
                  gas_assert (unreach->fr_type == rs_machine_dependent
                          && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
                          && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
                              || unreach->fr_subtype == RELAX_UNREACHABLE));
                              || unreach->fr_subtype == RELAX_UNREACHABLE));
 
 
                  target_offset += unreach->tc_frag_data.text_expansion[0];
                  target_offset += unreach->tc_frag_data.text_expansion[0];
                }
                }
              gas_assert (gen_label == NULL);
              gas_assert (gen_label == NULL);
              gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
              gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
                                      fr_opcode - fragP->fr_literal
                                      fr_opcode - fragP->fr_literal
                                      + target_offset, fragP);
                                      + target_offset, fragP);
              break;
              break;
 
 
            case ITYPE_INSN:
            case ITYPE_INSN:
              if (first && from_wide_insn)
              if (first && from_wide_insn)
                {
                {
                  target_offset += xtensa_format_length (isa, fmt);
                  target_offset += xtensa_format_length (isa, fmt);
                  first = FALSE;
                  first = FALSE;
                  if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
                  if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
                    target_offset += xg_get_single_size (tinsn->opcode);
                    target_offset += xg_get_single_size (tinsn->opcode);
                }
                }
              else
              else
                target_offset += xg_get_single_size (tinsn->opcode);
                target_offset += xg_get_single_size (tinsn->opcode);
              break;
              break;
            }
            }
        }
        }
 
 
      total_size = 0;
      total_size = 0;
      first = TRUE;
      first = TRUE;
      last_is_jump = FALSE;
      last_is_jump = FALSE;
      for (i = 0; i < istack.ninsn; i++)
      for (i = 0; i < istack.ninsn; i++)
        {
        {
          TInsn *tinsn = &istack.insn[i];
          TInsn *tinsn = &istack.insn[i];
          fragS *lit_frag;
          fragS *lit_frag;
          int size;
          int size;
          segT target_seg;
          segT target_seg;
          bfd_reloc_code_real_type reloc_type;
          bfd_reloc_code_real_type reloc_type;
 
 
          switch (tinsn->insn_type)
          switch (tinsn->insn_type)
            {
            {
            case ITYPE_LITERAL:
            case ITYPE_LITERAL:
              lit_frag = fragP->tc_frag_data.literal_frags[slot];
              lit_frag = fragP->tc_frag_data.literal_frags[slot];
              /* Already checked.  */
              /* Already checked.  */
              gas_assert (lit_frag != NULL);
              gas_assert (lit_frag != NULL);
              gas_assert (lit_sym != NULL);
              gas_assert (lit_sym != NULL);
              gas_assert (tinsn->ntok == 1);
              gas_assert (tinsn->ntok == 1);
              /* Add a fixup.  */
              /* Add a fixup.  */
              target_seg = S_GET_SEGMENT (lit_sym);
              target_seg = S_GET_SEGMENT (lit_sym);
              gas_assert (target_seg);
              gas_assert (target_seg);
              reloc_type = map_operator_to_reloc (tinsn->tok[0].X_op, TRUE);
              reloc_type = map_operator_to_reloc (tinsn->tok[0].X_op, TRUE);
              fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
              fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
                                  &tinsn->tok[0], FALSE, reloc_type);
                                  &tinsn->tok[0], FALSE, reloc_type);
              break;
              break;
 
 
            case ITYPE_LABEL:
            case ITYPE_LABEL:
              break;
              break;
 
 
            case ITYPE_INSN:
            case ITYPE_INSN:
              xg_resolve_labels (tinsn, gen_label);
              xg_resolve_labels (tinsn, gen_label);
              xg_resolve_literals (tinsn, lit_sym);
              xg_resolve_literals (tinsn, lit_sym);
              if (from_wide_insn && first)
              if (from_wide_insn && first)
                {
                {
                  first = FALSE;
                  first = FALSE;
                  if (opcode_fits_format_slot (tinsn->opcode, fmt, slot))
                  if (opcode_fits_format_slot (tinsn->opcode, fmt, slot))
                    {
                    {
                      cur_vinsn.slots[slot] = *tinsn;
                      cur_vinsn.slots[slot] = *tinsn;
                    }
                    }
                  else
                  else
                    {
                    {
                      cur_vinsn.slots[slot].opcode =
                      cur_vinsn.slots[slot].opcode =
                        xtensa_format_slot_nop_opcode (isa, fmt, slot);
                        xtensa_format_slot_nop_opcode (isa, fmt, slot);
                      cur_vinsn.slots[slot].ntok = 0;
                      cur_vinsn.slots[slot].ntok = 0;
                    }
                    }
                  vinsn_to_insnbuf (&cur_vinsn, immed_instr, fragP, TRUE);
                  vinsn_to_insnbuf (&cur_vinsn, immed_instr, fragP, TRUE);
                  xtensa_insnbuf_to_chars (isa, cur_vinsn.insnbuf,
                  xtensa_insnbuf_to_chars (isa, cur_vinsn.insnbuf,
                                           (unsigned char *) immed_instr, 0);
                                           (unsigned char *) immed_instr, 0);
                  fragP->tc_frag_data.is_insn = TRUE;
                  fragP->tc_frag_data.is_insn = TRUE;
                  size = xtensa_format_length (isa, fmt);
                  size = xtensa_format_length (isa, fmt);
                  if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
                  if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
                    {
                    {
                      xg_emit_insn_to_buf
                      xg_emit_insn_to_buf
                        (tinsn, immed_instr + size, fragP,
                        (tinsn, immed_instr + size, fragP,
                         immed_instr - fragP->fr_literal + size, TRUE);
                         immed_instr - fragP->fr_literal + size, TRUE);
                      size += xg_get_single_size (tinsn->opcode);
                      size += xg_get_single_size (tinsn->opcode);
                    }
                    }
                }
                }
              else
              else
                {
                {
                  size = xg_get_single_size (tinsn->opcode);
                  size = xg_get_single_size (tinsn->opcode);
                  xg_emit_insn_to_buf (tinsn, immed_instr, fragP,
                  xg_emit_insn_to_buf (tinsn, immed_instr, fragP,
                                       immed_instr - fragP->fr_literal, TRUE);
                                       immed_instr - fragP->fr_literal, TRUE);
                }
                }
              immed_instr += size;
              immed_instr += size;
              total_size += size;
              total_size += size;
              break;
              break;
            }
            }
        }
        }
 
 
      diff = total_size - old_size;
      diff = total_size - old_size;
      gas_assert (diff >= 0);
      gas_assert (diff >= 0);
      if (diff != 0)
      if (diff != 0)
        expanded = TRUE;
        expanded = TRUE;
      gas_assert (diff <= fragP->fr_var);
      gas_assert (diff <= fragP->fr_var);
      fragP->fr_var -= diff;
      fragP->fr_var -= diff;
      fragP->fr_fix += diff;
      fragP->fr_fix += diff;
    }
    }
 
 
  /* Check for undefined immediates in LOOP instructions.  */
  /* Check for undefined immediates in LOOP instructions.  */
  if (is_loop)
  if (is_loop)
    {
    {
      symbolS *sym;
      symbolS *sym;
      sym = orig_tinsn.tok[1].X_add_symbol;
      sym = orig_tinsn.tok[1].X_add_symbol;
      if (sym != NULL && !S_IS_DEFINED (sym))
      if (sym != NULL && !S_IS_DEFINED (sym))
        {
        {
          as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
          as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
          return;
          return;
        }
        }
      sym = orig_tinsn.tok[1].X_op_symbol;
      sym = orig_tinsn.tok[1].X_op_symbol;
      if (sym != NULL && !S_IS_DEFINED (sym))
      if (sym != NULL && !S_IS_DEFINED (sym))
        {
        {
          as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
          as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
          return;
          return;
        }
        }
    }
    }
 
 
  if (expanded && xtensa_opcode_is_loop (isa, orig_tinsn.opcode) == 1)
  if (expanded && xtensa_opcode_is_loop (isa, orig_tinsn.opcode) == 1)
    convert_frag_immed_finish_loop (segP, fragP, &orig_tinsn);
    convert_frag_immed_finish_loop (segP, fragP, &orig_tinsn);
 
 
  if (expanded && is_direct_call_opcode (orig_tinsn.opcode))
  if (expanded && is_direct_call_opcode (orig_tinsn.opcode))
    {
    {
      /* Add an expansion note on the expanded instruction.  */
      /* Add an expansion note on the expanded instruction.  */
      fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4,
      fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4,
                          &orig_tinsn.tok[0], TRUE,
                          &orig_tinsn.tok[0], TRUE,
                          BFD_RELOC_XTENSA_ASM_EXPAND);
                          BFD_RELOC_XTENSA_ASM_EXPAND);
    }
    }
}
}
 
 
 
 
/* Add a new fix expression into the desired segment.  We have to
/* Add a new fix expression into the desired segment.  We have to
   switch to that segment to do this.  */
   switch to that segment to do this.  */
 
 
static fixS *
static fixS *
fix_new_exp_in_seg (segT new_seg,
fix_new_exp_in_seg (segT new_seg,
                    subsegT new_subseg,
                    subsegT new_subseg,
                    fragS *frag,
                    fragS *frag,
                    int where,
                    int where,
                    int size,
                    int size,
                    expressionS *exp,
                    expressionS *exp,
                    int pcrel,
                    int pcrel,
                    bfd_reloc_code_real_type r_type)
                    bfd_reloc_code_real_type r_type)
{
{
  fixS *new_fix;
  fixS *new_fix;
  segT seg = now_seg;
  segT seg = now_seg;
  subsegT subseg = now_subseg;
  subsegT subseg = now_subseg;
 
 
  gas_assert (new_seg != 0);
  gas_assert (new_seg != 0);
  subseg_set (new_seg, new_subseg);
  subseg_set (new_seg, new_subseg);
 
 
  new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
  new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
  subseg_set (seg, subseg);
  subseg_set (seg, subseg);
  return new_fix;
  return new_fix;
}
}
 
 
 
 
/* Relax a loop instruction so that it can span loop >256 bytes.
/* Relax a loop instruction so that it can span loop >256 bytes.
 
 
                  loop    as, .L1
                  loop    as, .L1
          .L0:
          .L0:
                  rsr     as, LEND
                  rsr     as, LEND
                  wsr     as, LBEG
                  wsr     as, LBEG
                  addi    as, as, lo8 (label-.L1)
                  addi    as, as, lo8 (label-.L1)
                  addmi   as, as, mid8 (label-.L1)
                  addmi   as, as, mid8 (label-.L1)
                  wsr     as, LEND
                  wsr     as, LEND
                  isync
                  isync
                  rsr     as, LCOUNT
                  rsr     as, LCOUNT
                  addi    as, as, 1
                  addi    as, as, 1
          .L1:
          .L1:
                  <<body>>
                  <<body>>
          label:
          label:
*/
*/
 
 
static void
static void
convert_frag_immed_finish_loop (segT segP, fragS *fragP, TInsn *tinsn)
convert_frag_immed_finish_loop (segT segP, fragS *fragP, TInsn *tinsn)
{
{
  TInsn loop_insn;
  TInsn loop_insn;
  TInsn addi_insn;
  TInsn addi_insn;
  TInsn addmi_insn;
  TInsn addmi_insn;
  unsigned long target;
  unsigned long target;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  unsigned int loop_length, loop_length_hi, loop_length_lo;
  unsigned int loop_length, loop_length_hi, loop_length_lo;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  addressT loop_offset;
  addressT loop_offset;
  addressT addi_offset = 9;
  addressT addi_offset = 9;
  addressT addmi_offset = 12;
  addressT addmi_offset = 12;
  fragS *next_fragP;
  fragS *next_fragP;
  int target_count;
  int target_count;
 
 
  if (!insnbuf)
  if (!insnbuf)
    insnbuf = xtensa_insnbuf_alloc (isa);
    insnbuf = xtensa_insnbuf_alloc (isa);
 
 
  /* Get the loop offset.  */
  /* Get the loop offset.  */
  loop_offset = get_expanded_loop_offset (tinsn->opcode);
  loop_offset = get_expanded_loop_offset (tinsn->opcode);
 
 
  /* Validate that there really is a LOOP at the loop_offset.  Because
  /* Validate that there really is a LOOP at the loop_offset.  Because
     loops are not bundleable, we can assume that the instruction will be
     loops are not bundleable, we can assume that the instruction will be
     in slot 0.  */
     in slot 0.  */
  tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
  tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
  tinsn_immed_from_frag (&loop_insn, fragP, 0);
  tinsn_immed_from_frag (&loop_insn, fragP, 0);
 
 
  gas_assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
  gas_assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
  addi_offset += loop_offset;
  addi_offset += loop_offset;
  addmi_offset += loop_offset;
  addmi_offset += loop_offset;
 
 
  gas_assert (tinsn->ntok == 2);
  gas_assert (tinsn->ntok == 2);
  if (tinsn->tok[1].X_op == O_constant)
  if (tinsn->tok[1].X_op == O_constant)
    target = tinsn->tok[1].X_add_number;
    target = tinsn->tok[1].X_add_number;
  else if (tinsn->tok[1].X_op == O_symbol)
  else if (tinsn->tok[1].X_op == O_symbol)
    {
    {
      /* Find the fragment.  */
      /* Find the fragment.  */
      symbolS *sym = tinsn->tok[1].X_add_symbol;
      symbolS *sym = tinsn->tok[1].X_add_symbol;
      gas_assert (S_GET_SEGMENT (sym) == segP
      gas_assert (S_GET_SEGMENT (sym) == segP
              || S_GET_SEGMENT (sym) == absolute_section);
              || S_GET_SEGMENT (sym) == absolute_section);
      target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
      target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
    }
    }
  else
  else
    {
    {
      as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
      as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
      target = 0;
      target = 0;
    }
    }
 
 
  loop_length = target - (fragP->fr_address + fragP->fr_fix);
  loop_length = target - (fragP->fr_address + fragP->fr_fix);
  loop_length_hi = loop_length & ~0x0ff;
  loop_length_hi = loop_length & ~0x0ff;
  loop_length_lo = loop_length & 0x0ff;
  loop_length_lo = loop_length & 0x0ff;
  if (loop_length_lo >= 128)
  if (loop_length_lo >= 128)
    {
    {
      loop_length_lo -= 256;
      loop_length_lo -= 256;
      loop_length_hi += 256;
      loop_length_hi += 256;
    }
    }
 
 
  /* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most
  /* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most
     32512.  If the loop is larger than that, then we just fail.  */
     32512.  If the loop is larger than that, then we just fail.  */
  if (loop_length_hi > 32512)
  if (loop_length_hi > 32512)
    as_bad_where (fragP->fr_file, fragP->fr_line,
    as_bad_where (fragP->fr_file, fragP->fr_line,
                  _("loop too long for LOOP instruction"));
                  _("loop too long for LOOP instruction"));
 
 
  tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset, 0);
  tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset, 0);
  gas_assert (addi_insn.opcode == xtensa_addi_opcode);
  gas_assert (addi_insn.opcode == xtensa_addi_opcode);
 
 
  tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
  tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
  gas_assert (addmi_insn.opcode == xtensa_addmi_opcode);
  gas_assert (addmi_insn.opcode == xtensa_addmi_opcode);
 
 
  set_expr_const (&addi_insn.tok[2], loop_length_lo);
  set_expr_const (&addi_insn.tok[2], loop_length_lo);
  tinsn_to_insnbuf (&addi_insn, insnbuf);
  tinsn_to_insnbuf (&addi_insn, insnbuf);
 
 
  fragP->tc_frag_data.is_insn = TRUE;
  fragP->tc_frag_data.is_insn = TRUE;
  xtensa_insnbuf_to_chars
  xtensa_insnbuf_to_chars
    (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addi_offset, 0);
    (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addi_offset, 0);
 
 
  set_expr_const (&addmi_insn.tok[2], loop_length_hi);
  set_expr_const (&addmi_insn.tok[2], loop_length_hi);
  tinsn_to_insnbuf (&addmi_insn, insnbuf);
  tinsn_to_insnbuf (&addmi_insn, insnbuf);
  xtensa_insnbuf_to_chars
  xtensa_insnbuf_to_chars
    (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addmi_offset, 0);
    (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addmi_offset, 0);
 
 
  /* Walk through all of the frags from here to the loop end
  /* Walk through all of the frags from here to the loop end
     and mark them as no_transform to keep them from being modified
     and mark them as no_transform to keep them from being modified
     by the linker.  If we ever have a relocation for the
     by the linker.  If we ever have a relocation for the
     addi/addmi of the difference of two symbols we can remove this.  */
     addi/addmi of the difference of two symbols we can remove this.  */
 
 
  target_count = 0;
  target_count = 0;
  for (next_fragP = fragP; next_fragP != NULL;
  for (next_fragP = fragP; next_fragP != NULL;
       next_fragP = next_fragP->fr_next)
       next_fragP = next_fragP->fr_next)
    {
    {
      next_fragP->tc_frag_data.is_no_transform = TRUE;
      next_fragP->tc_frag_data.is_no_transform = TRUE;
      if (next_fragP->tc_frag_data.is_loop_target)
      if (next_fragP->tc_frag_data.is_loop_target)
        target_count++;
        target_count++;
      if (target_count == 2)
      if (target_count == 2)
        break;
        break;
    }
    }
}
}
 
 


/* A map that keeps information on a per-subsegment basis.  This is
/* A map that keeps information on a per-subsegment basis.  This is
   maintained during initial assembly, but is invalid once the
   maintained during initial assembly, but is invalid once the
   subsegments are smashed together.  I.E., it cannot be used during
   subsegments are smashed together.  I.E., it cannot be used during
   the relaxation.  */
   the relaxation.  */
 
 
typedef struct subseg_map_struct
typedef struct subseg_map_struct
{
{
  /* the key */
  /* the key */
  segT seg;
  segT seg;
  subsegT subseg;
  subsegT subseg;
 
 
  /* the data */
  /* the data */
  unsigned flags;
  unsigned flags;
  float total_freq;     /* fall-through + branch target frequency */
  float total_freq;     /* fall-through + branch target frequency */
  float target_freq;    /* branch target frequency alone */
  float target_freq;    /* branch target frequency alone */
 
 
  struct subseg_map_struct *next;
  struct subseg_map_struct *next;
} subseg_map;
} subseg_map;
 
 
 
 
static subseg_map *sseg_map = NULL;
static subseg_map *sseg_map = NULL;
 
 
static subseg_map *
static subseg_map *
get_subseg_info (segT seg, subsegT subseg)
get_subseg_info (segT seg, subsegT subseg)
{
{
  subseg_map *subseg_e;
  subseg_map *subseg_e;
 
 
  for (subseg_e = sseg_map; subseg_e; subseg_e = subseg_e->next)
  for (subseg_e = sseg_map; subseg_e; subseg_e = subseg_e->next)
    {
    {
      if (seg == subseg_e->seg && subseg == subseg_e->subseg)
      if (seg == subseg_e->seg && subseg == subseg_e->subseg)
        break;
        break;
    }
    }
  return subseg_e;
  return subseg_e;
}
}
 
 
 
 
static subseg_map *
static subseg_map *
add_subseg_info (segT seg, subsegT subseg)
add_subseg_info (segT seg, subsegT subseg)
{
{
  subseg_map *subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map));
  subseg_map *subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map));
  memset (subseg_e, 0, sizeof (subseg_map));
  memset (subseg_e, 0, sizeof (subseg_map));
  subseg_e->seg = seg;
  subseg_e->seg = seg;
  subseg_e->subseg = subseg;
  subseg_e->subseg = subseg;
  subseg_e->flags = 0;
  subseg_e->flags = 0;
  /* Start off considering every branch target very important.  */
  /* Start off considering every branch target very important.  */
  subseg_e->target_freq = 1.0;
  subseg_e->target_freq = 1.0;
  subseg_e->total_freq = 1.0;
  subseg_e->total_freq = 1.0;
  subseg_e->next = sseg_map;
  subseg_e->next = sseg_map;
  sseg_map = subseg_e;
  sseg_map = subseg_e;
  return subseg_e;
  return subseg_e;
}
}
 
 
 
 
static unsigned
static unsigned
get_last_insn_flags (segT seg, subsegT subseg)
get_last_insn_flags (segT seg, subsegT subseg)
{
{
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  if (subseg_e)
  if (subseg_e)
    return subseg_e->flags;
    return subseg_e->flags;
  return 0;
  return 0;
}
}
 
 
 
 
static void
static void
set_last_insn_flags (segT seg,
set_last_insn_flags (segT seg,
                     subsegT subseg,
                     subsegT subseg,
                     unsigned fl,
                     unsigned fl,
                     bfd_boolean val)
                     bfd_boolean val)
{
{
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  if (! subseg_e)
  if (! subseg_e)
    subseg_e = add_subseg_info (seg, subseg);
    subseg_e = add_subseg_info (seg, subseg);
  if (val)
  if (val)
    subseg_e->flags |= fl;
    subseg_e->flags |= fl;
  else
  else
    subseg_e->flags &= ~fl;
    subseg_e->flags &= ~fl;
}
}
 
 
 
 
static float
static float
get_subseg_total_freq (segT seg, subsegT subseg)
get_subseg_total_freq (segT seg, subsegT subseg)
{
{
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  if (subseg_e)
  if (subseg_e)
    return subseg_e->total_freq;
    return subseg_e->total_freq;
  return 1.0;
  return 1.0;
}
}
 
 
 
 
static float
static float
get_subseg_target_freq (segT seg, subsegT subseg)
get_subseg_target_freq (segT seg, subsegT subseg)
{
{
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  if (subseg_e)
  if (subseg_e)
    return subseg_e->target_freq;
    return subseg_e->target_freq;
  return 1.0;
  return 1.0;
}
}
 
 
 
 
static void
static void
set_subseg_freq (segT seg, subsegT subseg, float total_f, float target_f)
set_subseg_freq (segT seg, subsegT subseg, float total_f, float target_f)
{
{
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  subseg_map *subseg_e = get_subseg_info (seg, subseg);
  if (! subseg_e)
  if (! subseg_e)
    subseg_e = add_subseg_info (seg, subseg);
    subseg_e = add_subseg_info (seg, subseg);
  subseg_e->total_freq = total_f;
  subseg_e->total_freq = total_f;
  subseg_e->target_freq = target_f;
  subseg_e->target_freq = target_f;
}
}
 
 


/* Segment Lists and emit_state Stuff.  */
/* Segment Lists and emit_state Stuff.  */
 
 
static void
static void
xtensa_move_seg_list_to_beginning (seg_list *head)
xtensa_move_seg_list_to_beginning (seg_list *head)
{
{
  head = head->next;
  head = head->next;
  while (head)
  while (head)
    {
    {
      segT literal_section = head->seg;
      segT literal_section = head->seg;
 
 
      /* Move the literal section to the front of the section list.  */
      /* Move the literal section to the front of the section list.  */
      gas_assert (literal_section);
      gas_assert (literal_section);
      if (literal_section != stdoutput->sections)
      if (literal_section != stdoutput->sections)
        {
        {
          bfd_section_list_remove (stdoutput, literal_section);
          bfd_section_list_remove (stdoutput, literal_section);
          bfd_section_list_prepend (stdoutput, literal_section);
          bfd_section_list_prepend (stdoutput, literal_section);
        }
        }
      head = head->next;
      head = head->next;
    }
    }
}
}
 
 
 
 
static void mark_literal_frags (seg_list *);
static void mark_literal_frags (seg_list *);
 
 
static void
static void
xtensa_move_literals (void)
xtensa_move_literals (void)
{
{
  seg_list *segment;
  seg_list *segment;
  frchainS *frchain_from, *frchain_to;
  frchainS *frchain_from, *frchain_to;
  fragS *search_frag, *next_frag, *last_frag, *literal_pool, *insert_after;
  fragS *search_frag, *next_frag, *last_frag, *literal_pool, *insert_after;
  fragS **frag_splice;
  fragS **frag_splice;
  emit_state state;
  emit_state state;
  segT dest_seg;
  segT dest_seg;
  fixS *fix, *next_fix, **fix_splice;
  fixS *fix, *next_fix, **fix_splice;
  sym_list *lit;
  sym_list *lit;
 
 
  mark_literal_frags (literal_head->next);
  mark_literal_frags (literal_head->next);
 
 
  if (use_literal_section)
  if (use_literal_section)
    return;
    return;
 
 
  for (segment = literal_head->next; segment; segment = segment->next)
  for (segment = literal_head->next; segment; segment = segment->next)
    {
    {
      /* Keep the literals for .init and .fini in separate sections.  */
      /* Keep the literals for .init and .fini in separate sections.  */
      if (!strcmp (segment_name (segment->seg), INIT_SECTION_NAME)
      if (!strcmp (segment_name (segment->seg), INIT_SECTION_NAME)
          || !strcmp (segment_name (segment->seg), FINI_SECTION_NAME))
          || !strcmp (segment_name (segment->seg), FINI_SECTION_NAME))
        continue;
        continue;
 
 
      frchain_from = seg_info (segment->seg)->frchainP;
      frchain_from = seg_info (segment->seg)->frchainP;
      search_frag = frchain_from->frch_root;
      search_frag = frchain_from->frch_root;
      literal_pool = NULL;
      literal_pool = NULL;
      frchain_to = NULL;
      frchain_to = NULL;
      frag_splice = &(frchain_from->frch_root);
      frag_splice = &(frchain_from->frch_root);
 
 
      while (!search_frag->tc_frag_data.literal_frag)
      while (!search_frag->tc_frag_data.literal_frag)
        {
        {
          gas_assert (search_frag->fr_fix == 0
          gas_assert (search_frag->fr_fix == 0
                  || search_frag->fr_type == rs_align);
                  || search_frag->fr_type == rs_align);
          search_frag = search_frag->fr_next;
          search_frag = search_frag->fr_next;
        }
        }
 
 
      gas_assert (search_frag->tc_frag_data.literal_frag->fr_subtype
      gas_assert (search_frag->tc_frag_data.literal_frag->fr_subtype
              == RELAX_LITERAL_POOL_BEGIN);
              == RELAX_LITERAL_POOL_BEGIN);
      xtensa_switch_section_emit_state (&state, segment->seg, 0);
      xtensa_switch_section_emit_state (&state, segment->seg, 0);
 
 
      /* Make sure that all the frags in this series are closed, and
      /* Make sure that all the frags in this series are closed, and
         that there is at least one left over of zero-size.  This
         that there is at least one left over of zero-size.  This
         prevents us from making a segment with an frchain without any
         prevents us from making a segment with an frchain without any
         frags in it.  */
         frags in it.  */
      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      last_frag = frag_now;
      last_frag = frag_now;
      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
 
 
      while (search_frag != frag_now)
      while (search_frag != frag_now)
        {
        {
          next_frag = search_frag->fr_next;
          next_frag = search_frag->fr_next;
 
 
          /* First, move the frag out of the literal section and
          /* First, move the frag out of the literal section and
             to the appropriate place.  */
             to the appropriate place.  */
          if (search_frag->tc_frag_data.literal_frag)
          if (search_frag->tc_frag_data.literal_frag)
            {
            {
              literal_pool = search_frag->tc_frag_data.literal_frag;
              literal_pool = search_frag->tc_frag_data.literal_frag;
              gas_assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
              gas_assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
              frchain_to = literal_pool->tc_frag_data.lit_frchain;
              frchain_to = literal_pool->tc_frag_data.lit_frchain;
              gas_assert (frchain_to);
              gas_assert (frchain_to);
            }
            }
          insert_after = literal_pool->tc_frag_data.literal_frag;
          insert_after = literal_pool->tc_frag_data.literal_frag;
          dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
          dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
 
 
          *frag_splice = next_frag;
          *frag_splice = next_frag;
          search_frag->fr_next = insert_after->fr_next;
          search_frag->fr_next = insert_after->fr_next;
          insert_after->fr_next = search_frag;
          insert_after->fr_next = search_frag;
          search_frag->tc_frag_data.lit_seg = dest_seg;
          search_frag->tc_frag_data.lit_seg = dest_seg;
          literal_pool->tc_frag_data.literal_frag = search_frag;
          literal_pool->tc_frag_data.literal_frag = search_frag;
 
 
          /* Now move any fixups associated with this frag to the
          /* Now move any fixups associated with this frag to the
             right section.  */
             right section.  */
          fix = frchain_from->fix_root;
          fix = frchain_from->fix_root;
          fix_splice = &(frchain_from->fix_root);
          fix_splice = &(frchain_from->fix_root);
          while (fix)
          while (fix)
            {
            {
              next_fix = fix->fx_next;
              next_fix = fix->fx_next;
              if (fix->fx_frag == search_frag)
              if (fix->fx_frag == search_frag)
                {
                {
                  *fix_splice = next_fix;
                  *fix_splice = next_fix;
                  fix->fx_next = frchain_to->fix_root;
                  fix->fx_next = frchain_to->fix_root;
                  frchain_to->fix_root = fix;
                  frchain_to->fix_root = fix;
                  if (frchain_to->fix_tail == NULL)
                  if (frchain_to->fix_tail == NULL)
                    frchain_to->fix_tail = fix;
                    frchain_to->fix_tail = fix;
                }
                }
              else
              else
                fix_splice = &(fix->fx_next);
                fix_splice = &(fix->fx_next);
              fix = next_fix;
              fix = next_fix;
            }
            }
          search_frag = next_frag;
          search_frag = next_frag;
        }
        }
 
 
      if (frchain_from->fix_root != NULL)
      if (frchain_from->fix_root != NULL)
        {
        {
          frchain_from = seg_info (segment->seg)->frchainP;
          frchain_from = seg_info (segment->seg)->frchainP;
          as_warn (_("fixes not all moved from %s"), segment->seg->name);
          as_warn (_("fixes not all moved from %s"), segment->seg->name);
 
 
          gas_assert (frchain_from->fix_root == NULL);
          gas_assert (frchain_from->fix_root == NULL);
        }
        }
      frchain_from->fix_tail = NULL;
      frchain_from->fix_tail = NULL;
      xtensa_restore_emit_state (&state);
      xtensa_restore_emit_state (&state);
    }
    }
 
 
  /* Now fix up the SEGMENT value for all the literal symbols.  */
  /* Now fix up the SEGMENT value for all the literal symbols.  */
  for (lit = literal_syms; lit; lit = lit->next)
  for (lit = literal_syms; lit; lit = lit->next)
    {
    {
      symbolS *lit_sym = lit->sym;
      symbolS *lit_sym = lit->sym;
      segT dest_seg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
      segT dest_seg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
      if (dest_seg)
      if (dest_seg)
        S_SET_SEGMENT (lit_sym, dest_seg);
        S_SET_SEGMENT (lit_sym, dest_seg);
    }
    }
}
}
 
 
 
 
/* Walk over all the frags for segments in a list and mark them as
/* Walk over all the frags for segments in a list and mark them as
   containing literals.  As clunky as this is, we can't rely on frag_var
   containing literals.  As clunky as this is, we can't rely on frag_var
   and frag_variant to get called in all situations.  */
   and frag_variant to get called in all situations.  */
 
 
static void
static void
mark_literal_frags (seg_list *segment)
mark_literal_frags (seg_list *segment)
{
{
  frchainS *frchain_from;
  frchainS *frchain_from;
  fragS *search_frag;
  fragS *search_frag;
 
 
  while (segment)
  while (segment)
    {
    {
      frchain_from = seg_info (segment->seg)->frchainP;
      frchain_from = seg_info (segment->seg)->frchainP;
      search_frag = frchain_from->frch_root;
      search_frag = frchain_from->frch_root;
      while (search_frag)
      while (search_frag)
        {
        {
          search_frag->tc_frag_data.is_literal = TRUE;
          search_frag->tc_frag_data.is_literal = TRUE;
          search_frag = search_frag->fr_next;
          search_frag = search_frag->fr_next;
        }
        }
      segment = segment->next;
      segment = segment->next;
    }
    }
}
}
 
 
 
 
static void
static void
xtensa_reorder_seg_list (seg_list *head, segT after)
xtensa_reorder_seg_list (seg_list *head, segT after)
{
{
  /* Move all of the sections in the section list to come
  /* Move all of the sections in the section list to come
     after "after" in the gnu segment list.  */
     after "after" in the gnu segment list.  */
 
 
  head = head->next;
  head = head->next;
  while (head)
  while (head)
    {
    {
      segT literal_section = head->seg;
      segT literal_section = head->seg;
 
 
      /* Move the literal section after "after".  */
      /* Move the literal section after "after".  */
      gas_assert (literal_section);
      gas_assert (literal_section);
      if (literal_section != after)
      if (literal_section != after)
        {
        {
          bfd_section_list_remove (stdoutput, literal_section);
          bfd_section_list_remove (stdoutput, literal_section);
          bfd_section_list_insert_after (stdoutput, after, literal_section);
          bfd_section_list_insert_after (stdoutput, after, literal_section);
        }
        }
 
 
      head = head->next;
      head = head->next;
    }
    }
}
}
 
 
 
 
/* Push all the literal segments to the end of the gnu list.  */
/* Push all the literal segments to the end of the gnu list.  */
 
 
static void
static void
xtensa_reorder_segments (void)
xtensa_reorder_segments (void)
{
{
  segT sec;
  segT sec;
  segT last_sec = 0;
  segT last_sec = 0;
  int old_count = 0;
  int old_count = 0;
  int new_count = 0;
  int new_count = 0;
 
 
  for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
  for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
    {
    {
      last_sec = sec;
      last_sec = sec;
      old_count++;
      old_count++;
    }
    }
 
 
  /* Now that we have the last section, push all the literal
  /* Now that we have the last section, push all the literal
     sections to the end.  */
     sections to the end.  */
  xtensa_reorder_seg_list (literal_head, last_sec);
  xtensa_reorder_seg_list (literal_head, last_sec);
 
 
  /* Now perform the final error check.  */
  /* Now perform the final error check.  */
  for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
  for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
    new_count++;
    new_count++;
  gas_assert (new_count == old_count);
  gas_assert (new_count == old_count);
}
}
 
 
 
 
/* Change the emit state (seg, subseg, and frag related stuff) to the
/* Change the emit state (seg, subseg, and frag related stuff) to the
   correct location.  Return a emit_state which can be passed to
   correct location.  Return a emit_state which can be passed to
   xtensa_restore_emit_state to return to current fragment.  */
   xtensa_restore_emit_state to return to current fragment.  */
 
 
static void
static void
xtensa_switch_to_literal_fragment (emit_state *result)
xtensa_switch_to_literal_fragment (emit_state *result)
{
{
  if (directive_state[directive_absolute_literals])
  if (directive_state[directive_absolute_literals])
    {
    {
      segT lit4_seg = cache_literal_section (TRUE);
      segT lit4_seg = cache_literal_section (TRUE);
      xtensa_switch_section_emit_state (result, lit4_seg, 0);
      xtensa_switch_section_emit_state (result, lit4_seg, 0);
    }
    }
  else
  else
    xtensa_switch_to_non_abs_literal_fragment (result);
    xtensa_switch_to_non_abs_literal_fragment (result);
 
 
  /* Do a 4-byte align here.  */
  /* Do a 4-byte align here.  */
  frag_align (2, 0, 0);
  frag_align (2, 0, 0);
  record_alignment (now_seg, 2);
  record_alignment (now_seg, 2);
}
}
 
 
 
 
static void
static void
xtensa_switch_to_non_abs_literal_fragment (emit_state *result)
xtensa_switch_to_non_abs_literal_fragment (emit_state *result)
{
{
  static bfd_boolean recursive = FALSE;
  static bfd_boolean recursive = FALSE;
  fragS *pool_location = get_literal_pool_location (now_seg);
  fragS *pool_location = get_literal_pool_location (now_seg);
  segT lit_seg;
  segT lit_seg;
  bfd_boolean is_init =
  bfd_boolean is_init =
    (now_seg && !strcmp (segment_name (now_seg), INIT_SECTION_NAME));
    (now_seg && !strcmp (segment_name (now_seg), INIT_SECTION_NAME));
  bfd_boolean is_fini =
  bfd_boolean is_fini =
    (now_seg && !strcmp (segment_name (now_seg), FINI_SECTION_NAME));
    (now_seg && !strcmp (segment_name (now_seg), FINI_SECTION_NAME));
 
 
  if (pool_location == NULL
  if (pool_location == NULL
      && !use_literal_section
      && !use_literal_section
      && !recursive
      && !recursive
      && !is_init && ! is_fini)
      && !is_init && ! is_fini)
    {
    {
      as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
      as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
 
 
      /* When we mark a literal pool location, we want to put a frag in
      /* When we mark a literal pool location, we want to put a frag in
         the literal pool that points to it.  But to do that, we want to
         the literal pool that points to it.  But to do that, we want to
         switch_to_literal_fragment.  But literal sections don't have
         switch_to_literal_fragment.  But literal sections don't have
         literal pools, so their location is always null, so we would
         literal pools, so their location is always null, so we would
         recurse forever.  This is kind of hacky, but it works.  */
         recurse forever.  This is kind of hacky, but it works.  */
 
 
      recursive = TRUE;
      recursive = TRUE;
      xtensa_mark_literal_pool_location ();
      xtensa_mark_literal_pool_location ();
      recursive = FALSE;
      recursive = FALSE;
    }
    }
 
 
  lit_seg = cache_literal_section (FALSE);
  lit_seg = cache_literal_section (FALSE);
  xtensa_switch_section_emit_state (result, lit_seg, 0);
  xtensa_switch_section_emit_state (result, lit_seg, 0);
 
 
  if (!use_literal_section
  if (!use_literal_section
      && !is_init && !is_fini
      && !is_init && !is_fini
      && get_literal_pool_location (now_seg) != pool_location)
      && get_literal_pool_location (now_seg) != pool_location)
    {
    {
      /* Close whatever frag is there.  */
      /* Close whatever frag is there.  */
      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
      frag_now->tc_frag_data.literal_frag = pool_location;
      frag_now->tc_frag_data.literal_frag = pool_location;
      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
      xtensa_set_frag_assembly_state (frag_now);
      xtensa_set_frag_assembly_state (frag_now);
    }
    }
}
}
 
 
 
 
/* Call this function before emitting data into the literal section.
/* Call this function before emitting data into the literal section.
   This is a helper function for xtensa_switch_to_literal_fragment.
   This is a helper function for xtensa_switch_to_literal_fragment.
   This is similar to a .section new_now_seg subseg. */
   This is similar to a .section new_now_seg subseg. */
 
 
static void
static void
xtensa_switch_section_emit_state (emit_state *state,
xtensa_switch_section_emit_state (emit_state *state,
                                  segT new_now_seg,
                                  segT new_now_seg,
                                  subsegT new_now_subseg)
                                  subsegT new_now_subseg)
{
{
  state->name = now_seg->name;
  state->name = now_seg->name;
  state->now_seg = now_seg;
  state->now_seg = now_seg;
  state->now_subseg = now_subseg;
  state->now_subseg = now_subseg;
  state->generating_literals = generating_literals;
  state->generating_literals = generating_literals;
  generating_literals++;
  generating_literals++;
  subseg_set (new_now_seg, new_now_subseg);
  subseg_set (new_now_seg, new_now_subseg);
}
}
 
 
 
 
/* Use to restore the emitting into the normal place.  */
/* Use to restore the emitting into the normal place.  */
 
 
static void
static void
xtensa_restore_emit_state (emit_state *state)
xtensa_restore_emit_state (emit_state *state)
{
{
  generating_literals = state->generating_literals;
  generating_literals = state->generating_literals;
  subseg_set (state->now_seg, state->now_subseg);
  subseg_set (state->now_seg, state->now_subseg);
}
}
 
 
 
 
/* Predicate function used to look up a section in a particular group.  */
/* Predicate function used to look up a section in a particular group.  */
 
 
static bfd_boolean
static bfd_boolean
match_section_group (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
match_section_group (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
{
{
  const char *gname = inf;
  const char *gname = inf;
  const char *group_name = elf_group_name (sec);
  const char *group_name = elf_group_name (sec);
 
 
  return (group_name == gname
  return (group_name == gname
          || (group_name != NULL
          || (group_name != NULL
              && gname != NULL
              && gname != NULL
              && strcmp (group_name, gname) == 0));
              && strcmp (group_name, gname) == 0));
}
}
 
 
 
 
/* Get the literal section to be used for the current text section.
/* Get the literal section to be used for the current text section.
   The result may be cached in the default_lit_sections structure.  */
   The result may be cached in the default_lit_sections structure.  */
 
 
static segT
static segT
cache_literal_section (bfd_boolean use_abs_literals)
cache_literal_section (bfd_boolean use_abs_literals)
{
{
  const char *text_name, *group_name = 0;
  const char *text_name, *group_name = 0;
  char *base_name, *name, *suffix;
  char *base_name, *name, *suffix;
  segT *pcached;
  segT *pcached;
  segT seg, current_section;
  segT seg, current_section;
  int current_subsec;
  int current_subsec;
  bfd_boolean linkonce = FALSE;
  bfd_boolean linkonce = FALSE;
 
 
  /* Save the current section/subsection.  */
  /* Save the current section/subsection.  */
  current_section = now_seg;
  current_section = now_seg;
  current_subsec = now_subseg;
  current_subsec = now_subseg;
 
 
  /* Clear the cached values if they are no longer valid.  */
  /* Clear the cached values if they are no longer valid.  */
  if (now_seg != default_lit_sections.current_text_seg)
  if (now_seg != default_lit_sections.current_text_seg)
    {
    {
      default_lit_sections.current_text_seg = now_seg;
      default_lit_sections.current_text_seg = now_seg;
      default_lit_sections.lit_seg = NULL;
      default_lit_sections.lit_seg = NULL;
      default_lit_sections.lit4_seg = NULL;
      default_lit_sections.lit4_seg = NULL;
    }
    }
 
 
  /* Check if the literal section is already cached.  */
  /* Check if the literal section is already cached.  */
  if (use_abs_literals)
  if (use_abs_literals)
    pcached = &default_lit_sections.lit4_seg;
    pcached = &default_lit_sections.lit4_seg;
  else
  else
    pcached = &default_lit_sections.lit_seg;
    pcached = &default_lit_sections.lit_seg;
 
 
  if (*pcached)
  if (*pcached)
    return *pcached;
    return *pcached;
 
 
  text_name = default_lit_sections.lit_prefix;
  text_name = default_lit_sections.lit_prefix;
  if (! text_name || ! *text_name)
  if (! text_name || ! *text_name)
    {
    {
      text_name = segment_name (current_section);
      text_name = segment_name (current_section);
      group_name = elf_group_name (current_section);
      group_name = elf_group_name (current_section);
      linkonce = (current_section->flags & SEC_LINK_ONCE) != 0;
      linkonce = (current_section->flags & SEC_LINK_ONCE) != 0;
    }
    }
 
 
  base_name = use_abs_literals ? ".lit4" : ".literal";
  base_name = use_abs_literals ? ".lit4" : ".literal";
  if (group_name)
  if (group_name)
    {
    {
      name = xmalloc (strlen (base_name) + strlen (group_name) + 2);
      name = xmalloc (strlen (base_name) + strlen (group_name) + 2);
      sprintf (name, "%s.%s", base_name, group_name);
      sprintf (name, "%s.%s", base_name, group_name);
    }
    }
  else if (strncmp (text_name, ".gnu.linkonce.", linkonce_len) == 0)
  else if (strncmp (text_name, ".gnu.linkonce.", linkonce_len) == 0)
    {
    {
      suffix = strchr (text_name + linkonce_len, '.');
      suffix = strchr (text_name + linkonce_len, '.');
 
 
      name = xmalloc (linkonce_len + strlen (base_name) + 1
      name = xmalloc (linkonce_len + strlen (base_name) + 1
                      + (suffix ? strlen (suffix) : 0));
                      + (suffix ? strlen (suffix) : 0));
      strcpy (name, ".gnu.linkonce");
      strcpy (name, ".gnu.linkonce");
      strcat (name, base_name);
      strcat (name, base_name);
      if (suffix)
      if (suffix)
        strcat (name, suffix);
        strcat (name, suffix);
      linkonce = TRUE;
      linkonce = TRUE;
    }
    }
  else
  else
    {
    {
      /* If the section name ends with ".text", then replace that suffix
      /* If the section name ends with ".text", then replace that suffix
         instead of appending an additional suffix.  */
         instead of appending an additional suffix.  */
      size_t len = strlen (text_name);
      size_t len = strlen (text_name);
      if (len >= 5 && strcmp (text_name + len - 5, ".text") == 0)
      if (len >= 5 && strcmp (text_name + len - 5, ".text") == 0)
        len -= 5;
        len -= 5;
 
 
      name = xmalloc (len + strlen (base_name) + 1);
      name = xmalloc (len + strlen (base_name) + 1);
      strcpy (name, text_name);
      strcpy (name, text_name);
      strcpy (name + len, base_name);
      strcpy (name + len, base_name);
    }
    }
 
 
  /* Canonicalize section names to allow renaming literal sections.
  /* Canonicalize section names to allow renaming literal sections.
     The group name, if any, came from the current text section and
     The group name, if any, came from the current text section and
     has already been canonicalized.  */
     has already been canonicalized.  */
  name = tc_canonicalize_symbol_name (name);
  name = tc_canonicalize_symbol_name (name);
 
 
  seg = bfd_get_section_by_name_if (stdoutput, name, match_section_group,
  seg = bfd_get_section_by_name_if (stdoutput, name, match_section_group,
                                    (void *) group_name);
                                    (void *) group_name);
  if (! seg)
  if (! seg)
    {
    {
      flagword flags;
      flagword flags;
 
 
      seg = subseg_force_new (name, 0);
      seg = subseg_force_new (name, 0);
 
 
      if (! use_abs_literals)
      if (! use_abs_literals)
        {
        {
          /* Add the newly created literal segment to the list.  */
          /* Add the newly created literal segment to the list.  */
          seg_list *n = (seg_list *) xmalloc (sizeof (seg_list));
          seg_list *n = (seg_list *) xmalloc (sizeof (seg_list));
          n->seg = seg;
          n->seg = seg;
          n->next = literal_head->next;
          n->next = literal_head->next;
          literal_head->next = n;
          literal_head->next = n;
        }
        }
 
 
      flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_ALLOC | SEC_LOAD
      flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_ALLOC | SEC_LOAD
               | (linkonce ? (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD) : 0)
               | (linkonce ? (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD) : 0)
               | (use_abs_literals ? SEC_DATA : SEC_CODE));
               | (use_abs_literals ? SEC_DATA : SEC_CODE));
 
 
      elf_group_name (seg) = group_name;
      elf_group_name (seg) = group_name;
 
 
      bfd_set_section_flags (stdoutput, seg, flags);
      bfd_set_section_flags (stdoutput, seg, flags);
      bfd_set_section_alignment (stdoutput, seg, 2);
      bfd_set_section_alignment (stdoutput, seg, 2);
    }
    }
 
 
  *pcached = seg;
  *pcached = seg;
  subseg_set (current_section, current_subsec);
  subseg_set (current_section, current_subsec);
  return seg;
  return seg;
}
}
 
 


/* Property Tables Stuff.  */
/* Property Tables Stuff.  */
 
 
#define XTENSA_INSN_SEC_NAME ".xt.insn"
#define XTENSA_INSN_SEC_NAME ".xt.insn"
#define XTENSA_LIT_SEC_NAME ".xt.lit"
#define XTENSA_LIT_SEC_NAME ".xt.lit"
#define XTENSA_PROP_SEC_NAME ".xt.prop"
#define XTENSA_PROP_SEC_NAME ".xt.prop"
 
 
typedef bfd_boolean (*frag_predicate) (const fragS *);
typedef bfd_boolean (*frag_predicate) (const fragS *);
typedef void (*frag_flags_fn) (const fragS *, frag_flags *);
typedef void (*frag_flags_fn) (const fragS *, frag_flags *);
 
 
static bfd_boolean get_frag_is_literal (const fragS *);
static bfd_boolean get_frag_is_literal (const fragS *);
static void xtensa_create_property_segments
static void xtensa_create_property_segments
  (frag_predicate, frag_predicate, const char *, xt_section_type);
  (frag_predicate, frag_predicate, const char *, xt_section_type);
static void xtensa_create_xproperty_segments
static void xtensa_create_xproperty_segments
  (frag_flags_fn, const char *, xt_section_type);
  (frag_flags_fn, const char *, xt_section_type);
static bfd_boolean exclude_section_from_property_tables (segT);
static bfd_boolean exclude_section_from_property_tables (segT);
static bfd_boolean section_has_property (segT, frag_predicate);
static bfd_boolean section_has_property (segT, frag_predicate);
static bfd_boolean section_has_xproperty (segT, frag_flags_fn);
static bfd_boolean section_has_xproperty (segT, frag_flags_fn);
static void add_xt_block_frags
static void add_xt_block_frags
  (segT, xtensa_block_info **, frag_predicate, frag_predicate);
  (segT, xtensa_block_info **, frag_predicate, frag_predicate);
static bfd_boolean xtensa_frag_flags_is_empty (const frag_flags *);
static bfd_boolean xtensa_frag_flags_is_empty (const frag_flags *);
static void xtensa_frag_flags_init (frag_flags *);
static void xtensa_frag_flags_init (frag_flags *);
static void get_frag_property_flags (const fragS *, frag_flags *);
static void get_frag_property_flags (const fragS *, frag_flags *);
static flagword frag_flags_to_number (const frag_flags *);
static flagword frag_flags_to_number (const frag_flags *);
static void add_xt_prop_frags (segT, xtensa_block_info **, frag_flags_fn);
static void add_xt_prop_frags (segT, xtensa_block_info **, frag_flags_fn);
 
 
/* Set up property tables after relaxation.  */
/* Set up property tables after relaxation.  */
 
 
void
void
xtensa_post_relax_hook (void)
xtensa_post_relax_hook (void)
{
{
  xtensa_move_seg_list_to_beginning (literal_head);
  xtensa_move_seg_list_to_beginning (literal_head);
 
 
  xtensa_find_unmarked_state_frags ();
  xtensa_find_unmarked_state_frags ();
  xtensa_mark_frags_for_org ();
  xtensa_mark_frags_for_org ();
  xtensa_mark_difference_of_two_symbols ();
  xtensa_mark_difference_of_two_symbols ();
 
 
  xtensa_create_property_segments (get_frag_is_literal,
  xtensa_create_property_segments (get_frag_is_literal,
                                   NULL,
                                   NULL,
                                   XTENSA_LIT_SEC_NAME,
                                   XTENSA_LIT_SEC_NAME,
                                   xt_literal_sec);
                                   xt_literal_sec);
  xtensa_create_xproperty_segments (get_frag_property_flags,
  xtensa_create_xproperty_segments (get_frag_property_flags,
                                    XTENSA_PROP_SEC_NAME,
                                    XTENSA_PROP_SEC_NAME,
                                    xt_prop_sec);
                                    xt_prop_sec);
 
 
  if (warn_unaligned_branch_targets)
  if (warn_unaligned_branch_targets)
    bfd_map_over_sections (stdoutput, xtensa_find_unaligned_branch_targets, 0);
    bfd_map_over_sections (stdoutput, xtensa_find_unaligned_branch_targets, 0);
  bfd_map_over_sections (stdoutput, xtensa_find_unaligned_loops, 0);
  bfd_map_over_sections (stdoutput, xtensa_find_unaligned_loops, 0);
}
}
 
 
 
 
/* This function is only meaningful after xtensa_move_literals.  */
/* This function is only meaningful after xtensa_move_literals.  */
 
 
static bfd_boolean
static bfd_boolean
get_frag_is_literal (const fragS *fragP)
get_frag_is_literal (const fragS *fragP)
{
{
  gas_assert (fragP != NULL);
  gas_assert (fragP != NULL);
  return fragP->tc_frag_data.is_literal;
  return fragP->tc_frag_data.is_literal;
}
}
 
 
 
 
static void
static void
xtensa_create_property_segments (frag_predicate property_function,
xtensa_create_property_segments (frag_predicate property_function,
                                 frag_predicate end_property_function,
                                 frag_predicate end_property_function,
                                 const char *section_name_base,
                                 const char *section_name_base,
                                 xt_section_type sec_type)
                                 xt_section_type sec_type)
{
{
  segT *seclist;
  segT *seclist;
 
 
  /* Walk over all of the current segments.
  /* Walk over all of the current segments.
     Walk over each fragment
     Walk over each fragment
     For each non-empty fragment,
     For each non-empty fragment,
     Build a property record (append where possible).  */
     Build a property record (append where possible).  */
 
 
  for (seclist = &stdoutput->sections;
  for (seclist = &stdoutput->sections;
       seclist && *seclist;
       seclist && *seclist;
       seclist = &(*seclist)->next)
       seclist = &(*seclist)->next)
    {
    {
      segT sec = *seclist;
      segT sec = *seclist;
 
 
      if (exclude_section_from_property_tables (sec))
      if (exclude_section_from_property_tables (sec))
        continue;
        continue;
 
 
      if (section_has_property (sec, property_function))
      if (section_has_property (sec, property_function))
        {
        {
          segment_info_type *xt_seg_info;
          segment_info_type *xt_seg_info;
          xtensa_block_info **xt_blocks;
          xtensa_block_info **xt_blocks;
          segT prop_sec = xtensa_make_property_section (sec, section_name_base);
          segT prop_sec = xtensa_make_property_section (sec, section_name_base);
 
 
          prop_sec->output_section = prop_sec;
          prop_sec->output_section = prop_sec;
          subseg_set (prop_sec, 0);
          subseg_set (prop_sec, 0);
          xt_seg_info = seg_info (prop_sec);
          xt_seg_info = seg_info (prop_sec);
          xt_blocks = &xt_seg_info->tc_segment_info_data.blocks[sec_type];
          xt_blocks = &xt_seg_info->tc_segment_info_data.blocks[sec_type];
 
 
          /* Walk over all of the frchains here and add new sections.  */
          /* Walk over all of the frchains here and add new sections.  */
          add_xt_block_frags (sec, xt_blocks, property_function,
          add_xt_block_frags (sec, xt_blocks, property_function,
                              end_property_function);
                              end_property_function);
        }
        }
    }
    }
 
 
  /* Now we fill them out....  */
  /* Now we fill them out....  */
 
 
  for (seclist = &stdoutput->sections;
  for (seclist = &stdoutput->sections;
       seclist && *seclist;
       seclist && *seclist;
       seclist = &(*seclist)->next)
       seclist = &(*seclist)->next)
    {
    {
      segment_info_type *seginfo;
      segment_info_type *seginfo;
      xtensa_block_info *block;
      xtensa_block_info *block;
      segT sec = *seclist;
      segT sec = *seclist;
 
 
      seginfo = seg_info (sec);
      seginfo = seg_info (sec);
      block = seginfo->tc_segment_info_data.blocks[sec_type];
      block = seginfo->tc_segment_info_data.blocks[sec_type];
 
 
      if (block)
      if (block)
        {
        {
          xtensa_block_info *cur_block;
          xtensa_block_info *cur_block;
          int num_recs = 0;
          int num_recs = 0;
          bfd_size_type rec_size;
          bfd_size_type rec_size;
 
 
          for (cur_block = block; cur_block; cur_block = cur_block->next)
          for (cur_block = block; cur_block; cur_block = cur_block->next)
            num_recs++;
            num_recs++;
 
 
          rec_size = num_recs * 8;
          rec_size = num_recs * 8;
          bfd_set_section_size (stdoutput, sec, rec_size);
          bfd_set_section_size (stdoutput, sec, rec_size);
 
 
          if (num_recs)
          if (num_recs)
            {
            {
              char *frag_data;
              char *frag_data;
              int i;
              int i;
 
 
              subseg_set (sec, 0);
              subseg_set (sec, 0);
              frag_data = frag_more (rec_size);
              frag_data = frag_more (rec_size);
              cur_block = block;
              cur_block = block;
              for (i = 0; i < num_recs; i++)
              for (i = 0; i < num_recs; i++)
                {
                {
                  fixS *fix;
                  fixS *fix;
 
 
                  /* Write the fixup.  */
                  /* Write the fixup.  */
                  gas_assert (cur_block);
                  gas_assert (cur_block);
                  fix = fix_new (frag_now, i * 8, 4,
                  fix = fix_new (frag_now, i * 8, 4,
                                 section_symbol (cur_block->sec),
                                 section_symbol (cur_block->sec),
                                 cur_block->offset,
                                 cur_block->offset,
                                 FALSE, BFD_RELOC_32);
                                 FALSE, BFD_RELOC_32);
                  fix->fx_file = "<internal>";
                  fix->fx_file = "<internal>";
                  fix->fx_line = 0;
                  fix->fx_line = 0;
 
 
                  /* Write the length.  */
                  /* Write the length.  */
                  md_number_to_chars (&frag_data[4 + i * 8],
                  md_number_to_chars (&frag_data[4 + i * 8],
                                      cur_block->size, 4);
                                      cur_block->size, 4);
                  cur_block = cur_block->next;
                  cur_block = cur_block->next;
                }
                }
              frag_wane (frag_now);
              frag_wane (frag_now);
              frag_new (0);
              frag_new (0);
              frag_wane (frag_now);
              frag_wane (frag_now);
            }
            }
        }
        }
    }
    }
}
}
 
 
 
 
static void
static void
xtensa_create_xproperty_segments (frag_flags_fn flag_fn,
xtensa_create_xproperty_segments (frag_flags_fn flag_fn,
                                  const char *section_name_base,
                                  const char *section_name_base,
                                  xt_section_type sec_type)
                                  xt_section_type sec_type)
{
{
  segT *seclist;
  segT *seclist;
 
 
  /* Walk over all of the current segments.
  /* Walk over all of the current segments.
     Walk over each fragment.
     Walk over each fragment.
     For each fragment that has instructions,
     For each fragment that has instructions,
     build an instruction record (append where possible).  */
     build an instruction record (append where possible).  */
 
 
  for (seclist = &stdoutput->sections;
  for (seclist = &stdoutput->sections;
       seclist && *seclist;
       seclist && *seclist;
       seclist = &(*seclist)->next)
       seclist = &(*seclist)->next)
    {
    {
      segT sec = *seclist;
      segT sec = *seclist;
 
 
      if (exclude_section_from_property_tables (sec))
      if (exclude_section_from_property_tables (sec))
        continue;
        continue;
 
 
      if (section_has_xproperty (sec, flag_fn))
      if (section_has_xproperty (sec, flag_fn))
        {
        {
          segment_info_type *xt_seg_info;
          segment_info_type *xt_seg_info;
          xtensa_block_info **xt_blocks;
          xtensa_block_info **xt_blocks;
          segT prop_sec = xtensa_make_property_section (sec, section_name_base);
          segT prop_sec = xtensa_make_property_section (sec, section_name_base);
 
 
          prop_sec->output_section = prop_sec;
          prop_sec->output_section = prop_sec;
          subseg_set (prop_sec, 0);
          subseg_set (prop_sec, 0);
          xt_seg_info = seg_info (prop_sec);
          xt_seg_info = seg_info (prop_sec);
          xt_blocks = &xt_seg_info->tc_segment_info_data.blocks[sec_type];
          xt_blocks = &xt_seg_info->tc_segment_info_data.blocks[sec_type];
 
 
          /* Walk over all of the frchains here and add new sections.  */
          /* Walk over all of the frchains here and add new sections.  */
          add_xt_prop_frags (sec, xt_blocks, flag_fn);
          add_xt_prop_frags (sec, xt_blocks, flag_fn);
        }
        }
    }
    }
 
 
  /* Now we fill them out....  */
  /* Now we fill them out....  */
 
 
  for (seclist = &stdoutput->sections;
  for (seclist = &stdoutput->sections;
       seclist && *seclist;
       seclist && *seclist;
       seclist = &(*seclist)->next)
       seclist = &(*seclist)->next)
    {
    {
      segment_info_type *seginfo;
      segment_info_type *seginfo;
      xtensa_block_info *block;
      xtensa_block_info *block;
      segT sec = *seclist;
      segT sec = *seclist;
 
 
      seginfo = seg_info (sec);
      seginfo = seg_info (sec);
      block = seginfo->tc_segment_info_data.blocks[sec_type];
      block = seginfo->tc_segment_info_data.blocks[sec_type];
 
 
      if (block)
      if (block)
        {
        {
          xtensa_block_info *cur_block;
          xtensa_block_info *cur_block;
          int num_recs = 0;
          int num_recs = 0;
          bfd_size_type rec_size;
          bfd_size_type rec_size;
 
 
          for (cur_block = block; cur_block; cur_block = cur_block->next)
          for (cur_block = block; cur_block; cur_block = cur_block->next)
            num_recs++;
            num_recs++;
 
 
          rec_size = num_recs * (8 + 4);
          rec_size = num_recs * (8 + 4);
          bfd_set_section_size (stdoutput, sec, rec_size);
          bfd_set_section_size (stdoutput, sec, rec_size);
          /* elf_section_data (sec)->this_hdr.sh_entsize = 12; */
          /* elf_section_data (sec)->this_hdr.sh_entsize = 12; */
 
 
          if (num_recs)
          if (num_recs)
            {
            {
              char *frag_data;
              char *frag_data;
              int i;
              int i;
 
 
              subseg_set (sec, 0);
              subseg_set (sec, 0);
              frag_data = frag_more (rec_size);
              frag_data = frag_more (rec_size);
              cur_block = block;
              cur_block = block;
              for (i = 0; i < num_recs; i++)
              for (i = 0; i < num_recs; i++)
                {
                {
                  fixS *fix;
                  fixS *fix;
 
 
                  /* Write the fixup.  */
                  /* Write the fixup.  */
                  gas_assert (cur_block);
                  gas_assert (cur_block);
                  fix = fix_new (frag_now, i * 12, 4,
                  fix = fix_new (frag_now, i * 12, 4,
                                 section_symbol (cur_block->sec),
                                 section_symbol (cur_block->sec),
                                 cur_block->offset,
                                 cur_block->offset,
                                 FALSE, BFD_RELOC_32);
                                 FALSE, BFD_RELOC_32);
                  fix->fx_file = "<internal>";
                  fix->fx_file = "<internal>";
                  fix->fx_line = 0;
                  fix->fx_line = 0;
 
 
                  /* Write the length.  */
                  /* Write the length.  */
                  md_number_to_chars (&frag_data[4 + i * 12],
                  md_number_to_chars (&frag_data[4 + i * 12],
                                      cur_block->size, 4);
                                      cur_block->size, 4);
                  md_number_to_chars (&frag_data[8 + i * 12],
                  md_number_to_chars (&frag_data[8 + i * 12],
                                      frag_flags_to_number (&cur_block->flags),
                                      frag_flags_to_number (&cur_block->flags),
                                      sizeof (flagword));
                                      sizeof (flagword));
                  cur_block = cur_block->next;
                  cur_block = cur_block->next;
                }
                }
              frag_wane (frag_now);
              frag_wane (frag_now);
              frag_new (0);
              frag_new (0);
              frag_wane (frag_now);
              frag_wane (frag_now);
            }
            }
        }
        }
    }
    }
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
exclude_section_from_property_tables (segT sec)
exclude_section_from_property_tables (segT sec)
{
{
  flagword flags = bfd_get_section_flags (stdoutput, sec);
  flagword flags = bfd_get_section_flags (stdoutput, sec);
 
 
  /* Sections that don't contribute to the memory footprint are excluded.  */
  /* Sections that don't contribute to the memory footprint are excluded.  */
  if ((flags & SEC_DEBUGGING)
  if ((flags & SEC_DEBUGGING)
      || !(flags & SEC_ALLOC)
      || !(flags & SEC_ALLOC)
      || (flags & SEC_MERGE))
      || (flags & SEC_MERGE))
    return TRUE;
    return TRUE;
 
 
  /* Linker cie and fde optimizations mess up property entries for
  /* Linker cie and fde optimizations mess up property entries for
     eh_frame sections, but there is nothing inside them relevant to
     eh_frame sections, but there is nothing inside them relevant to
     property tables anyway.  */
     property tables anyway.  */
  if (strcmp (sec->name, ".eh_frame") == 0)
  if (strcmp (sec->name, ".eh_frame") == 0)
    return TRUE;
    return TRUE;
 
 
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
section_has_property (segT sec, frag_predicate property_function)
section_has_property (segT sec, frag_predicate property_function)
{
{
  segment_info_type *seginfo = seg_info (sec);
  segment_info_type *seginfo = seg_info (sec);
  fragS *fragP;
  fragS *fragP;
 
 
  if (seginfo && seginfo->frchainP)
  if (seginfo && seginfo->frchainP)
    {
    {
      for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
      for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
        {
        {
          if (property_function (fragP)
          if (property_function (fragP)
              && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
              && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
            return TRUE;
            return TRUE;
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
section_has_xproperty (segT sec, frag_flags_fn property_function)
section_has_xproperty (segT sec, frag_flags_fn property_function)
{
{
  segment_info_type *seginfo = seg_info (sec);
  segment_info_type *seginfo = seg_info (sec);
  fragS *fragP;
  fragS *fragP;
 
 
  if (seginfo && seginfo->frchainP)
  if (seginfo && seginfo->frchainP)
    {
    {
      for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
      for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
        {
        {
          frag_flags prop_flags;
          frag_flags prop_flags;
          property_function (fragP, &prop_flags);
          property_function (fragP, &prop_flags);
          if (!xtensa_frag_flags_is_empty (&prop_flags))
          if (!xtensa_frag_flags_is_empty (&prop_flags))
            return TRUE;
            return TRUE;
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Two types of block sections exist right now: literal and insns.  */
/* Two types of block sections exist right now: literal and insns.  */
 
 
static void
static void
add_xt_block_frags (segT sec,
add_xt_block_frags (segT sec,
                    xtensa_block_info **xt_block,
                    xtensa_block_info **xt_block,
                    frag_predicate property_function,
                    frag_predicate property_function,
                    frag_predicate end_property_function)
                    frag_predicate end_property_function)
{
{
  fragS *fragP;
  fragS *fragP;
 
 
  /* Build it if needed.  */
  /* Build it if needed.  */
  while (*xt_block != NULL)
  while (*xt_block != NULL)
    xt_block = &(*xt_block)->next;
    xt_block = &(*xt_block)->next;
  /* We are either at NULL at the beginning or at the end.  */
  /* We are either at NULL at the beginning or at the end.  */
 
 
  /* Walk through the frags.  */
  /* Walk through the frags.  */
  if (seg_info (sec)->frchainP)
  if (seg_info (sec)->frchainP)
    {
    {
      for (fragP = seg_info (sec)->frchainP->frch_root;
      for (fragP = seg_info (sec)->frchainP->frch_root;
           fragP;
           fragP;
           fragP = fragP->fr_next)
           fragP = fragP->fr_next)
        {
        {
          if (property_function (fragP)
          if (property_function (fragP)
              && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
              && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
            {
            {
              if (*xt_block != NULL)
              if (*xt_block != NULL)
                {
                {
                  if ((*xt_block)->offset + (*xt_block)->size
                  if ((*xt_block)->offset + (*xt_block)->size
                      == fragP->fr_address)
                      == fragP->fr_address)
                    (*xt_block)->size += fragP->fr_fix;
                    (*xt_block)->size += fragP->fr_fix;
                  else
                  else
                    xt_block = &((*xt_block)->next);
                    xt_block = &((*xt_block)->next);
                }
                }
              if (*xt_block == NULL)
              if (*xt_block == NULL)
                {
                {
                  xtensa_block_info *new_block = (xtensa_block_info *)
                  xtensa_block_info *new_block = (xtensa_block_info *)
                    xmalloc (sizeof (xtensa_block_info));
                    xmalloc (sizeof (xtensa_block_info));
                  new_block->sec = sec;
                  new_block->sec = sec;
                  new_block->offset = fragP->fr_address;
                  new_block->offset = fragP->fr_address;
                  new_block->size = fragP->fr_fix;
                  new_block->size = fragP->fr_fix;
                  new_block->next = NULL;
                  new_block->next = NULL;
                  xtensa_frag_flags_init (&new_block->flags);
                  xtensa_frag_flags_init (&new_block->flags);
                  *xt_block = new_block;
                  *xt_block = new_block;
                }
                }
              if (end_property_function
              if (end_property_function
                  && end_property_function (fragP))
                  && end_property_function (fragP))
                {
                {
                  xt_block = &((*xt_block)->next);
                  xt_block = &((*xt_block)->next);
                }
                }
            }
            }
        }
        }
    }
    }
}
}
 
 
 
 
/* Break the encapsulation of add_xt_prop_frags here.  */
/* Break the encapsulation of add_xt_prop_frags here.  */
 
 
static bfd_boolean
static bfd_boolean
xtensa_frag_flags_is_empty (const frag_flags *prop_flags)
xtensa_frag_flags_is_empty (const frag_flags *prop_flags)
{
{
  if (prop_flags->is_literal
  if (prop_flags->is_literal
      || prop_flags->is_insn
      || prop_flags->is_insn
      || prop_flags->is_data
      || prop_flags->is_data
      || prop_flags->is_unreachable)
      || prop_flags->is_unreachable)
    return FALSE;
    return FALSE;
  return TRUE;
  return TRUE;
}
}
 
 
 
 
static void
static void
xtensa_frag_flags_init (frag_flags *prop_flags)
xtensa_frag_flags_init (frag_flags *prop_flags)
{
{
  memset (prop_flags, 0, sizeof (frag_flags));
  memset (prop_flags, 0, sizeof (frag_flags));
}
}
 
 
 
 
static void
static void
get_frag_property_flags (const fragS *fragP, frag_flags *prop_flags)
get_frag_property_flags (const fragS *fragP, frag_flags *prop_flags)
{
{
  xtensa_frag_flags_init (prop_flags);
  xtensa_frag_flags_init (prop_flags);
  if (fragP->tc_frag_data.is_literal)
  if (fragP->tc_frag_data.is_literal)
    prop_flags->is_literal = TRUE;
    prop_flags->is_literal = TRUE;
  if (fragP->tc_frag_data.is_specific_opcode
  if (fragP->tc_frag_data.is_specific_opcode
      || fragP->tc_frag_data.is_no_transform)
      || fragP->tc_frag_data.is_no_transform)
    {
    {
      prop_flags->is_no_transform = TRUE;
      prop_flags->is_no_transform = TRUE;
      if (xtensa_frag_flags_is_empty (prop_flags))
      if (xtensa_frag_flags_is_empty (prop_flags))
        prop_flags->is_data = TRUE;
        prop_flags->is_data = TRUE;
    }
    }
  if (fragP->tc_frag_data.is_unreachable)
  if (fragP->tc_frag_data.is_unreachable)
    prop_flags->is_unreachable = TRUE;
    prop_flags->is_unreachable = TRUE;
  else if (fragP->tc_frag_data.is_insn)
  else if (fragP->tc_frag_data.is_insn)
    {
    {
      prop_flags->is_insn = TRUE;
      prop_flags->is_insn = TRUE;
      if (fragP->tc_frag_data.is_loop_target)
      if (fragP->tc_frag_data.is_loop_target)
        prop_flags->insn.is_loop_target = TRUE;
        prop_flags->insn.is_loop_target = TRUE;
      if (fragP->tc_frag_data.is_branch_target)
      if (fragP->tc_frag_data.is_branch_target)
        prop_flags->insn.is_branch_target = TRUE;
        prop_flags->insn.is_branch_target = TRUE;
      if (fragP->tc_frag_data.is_no_density)
      if (fragP->tc_frag_data.is_no_density)
        prop_flags->insn.is_no_density = TRUE;
        prop_flags->insn.is_no_density = TRUE;
      if (fragP->tc_frag_data.use_absolute_literals)
      if (fragP->tc_frag_data.use_absolute_literals)
        prop_flags->insn.is_abslit = TRUE;
        prop_flags->insn.is_abslit = TRUE;
    }
    }
  if (fragP->tc_frag_data.is_align)
  if (fragP->tc_frag_data.is_align)
    {
    {
      prop_flags->is_align = TRUE;
      prop_flags->is_align = TRUE;
      prop_flags->alignment = fragP->tc_frag_data.alignment;
      prop_flags->alignment = fragP->tc_frag_data.alignment;
      if (xtensa_frag_flags_is_empty (prop_flags))
      if (xtensa_frag_flags_is_empty (prop_flags))
        prop_flags->is_data = TRUE;
        prop_flags->is_data = TRUE;
    }
    }
}
}
 
 
 
 
static flagword
static flagword
frag_flags_to_number (const frag_flags *prop_flags)
frag_flags_to_number (const frag_flags *prop_flags)
{
{
  flagword num = 0;
  flagword num = 0;
  if (prop_flags->is_literal)
  if (prop_flags->is_literal)
    num |= XTENSA_PROP_LITERAL;
    num |= XTENSA_PROP_LITERAL;
  if (prop_flags->is_insn)
  if (prop_flags->is_insn)
    num |= XTENSA_PROP_INSN;
    num |= XTENSA_PROP_INSN;
  if (prop_flags->is_data)
  if (prop_flags->is_data)
    num |= XTENSA_PROP_DATA;
    num |= XTENSA_PROP_DATA;
  if (prop_flags->is_unreachable)
  if (prop_flags->is_unreachable)
    num |= XTENSA_PROP_UNREACHABLE;
    num |= XTENSA_PROP_UNREACHABLE;
  if (prop_flags->insn.is_loop_target)
  if (prop_flags->insn.is_loop_target)
    num |= XTENSA_PROP_INSN_LOOP_TARGET;
    num |= XTENSA_PROP_INSN_LOOP_TARGET;
  if (prop_flags->insn.is_branch_target)
  if (prop_flags->insn.is_branch_target)
    {
    {
      num |= XTENSA_PROP_INSN_BRANCH_TARGET;
      num |= XTENSA_PROP_INSN_BRANCH_TARGET;
      num = SET_XTENSA_PROP_BT_ALIGN (num, prop_flags->insn.bt_align_priority);
      num = SET_XTENSA_PROP_BT_ALIGN (num, prop_flags->insn.bt_align_priority);
    }
    }
 
 
  if (prop_flags->insn.is_no_density)
  if (prop_flags->insn.is_no_density)
    num |= XTENSA_PROP_INSN_NO_DENSITY;
    num |= XTENSA_PROP_INSN_NO_DENSITY;
  if (prop_flags->is_no_transform)
  if (prop_flags->is_no_transform)
    num |= XTENSA_PROP_NO_TRANSFORM;
    num |= XTENSA_PROP_NO_TRANSFORM;
  if (prop_flags->insn.is_no_reorder)
  if (prop_flags->insn.is_no_reorder)
    num |= XTENSA_PROP_INSN_NO_REORDER;
    num |= XTENSA_PROP_INSN_NO_REORDER;
  if (prop_flags->insn.is_abslit)
  if (prop_flags->insn.is_abslit)
    num |= XTENSA_PROP_INSN_ABSLIT;
    num |= XTENSA_PROP_INSN_ABSLIT;
 
 
  if (prop_flags->is_align)
  if (prop_flags->is_align)
    {
    {
      num |= XTENSA_PROP_ALIGN;
      num |= XTENSA_PROP_ALIGN;
      num = SET_XTENSA_PROP_ALIGNMENT (num, prop_flags->alignment);
      num = SET_XTENSA_PROP_ALIGNMENT (num, prop_flags->alignment);
    }
    }
 
 
  return num;
  return num;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
xtensa_frag_flags_combinable (const frag_flags *prop_flags_1,
xtensa_frag_flags_combinable (const frag_flags *prop_flags_1,
                              const frag_flags *prop_flags_2)
                              const frag_flags *prop_flags_2)
{
{
  /* Cannot combine with an end marker.  */
  /* Cannot combine with an end marker.  */
 
 
  if (prop_flags_1->is_literal != prop_flags_2->is_literal)
  if (prop_flags_1->is_literal != prop_flags_2->is_literal)
    return FALSE;
    return FALSE;
  if (prop_flags_1->is_insn != prop_flags_2->is_insn)
  if (prop_flags_1->is_insn != prop_flags_2->is_insn)
    return FALSE;
    return FALSE;
  if (prop_flags_1->is_data != prop_flags_2->is_data)
  if (prop_flags_1->is_data != prop_flags_2->is_data)
    return FALSE;
    return FALSE;
 
 
  if (prop_flags_1->is_insn)
  if (prop_flags_1->is_insn)
    {
    {
      /* Properties of the beginning of the frag.  */
      /* Properties of the beginning of the frag.  */
      if (prop_flags_2->insn.is_loop_target)
      if (prop_flags_2->insn.is_loop_target)
        return FALSE;
        return FALSE;
      if (prop_flags_2->insn.is_branch_target)
      if (prop_flags_2->insn.is_branch_target)
        return FALSE;
        return FALSE;
      if (prop_flags_1->insn.is_no_density !=
      if (prop_flags_1->insn.is_no_density !=
          prop_flags_2->insn.is_no_density)
          prop_flags_2->insn.is_no_density)
        return FALSE;
        return FALSE;
      if (prop_flags_1->is_no_transform !=
      if (prop_flags_1->is_no_transform !=
          prop_flags_2->is_no_transform)
          prop_flags_2->is_no_transform)
        return FALSE;
        return FALSE;
      if (prop_flags_1->insn.is_no_reorder !=
      if (prop_flags_1->insn.is_no_reorder !=
          prop_flags_2->insn.is_no_reorder)
          prop_flags_2->insn.is_no_reorder)
        return FALSE;
        return FALSE;
      if (prop_flags_1->insn.is_abslit !=
      if (prop_flags_1->insn.is_abslit !=
          prop_flags_2->insn.is_abslit)
          prop_flags_2->insn.is_abslit)
        return FALSE;
        return FALSE;
    }
    }
 
 
  if (prop_flags_1->is_align)
  if (prop_flags_1->is_align)
    return FALSE;
    return FALSE;
 
 
  return TRUE;
  return TRUE;
}
}
 
 
 
 
static bfd_vma
static bfd_vma
xt_block_aligned_size (const xtensa_block_info *xt_block)
xt_block_aligned_size (const xtensa_block_info *xt_block)
{
{
  bfd_vma end_addr;
  bfd_vma end_addr;
  unsigned align_bits;
  unsigned align_bits;
 
 
  if (!xt_block->flags.is_align)
  if (!xt_block->flags.is_align)
    return xt_block->size;
    return xt_block->size;
 
 
  end_addr = xt_block->offset + xt_block->size;
  end_addr = xt_block->offset + xt_block->size;
  align_bits = xt_block->flags.alignment;
  align_bits = xt_block->flags.alignment;
  end_addr = ((end_addr + ((1 << align_bits) -1)) >> align_bits) << align_bits;
  end_addr = ((end_addr + ((1 << align_bits) -1)) >> align_bits) << align_bits;
  return end_addr - xt_block->offset;
  return end_addr - xt_block->offset;
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
xtensa_xt_block_combine (xtensa_block_info *xt_block,
xtensa_xt_block_combine (xtensa_block_info *xt_block,
                         const xtensa_block_info *xt_block_2)
                         const xtensa_block_info *xt_block_2)
{
{
  if (xt_block->sec != xt_block_2->sec)
  if (xt_block->sec != xt_block_2->sec)
    return FALSE;
    return FALSE;
  if (xt_block->offset + xt_block_aligned_size (xt_block)
  if (xt_block->offset + xt_block_aligned_size (xt_block)
      != xt_block_2->offset)
      != xt_block_2->offset)
    return FALSE;
    return FALSE;
 
 
  if (xt_block_2->size == 0
  if (xt_block_2->size == 0
      && (!xt_block_2->flags.is_unreachable
      && (!xt_block_2->flags.is_unreachable
          || xt_block->flags.is_unreachable))
          || xt_block->flags.is_unreachable))
    {
    {
      if (xt_block_2->flags.is_align
      if (xt_block_2->flags.is_align
          && xt_block->flags.is_align)
          && xt_block->flags.is_align)
        {
        {
          /* Nothing needed.  */
          /* Nothing needed.  */
          if (xt_block->flags.alignment >= xt_block_2->flags.alignment)
          if (xt_block->flags.alignment >= xt_block_2->flags.alignment)
            return TRUE;
            return TRUE;
        }
        }
      else
      else
        {
        {
          if (xt_block_2->flags.is_align)
          if (xt_block_2->flags.is_align)
            {
            {
              /* Push alignment to previous entry.  */
              /* Push alignment to previous entry.  */
              xt_block->flags.is_align = xt_block_2->flags.is_align;
              xt_block->flags.is_align = xt_block_2->flags.is_align;
              xt_block->flags.alignment = xt_block_2->flags.alignment;
              xt_block->flags.alignment = xt_block_2->flags.alignment;
            }
            }
          return TRUE;
          return TRUE;
        }
        }
    }
    }
  if (!xtensa_frag_flags_combinable (&xt_block->flags,
  if (!xtensa_frag_flags_combinable (&xt_block->flags,
                                     &xt_block_2->flags))
                                     &xt_block_2->flags))
    return FALSE;
    return FALSE;
 
 
  xt_block->size += xt_block_2->size;
  xt_block->size += xt_block_2->size;
 
 
  if (xt_block_2->flags.is_align)
  if (xt_block_2->flags.is_align)
    {
    {
      xt_block->flags.is_align = TRUE;
      xt_block->flags.is_align = TRUE;
      xt_block->flags.alignment = xt_block_2->flags.alignment;
      xt_block->flags.alignment = xt_block_2->flags.alignment;
    }
    }
 
 
  return TRUE;
  return TRUE;
}
}
 
 
 
 
static void
static void
add_xt_prop_frags (segT sec,
add_xt_prop_frags (segT sec,
                   xtensa_block_info **xt_block,
                   xtensa_block_info **xt_block,
                   frag_flags_fn property_function)
                   frag_flags_fn property_function)
{
{
  fragS *fragP;
  fragS *fragP;
 
 
  /* Build it if needed.  */
  /* Build it if needed.  */
  while (*xt_block != NULL)
  while (*xt_block != NULL)
    {
    {
      xt_block = &(*xt_block)->next;
      xt_block = &(*xt_block)->next;
    }
    }
  /* We are either at NULL at the beginning or at the end.  */
  /* We are either at NULL at the beginning or at the end.  */
 
 
  /* Walk through the frags.  */
  /* Walk through the frags.  */
  if (seg_info (sec)->frchainP)
  if (seg_info (sec)->frchainP)
    {
    {
      for (fragP = seg_info (sec)->frchainP->frch_root; fragP;
      for (fragP = seg_info (sec)->frchainP->frch_root; fragP;
           fragP = fragP->fr_next)
           fragP = fragP->fr_next)
        {
        {
          xtensa_block_info tmp_block;
          xtensa_block_info tmp_block;
          tmp_block.sec = sec;
          tmp_block.sec = sec;
          tmp_block.offset = fragP->fr_address;
          tmp_block.offset = fragP->fr_address;
          tmp_block.size = fragP->fr_fix;
          tmp_block.size = fragP->fr_fix;
          tmp_block.next = NULL;
          tmp_block.next = NULL;
          property_function (fragP, &tmp_block.flags);
          property_function (fragP, &tmp_block.flags);
 
 
          if (!xtensa_frag_flags_is_empty (&tmp_block.flags))
          if (!xtensa_frag_flags_is_empty (&tmp_block.flags))
            /* && fragP->fr_fix != 0) */
            /* && fragP->fr_fix != 0) */
            {
            {
              if ((*xt_block) == NULL
              if ((*xt_block) == NULL
                  || !xtensa_xt_block_combine (*xt_block, &tmp_block))
                  || !xtensa_xt_block_combine (*xt_block, &tmp_block))
                {
                {
                  xtensa_block_info *new_block;
                  xtensa_block_info *new_block;
                  if ((*xt_block) != NULL)
                  if ((*xt_block) != NULL)
                    xt_block = &(*xt_block)->next;
                    xt_block = &(*xt_block)->next;
                  new_block = (xtensa_block_info *)
                  new_block = (xtensa_block_info *)
                    xmalloc (sizeof (xtensa_block_info));
                    xmalloc (sizeof (xtensa_block_info));
                  *new_block = tmp_block;
                  *new_block = tmp_block;
                  *xt_block = new_block;
                  *xt_block = new_block;
                }
                }
            }
            }
        }
        }
    }
    }
}
}
 
 


/* op_placement_info_table */
/* op_placement_info_table */
 
 
/* op_placement_info makes it easier to determine which
/* op_placement_info makes it easier to determine which
   ops can go in which slots.  */
   ops can go in which slots.  */
 
 
static void
static void
init_op_placement_info_table (void)
init_op_placement_info_table (void)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_insnbuf ibuf = xtensa_insnbuf_alloc (isa);
  xtensa_insnbuf ibuf = xtensa_insnbuf_alloc (isa);
  xtensa_opcode opcode;
  xtensa_opcode opcode;
  xtensa_format fmt;
  xtensa_format fmt;
  int slot;
  int slot;
  int num_opcodes = xtensa_isa_num_opcodes (isa);
  int num_opcodes = xtensa_isa_num_opcodes (isa);
 
 
  op_placement_table = (op_placement_info_table)
  op_placement_table = (op_placement_info_table)
    xmalloc (sizeof (op_placement_info) * num_opcodes);
    xmalloc (sizeof (op_placement_info) * num_opcodes);
  gas_assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
  gas_assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
 
 
  for (opcode = 0; opcode < num_opcodes; opcode++)
  for (opcode = 0; opcode < num_opcodes; opcode++)
    {
    {
      op_placement_info *opi = &op_placement_table[opcode];
      op_placement_info *opi = &op_placement_table[opcode];
      /* FIXME: Make tinsn allocation dynamic.  */
      /* FIXME: Make tinsn allocation dynamic.  */
      if (xtensa_opcode_num_operands (isa, opcode) > MAX_INSN_ARGS)
      if (xtensa_opcode_num_operands (isa, opcode) > MAX_INSN_ARGS)
        as_fatal (_("too many operands in instruction"));
        as_fatal (_("too many operands in instruction"));
      opi->narrowest = XTENSA_UNDEFINED;
      opi->narrowest = XTENSA_UNDEFINED;
      opi->narrowest_size = 0x7F;
      opi->narrowest_size = 0x7F;
      opi->narrowest_slot = 0;
      opi->narrowest_slot = 0;
      opi->formats = 0;
      opi->formats = 0;
      opi->num_formats = 0;
      opi->num_formats = 0;
      opi->issuef = 0;
      opi->issuef = 0;
      for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
      for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
        {
        {
          opi->slots[fmt] = 0;
          opi->slots[fmt] = 0;
          for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
          for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
            {
            {
              if (xtensa_opcode_encode (isa, fmt, slot, ibuf, opcode) == 0)
              if (xtensa_opcode_encode (isa, fmt, slot, ibuf, opcode) == 0)
                {
                {
                  int fmt_length = xtensa_format_length (isa, fmt);
                  int fmt_length = xtensa_format_length (isa, fmt);
                  opi->issuef++;
                  opi->issuef++;
                  set_bit (fmt, opi->formats);
                  set_bit (fmt, opi->formats);
                  set_bit (slot, opi->slots[fmt]);
                  set_bit (slot, opi->slots[fmt]);
                  if (fmt_length < opi->narrowest_size
                  if (fmt_length < opi->narrowest_size
                      || (fmt_length == opi->narrowest_size
                      || (fmt_length == opi->narrowest_size
                          && (xtensa_format_num_slots (isa, fmt)
                          && (xtensa_format_num_slots (isa, fmt)
                              < xtensa_format_num_slots (isa,
                              < xtensa_format_num_slots (isa,
                                                         opi->narrowest))))
                                                         opi->narrowest))))
                    {
                    {
                      opi->narrowest = fmt;
                      opi->narrowest = fmt;
                      opi->narrowest_size = fmt_length;
                      opi->narrowest_size = fmt_length;
                      opi->narrowest_slot = slot;
                      opi->narrowest_slot = slot;
                    }
                    }
                }
                }
            }
            }
          if (opi->formats)
          if (opi->formats)
            opi->num_formats++;
            opi->num_formats++;
        }
        }
    }
    }
  xtensa_insnbuf_free (isa, ibuf);
  xtensa_insnbuf_free (isa, ibuf);
}
}
 
 
 
 
bfd_boolean
bfd_boolean
opcode_fits_format_slot (xtensa_opcode opcode, xtensa_format fmt, int slot)
opcode_fits_format_slot (xtensa_opcode opcode, xtensa_format fmt, int slot)
{
{
  return bit_is_set (slot, op_placement_table[opcode].slots[fmt]);
  return bit_is_set (slot, op_placement_table[opcode].slots[fmt]);
}
}
 
 
 
 
/* If the opcode is available in a single slot format, return its size.  */
/* If the opcode is available in a single slot format, return its size.  */
 
 
static int
static int
xg_get_single_size (xtensa_opcode opcode)
xg_get_single_size (xtensa_opcode opcode)
{
{
  return op_placement_table[opcode].narrowest_size;
  return op_placement_table[opcode].narrowest_size;
}
}
 
 
 
 
static xtensa_format
static xtensa_format
xg_get_single_format (xtensa_opcode opcode)
xg_get_single_format (xtensa_opcode opcode)
{
{
  return op_placement_table[opcode].narrowest;
  return op_placement_table[opcode].narrowest;
}
}
 
 
 
 
static int
static int
xg_get_single_slot (xtensa_opcode opcode)
xg_get_single_slot (xtensa_opcode opcode)
{
{
  return op_placement_table[opcode].narrowest_slot;
  return op_placement_table[opcode].narrowest_slot;
}
}
 
 


/* Instruction Stack Functions (from "xtensa-istack.h").  */
/* Instruction Stack Functions (from "xtensa-istack.h").  */
 
 
void
void
istack_init (IStack *stack)
istack_init (IStack *stack)
{
{
  memset (stack, 0, sizeof (IStack));
  memset (stack, 0, sizeof (IStack));
  stack->ninsn = 0;
  stack->ninsn = 0;
}
}
 
 
 
 
bfd_boolean
bfd_boolean
istack_empty (IStack *stack)
istack_empty (IStack *stack)
{
{
  return (stack->ninsn == 0);
  return (stack->ninsn == 0);
}
}
 
 
 
 
bfd_boolean
bfd_boolean
istack_full (IStack *stack)
istack_full (IStack *stack)
{
{
  return (stack->ninsn == MAX_ISTACK);
  return (stack->ninsn == MAX_ISTACK);
}
}
 
 
 
 
/* Return a pointer to the top IStack entry.
/* Return a pointer to the top IStack entry.
   It is an error to call this if istack_empty () is TRUE. */
   It is an error to call this if istack_empty () is TRUE. */
 
 
TInsn *
TInsn *
istack_top (IStack *stack)
istack_top (IStack *stack)
{
{
  int rec = stack->ninsn - 1;
  int rec = stack->ninsn - 1;
  gas_assert (!istack_empty (stack));
  gas_assert (!istack_empty (stack));
  return &stack->insn[rec];
  return &stack->insn[rec];
}
}
 
 
 
 
/* Add a new TInsn to an IStack.
/* Add a new TInsn to an IStack.
   It is an error to call this if istack_full () is TRUE.  */
   It is an error to call this if istack_full () is TRUE.  */
 
 
void
void
istack_push (IStack *stack, TInsn *insn)
istack_push (IStack *stack, TInsn *insn)
{
{
  int rec = stack->ninsn;
  int rec = stack->ninsn;
  gas_assert (!istack_full (stack));
  gas_assert (!istack_full (stack));
  stack->insn[rec] = *insn;
  stack->insn[rec] = *insn;
  stack->ninsn++;
  stack->ninsn++;
}
}
 
 
 
 
/* Clear space for the next TInsn on the IStack and return a pointer
/* Clear space for the next TInsn on the IStack and return a pointer
   to it.  It is an error to call this if istack_full () is TRUE.  */
   to it.  It is an error to call this if istack_full () is TRUE.  */
 
 
TInsn *
TInsn *
istack_push_space (IStack *stack)
istack_push_space (IStack *stack)
{
{
  int rec = stack->ninsn;
  int rec = stack->ninsn;
  TInsn *insn;
  TInsn *insn;
  gas_assert (!istack_full (stack));
  gas_assert (!istack_full (stack));
  insn = &stack->insn[rec];
  insn = &stack->insn[rec];
  tinsn_init (insn);
  tinsn_init (insn);
  stack->ninsn++;
  stack->ninsn++;
  return insn;
  return insn;
}
}
 
 
 
 
/* Remove the last pushed instruction.  It is an error to call this if
/* Remove the last pushed instruction.  It is an error to call this if
   istack_empty () returns TRUE.  */
   istack_empty () returns TRUE.  */
 
 
void
void
istack_pop (IStack *stack)
istack_pop (IStack *stack)
{
{
  int rec = stack->ninsn - 1;
  int rec = stack->ninsn - 1;
  gas_assert (!istack_empty (stack));
  gas_assert (!istack_empty (stack));
  stack->ninsn--;
  stack->ninsn--;
  tinsn_init (&stack->insn[rec]);
  tinsn_init (&stack->insn[rec]);
}
}
 
 


/* TInsn functions.  */
/* TInsn functions.  */
 
 
void
void
tinsn_init (TInsn *dst)
tinsn_init (TInsn *dst)
{
{
  memset (dst, 0, sizeof (TInsn));
  memset (dst, 0, sizeof (TInsn));
}
}
 
 
 
 
/* Return TRUE if ANY of the operands in the insn are symbolic.  */
/* Return TRUE if ANY of the operands in the insn are symbolic.  */
 
 
static bfd_boolean
static bfd_boolean
tinsn_has_symbolic_operands (const TInsn *insn)
tinsn_has_symbolic_operands (const TInsn *insn)
{
{
  int i;
  int i;
  int n = insn->ntok;
  int n = insn->ntok;
 
 
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
 
 
  for (i = 0; i < n; ++i)
  for (i = 0; i < n; ++i)
    {
    {
      switch (insn->tok[i].X_op)
      switch (insn->tok[i].X_op)
        {
        {
        case O_register:
        case O_register:
        case O_constant:
        case O_constant:
          break;
          break;
        default:
        default:
          return TRUE;
          return TRUE;
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
bfd_boolean
bfd_boolean
tinsn_has_invalid_symbolic_operands (const TInsn *insn)
tinsn_has_invalid_symbolic_operands (const TInsn *insn)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  int i;
  int i;
  int n = insn->ntok;
  int n = insn->ntok;
 
 
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
 
 
  for (i = 0; i < n; ++i)
  for (i = 0; i < n; ++i)
    {
    {
      switch (insn->tok[i].X_op)
      switch (insn->tok[i].X_op)
        {
        {
        case O_register:
        case O_register:
        case O_constant:
        case O_constant:
          break;
          break;
        case O_big:
        case O_big:
        case O_illegal:
        case O_illegal:
        case O_absent:
        case O_absent:
          /* Errors for these types are caught later.  */
          /* Errors for these types are caught later.  */
          break;
          break;
        case O_hi16:
        case O_hi16:
        case O_lo16:
        case O_lo16:
        default:
        default:
          /* Symbolic immediates are only allowed on the last immediate
          /* Symbolic immediates are only allowed on the last immediate
             operand.  At this time, CONST16 is the only opcode where we
             operand.  At this time, CONST16 is the only opcode where we
             support non-PC-relative relocations.  */
             support non-PC-relative relocations.  */
          if (i != get_relaxable_immed (insn->opcode)
          if (i != get_relaxable_immed (insn->opcode)
              || (xtensa_operand_is_PCrelative (isa, insn->opcode, i) != 1
              || (xtensa_operand_is_PCrelative (isa, insn->opcode, i) != 1
                  && insn->opcode != xtensa_const16_opcode))
                  && insn->opcode != xtensa_const16_opcode))
            {
            {
              as_bad (_("invalid symbolic operand"));
              as_bad (_("invalid symbolic operand"));
              return TRUE;
              return TRUE;
            }
            }
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* For assembly code with complex expressions (e.g. subtraction),
/* For assembly code with complex expressions (e.g. subtraction),
   we have to build them in the literal pool so that
   we have to build them in the literal pool so that
   their results are calculated correctly after relaxation.
   their results are calculated correctly after relaxation.
   The relaxation only handles expressions that
   The relaxation only handles expressions that
   boil down to SYMBOL + OFFSET.  */
   boil down to SYMBOL + OFFSET.  */
 
 
static bfd_boolean
static bfd_boolean
tinsn_has_complex_operands (const TInsn *insn)
tinsn_has_complex_operands (const TInsn *insn)
{
{
  int i;
  int i;
  int n = insn->ntok;
  int n = insn->ntok;
  gas_assert (insn->insn_type == ITYPE_INSN);
  gas_assert (insn->insn_type == ITYPE_INSN);
  for (i = 0; i < n; ++i)
  for (i = 0; i < n; ++i)
    {
    {
      switch (insn->tok[i].X_op)
      switch (insn->tok[i].X_op)
        {
        {
        case O_register:
        case O_register:
        case O_constant:
        case O_constant:
        case O_symbol:
        case O_symbol:
        case O_lo16:
        case O_lo16:
        case O_hi16:
        case O_hi16:
          break;
          break;
        default:
        default:
          return TRUE;
          return TRUE;
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Encode a TInsn opcode and its constant operands into slotbuf.
/* Encode a TInsn opcode and its constant operands into slotbuf.
   Return TRUE if there is a symbol in the immediate field.  This
   Return TRUE if there is a symbol in the immediate field.  This
   function assumes that:
   function assumes that:
   1) The number of operands are correct.
   1) The number of operands are correct.
   2) The insn_type is ITYPE_INSN.
   2) The insn_type is ITYPE_INSN.
   3) The opcode can be encoded in the specified format and slot.
   3) The opcode can be encoded in the specified format and slot.
   4) Operands are either O_constant or O_symbol, and all constants fit.  */
   4) Operands are either O_constant or O_symbol, and all constants fit.  */
 
 
static bfd_boolean
static bfd_boolean
tinsn_to_slotbuf (xtensa_format fmt,
tinsn_to_slotbuf (xtensa_format fmt,
                  int slot,
                  int slot,
                  TInsn *tinsn,
                  TInsn *tinsn,
                  xtensa_insnbuf slotbuf)
                  xtensa_insnbuf slotbuf)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_opcode opcode = tinsn->opcode;
  xtensa_opcode opcode = tinsn->opcode;
  bfd_boolean has_fixup = FALSE;
  bfd_boolean has_fixup = FALSE;
  int noperands = xtensa_opcode_num_operands (isa, opcode);
  int noperands = xtensa_opcode_num_operands (isa, opcode);
  int i;
  int i;
 
 
  gas_assert (tinsn->insn_type == ITYPE_INSN);
  gas_assert (tinsn->insn_type == ITYPE_INSN);
  if (noperands != tinsn->ntok)
  if (noperands != tinsn->ntok)
    as_fatal (_("operand number mismatch"));
    as_fatal (_("operand number mismatch"));
 
 
  if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opcode))
  if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opcode))
    {
    {
      as_bad (_("cannot encode opcode \"%s\" in the given format \"%s\""),
      as_bad (_("cannot encode opcode \"%s\" in the given format \"%s\""),
              xtensa_opcode_name (isa, opcode), xtensa_format_name (isa, fmt));
              xtensa_opcode_name (isa, opcode), xtensa_format_name (isa, fmt));
      return FALSE;
      return FALSE;
    }
    }
 
 
  for (i = 0; i < noperands; i++)
  for (i = 0; i < noperands; i++)
    {
    {
      expressionS *expr = &tinsn->tok[i];
      expressionS *expr = &tinsn->tok[i];
      int rc;
      int rc;
      unsigned line;
      unsigned line;
      char *file_name;
      char *file_name;
      uint32 opnd_value;
      uint32 opnd_value;
 
 
      switch (expr->X_op)
      switch (expr->X_op)
        {
        {
        case O_register:
        case O_register:
          if (xtensa_operand_is_visible (isa, opcode, i) == 0)
          if (xtensa_operand_is_visible (isa, opcode, i) == 0)
            break;
            break;
          /* The register number has already been checked in
          /* The register number has already been checked in
             expression_maybe_register, so we don't need to check here.  */
             expression_maybe_register, so we don't need to check here.  */
          opnd_value = expr->X_add_number;
          opnd_value = expr->X_add_number;
          (void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
          (void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
          rc = xtensa_operand_set_field (isa, opcode, i, fmt, slot, slotbuf,
          rc = xtensa_operand_set_field (isa, opcode, i, fmt, slot, slotbuf,
                                         opnd_value);
                                         opnd_value);
          if (rc != 0)
          if (rc != 0)
            as_warn (_("xtensa-isa failure: %s"), xtensa_isa_error_msg (isa));
            as_warn (_("xtensa-isa failure: %s"), xtensa_isa_error_msg (isa));
          break;
          break;
 
 
        case O_constant:
        case O_constant:
          if (xtensa_operand_is_visible (isa, opcode, i) == 0)
          if (xtensa_operand_is_visible (isa, opcode, i) == 0)
            break;
            break;
          as_where (&file_name, &line);
          as_where (&file_name, &line);
          /* It is a constant and we called this function
          /* It is a constant and we called this function
             then we have to try to fit it.  */
             then we have to try to fit it.  */
          xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
          xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
                                      expr->X_add_number, file_name, line);
                                      expr->X_add_number, file_name, line);
          break;
          break;
 
 
        default:
        default:
          has_fixup = TRUE;
          has_fixup = TRUE;
          break;
          break;
        }
        }
    }
    }
 
 
  return has_fixup;
  return has_fixup;
}
}
 
 
 
 
/* Encode a single TInsn into an insnbuf.  If the opcode can only be encoded
/* Encode a single TInsn into an insnbuf.  If the opcode can only be encoded
   into a multi-slot instruction, fill the other slots with NOPs.
   into a multi-slot instruction, fill the other slots with NOPs.
   Return TRUE if there is a symbol in the immediate field.  See also the
   Return TRUE if there is a symbol in the immediate field.  See also the
   assumptions listed for tinsn_to_slotbuf.  */
   assumptions listed for tinsn_to_slotbuf.  */
 
 
static bfd_boolean
static bfd_boolean
tinsn_to_insnbuf (TInsn *tinsn, xtensa_insnbuf insnbuf)
tinsn_to_insnbuf (TInsn *tinsn, xtensa_insnbuf insnbuf)
{
{
  static xtensa_insnbuf slotbuf = 0;
  static xtensa_insnbuf slotbuf = 0;
  static vliw_insn vinsn;
  static vliw_insn vinsn;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  bfd_boolean has_fixup = FALSE;
  bfd_boolean has_fixup = FALSE;
  int i;
  int i;
 
 
  if (!slotbuf)
  if (!slotbuf)
    {
    {
      slotbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
      xg_init_vinsn (&vinsn);
      xg_init_vinsn (&vinsn);
    }
    }
 
 
  xg_clear_vinsn (&vinsn);
  xg_clear_vinsn (&vinsn);
 
 
  bundle_tinsn (tinsn, &vinsn);
  bundle_tinsn (tinsn, &vinsn);
 
 
  xtensa_format_encode (isa, vinsn.format, insnbuf);
  xtensa_format_encode (isa, vinsn.format, insnbuf);
 
 
  for (i = 0; i < vinsn.num_slots; i++)
  for (i = 0; i < vinsn.num_slots; i++)
    {
    {
      /* Only one slot may have a fix-up because the rest contains NOPs.  */
      /* Only one slot may have a fix-up because the rest contains NOPs.  */
      has_fixup |=
      has_fixup |=
        tinsn_to_slotbuf (vinsn.format, i, &vinsn.slots[i], vinsn.slotbuf[i]);
        tinsn_to_slotbuf (vinsn.format, i, &vinsn.slots[i], vinsn.slotbuf[i]);
      xtensa_format_set_slot (isa, vinsn.format, i, insnbuf, vinsn.slotbuf[i]);
      xtensa_format_set_slot (isa, vinsn.format, i, insnbuf, vinsn.slotbuf[i]);
    }
    }
 
 
  return has_fixup;
  return has_fixup;
}
}
 
 
 
 
/* Check the instruction arguments.  Return TRUE on failure.  */
/* Check the instruction arguments.  Return TRUE on failure.  */
 
 
static bfd_boolean
static bfd_boolean
tinsn_check_arguments (const TInsn *insn)
tinsn_check_arguments (const TInsn *insn)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_opcode opcode = insn->opcode;
  xtensa_opcode opcode = insn->opcode;
  xtensa_regfile t1_regfile, t2_regfile;
  xtensa_regfile t1_regfile, t2_regfile;
  int t1_reg, t2_reg;
  int t1_reg, t2_reg;
  int t1_base_reg, t1_last_reg;
  int t1_base_reg, t1_last_reg;
  int t2_base_reg, t2_last_reg;
  int t2_base_reg, t2_last_reg;
  char t1_inout, t2_inout;
  char t1_inout, t2_inout;
  int i, j;
  int i, j;
 
 
  if (opcode == XTENSA_UNDEFINED)
  if (opcode == XTENSA_UNDEFINED)
    {
    {
      as_bad (_("invalid opcode"));
      as_bad (_("invalid opcode"));
      return TRUE;
      return TRUE;
    }
    }
 
 
  if (xtensa_opcode_num_operands (isa, opcode) > insn->ntok)
  if (xtensa_opcode_num_operands (isa, opcode) > insn->ntok)
    {
    {
      as_bad (_("too few operands"));
      as_bad (_("too few operands"));
      return TRUE;
      return TRUE;
    }
    }
 
 
  if (xtensa_opcode_num_operands (isa, opcode) < insn->ntok)
  if (xtensa_opcode_num_operands (isa, opcode) < insn->ntok)
    {
    {
      as_bad (_("too many operands"));
      as_bad (_("too many operands"));
      return TRUE;
      return TRUE;
    }
    }
 
 
  /* Check registers.  */
  /* Check registers.  */
  for (j = 0; j < insn->ntok; j++)
  for (j = 0; j < insn->ntok; j++)
    {
    {
      if (xtensa_operand_is_register (isa, insn->opcode, j) != 1)
      if (xtensa_operand_is_register (isa, insn->opcode, j) != 1)
        continue;
        continue;
 
 
      t2_regfile = xtensa_operand_regfile (isa, insn->opcode, j);
      t2_regfile = xtensa_operand_regfile (isa, insn->opcode, j);
      t2_base_reg = insn->tok[j].X_add_number;
      t2_base_reg = insn->tok[j].X_add_number;
      t2_last_reg
      t2_last_reg
        = t2_base_reg + xtensa_operand_num_regs (isa, insn->opcode, j);
        = t2_base_reg + xtensa_operand_num_regs (isa, insn->opcode, j);
 
 
      for (i = 0; i < insn->ntok; i++)
      for (i = 0; i < insn->ntok; i++)
        {
        {
          if (i == j)
          if (i == j)
            continue;
            continue;
 
 
          if (xtensa_operand_is_register (isa, insn->opcode, i) != 1)
          if (xtensa_operand_is_register (isa, insn->opcode, i) != 1)
            continue;
            continue;
 
 
          t1_regfile = xtensa_operand_regfile (isa, insn->opcode, i);
          t1_regfile = xtensa_operand_regfile (isa, insn->opcode, i);
 
 
          if (t1_regfile != t2_regfile)
          if (t1_regfile != t2_regfile)
            continue;
            continue;
 
 
          t1_inout = xtensa_operand_inout (isa, insn->opcode, i);
          t1_inout = xtensa_operand_inout (isa, insn->opcode, i);
          t2_inout = xtensa_operand_inout (isa, insn->opcode, j);
          t2_inout = xtensa_operand_inout (isa, insn->opcode, j);
 
 
          t1_base_reg = insn->tok[i].X_add_number;
          t1_base_reg = insn->tok[i].X_add_number;
          t1_last_reg = (t1_base_reg
          t1_last_reg = (t1_base_reg
                         + xtensa_operand_num_regs (isa, insn->opcode, i));
                         + xtensa_operand_num_regs (isa, insn->opcode, i));
 
 
          for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
          for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
            {
            {
              for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
              for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
                {
                {
                  if (t1_reg != t2_reg)
                  if (t1_reg != t2_reg)
                    continue;
                    continue;
 
 
                  if (t1_inout != 'i' && t2_inout != 'i')
                  if (t1_inout != 'i' && t2_inout != 'i')
                    {
                    {
                      as_bad (_("multiple writes to the same register"));
                      as_bad (_("multiple writes to the same register"));
                      return TRUE;
                      return TRUE;
                    }
                    }
                }
                }
            }
            }
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
/* Load an instruction from its encoded form.  */
/* Load an instruction from its encoded form.  */
 
 
static void
static void
tinsn_from_chars (TInsn *tinsn, char *f, int slot)
tinsn_from_chars (TInsn *tinsn, char *f, int slot)
{
{
  vliw_insn vinsn;
  vliw_insn vinsn;
 
 
  xg_init_vinsn (&vinsn);
  xg_init_vinsn (&vinsn);
  vinsn_from_chars (&vinsn, f);
  vinsn_from_chars (&vinsn, f);
 
 
  *tinsn = vinsn.slots[slot];
  *tinsn = vinsn.slots[slot];
  xg_free_vinsn (&vinsn);
  xg_free_vinsn (&vinsn);
}
}
 
 
 
 
static void
static void
tinsn_from_insnbuf (TInsn *tinsn,
tinsn_from_insnbuf (TInsn *tinsn,
                    xtensa_insnbuf slotbuf,
                    xtensa_insnbuf slotbuf,
                    xtensa_format fmt,
                    xtensa_format fmt,
                    int slot)
                    int slot)
{
{
  int i;
  int i;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
 
 
  /* Find the immed.  */
  /* Find the immed.  */
  tinsn_init (tinsn);
  tinsn_init (tinsn);
  tinsn->insn_type = ITYPE_INSN;
  tinsn->insn_type = ITYPE_INSN;
  tinsn->is_specific_opcode = FALSE;    /* must not be specific */
  tinsn->is_specific_opcode = FALSE;    /* must not be specific */
  tinsn->opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
  tinsn->opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
  tinsn->ntok = xtensa_opcode_num_operands (isa, tinsn->opcode);
  tinsn->ntok = xtensa_opcode_num_operands (isa, tinsn->opcode);
  for (i = 0; i < tinsn->ntok; i++)
  for (i = 0; i < tinsn->ntok; i++)
    {
    {
      set_expr_const (&tinsn->tok[i],
      set_expr_const (&tinsn->tok[i],
                      xtensa_insnbuf_get_operand (slotbuf, fmt, slot,
                      xtensa_insnbuf_get_operand (slotbuf, fmt, slot,
                                                  tinsn->opcode, i));
                                                  tinsn->opcode, i));
    }
    }
}
}
 
 
 
 
/* Read the value of the relaxable immed from the fr_symbol and fr_offset.  */
/* Read the value of the relaxable immed from the fr_symbol and fr_offset.  */
 
 
static void
static void
tinsn_immed_from_frag (TInsn *tinsn, fragS *fragP, int slot)
tinsn_immed_from_frag (TInsn *tinsn, fragS *fragP, int slot)
{
{
  xtensa_opcode opcode = tinsn->opcode;
  xtensa_opcode opcode = tinsn->opcode;
  int opnum;
  int opnum;
 
 
  if (fragP->tc_frag_data.slot_symbols[slot])
  if (fragP->tc_frag_data.slot_symbols[slot])
    {
    {
      opnum = get_relaxable_immed (opcode);
      opnum = get_relaxable_immed (opcode);
      gas_assert (opnum >= 0);
      gas_assert (opnum >= 0);
      set_expr_symbol_offset (&tinsn->tok[opnum],
      set_expr_symbol_offset (&tinsn->tok[opnum],
                              fragP->tc_frag_data.slot_symbols[slot],
                              fragP->tc_frag_data.slot_symbols[slot],
                              fragP->tc_frag_data.slot_offsets[slot]);
                              fragP->tc_frag_data.slot_offsets[slot]);
    }
    }
  tinsn->extra_arg = fragP->tc_frag_data.free_reg[slot];
  tinsn->extra_arg = fragP->tc_frag_data.free_reg[slot];
}
}
 
 
 
 
static int
static int
get_num_stack_text_bytes (IStack *istack)
get_num_stack_text_bytes (IStack *istack)
{
{
  int i;
  int i;
  int text_bytes = 0;
  int text_bytes = 0;
 
 
  for (i = 0; i < istack->ninsn; i++)
  for (i = 0; i < istack->ninsn; i++)
    {
    {
      TInsn *tinsn = &istack->insn[i];
      TInsn *tinsn = &istack->insn[i];
      if (tinsn->insn_type == ITYPE_INSN)
      if (tinsn->insn_type == ITYPE_INSN)
        text_bytes += xg_get_single_size (tinsn->opcode);
        text_bytes += xg_get_single_size (tinsn->opcode);
    }
    }
  return text_bytes;
  return text_bytes;
}
}
 
 
 
 
static int
static int
get_num_stack_literal_bytes (IStack *istack)
get_num_stack_literal_bytes (IStack *istack)
{
{
  int i;
  int i;
  int lit_bytes = 0;
  int lit_bytes = 0;
 
 
  for (i = 0; i < istack->ninsn; i++)
  for (i = 0; i < istack->ninsn; i++)
    {
    {
      TInsn *tinsn = &istack->insn[i];
      TInsn *tinsn = &istack->insn[i];
      if (tinsn->insn_type == ITYPE_LITERAL && tinsn->ntok == 1)
      if (tinsn->insn_type == ITYPE_LITERAL && tinsn->ntok == 1)
        lit_bytes += 4;
        lit_bytes += 4;
    }
    }
  return lit_bytes;
  return lit_bytes;
}
}
 
 


/* vliw_insn functions.  */
/* vliw_insn functions.  */
 
 
static void
static void
xg_init_vinsn (vliw_insn *v)
xg_init_vinsn (vliw_insn *v)
{
{
  int i;
  int i;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
 
 
  xg_clear_vinsn (v);
  xg_clear_vinsn (v);
 
 
  v->insnbuf = xtensa_insnbuf_alloc (isa);
  v->insnbuf = xtensa_insnbuf_alloc (isa);
  if (v->insnbuf == NULL)
  if (v->insnbuf == NULL)
    as_fatal (_("out of memory"));
    as_fatal (_("out of memory"));
 
 
  for (i = 0; i < config_max_slots; i++)
  for (i = 0; i < config_max_slots; i++)
    {
    {
      v->slotbuf[i] = xtensa_insnbuf_alloc (isa);
      v->slotbuf[i] = xtensa_insnbuf_alloc (isa);
      if (v->slotbuf[i] == NULL)
      if (v->slotbuf[i] == NULL)
        as_fatal (_("out of memory"));
        as_fatal (_("out of memory"));
    }
    }
}
}
 
 
 
 
static void
static void
xg_clear_vinsn (vliw_insn *v)
xg_clear_vinsn (vliw_insn *v)
{
{
  int i;
  int i;
 
 
  memset (v, 0, offsetof (vliw_insn, slots)
  memset (v, 0, offsetof (vliw_insn, slots)
                + sizeof(TInsn) * config_max_slots);
                + sizeof(TInsn) * config_max_slots);
 
 
  v->format = XTENSA_UNDEFINED;
  v->format = XTENSA_UNDEFINED;
  v->num_slots = 0;
  v->num_slots = 0;
  v->inside_bundle = FALSE;
  v->inside_bundle = FALSE;
 
 
  if (xt_saved_debug_type != DEBUG_NONE)
  if (xt_saved_debug_type != DEBUG_NONE)
    debug_type = xt_saved_debug_type;
    debug_type = xt_saved_debug_type;
 
 
  for (i = 0; i < config_max_slots; i++)
  for (i = 0; i < config_max_slots; i++)
    v->slots[i].opcode = XTENSA_UNDEFINED;
    v->slots[i].opcode = XTENSA_UNDEFINED;
}
}
 
 
 
 
static void
static void
xg_copy_vinsn (vliw_insn *dst, vliw_insn *src)
xg_copy_vinsn (vliw_insn *dst, vliw_insn *src)
{
{
  memcpy (dst, src,
  memcpy (dst, src,
          offsetof(vliw_insn, slots) + src->num_slots * sizeof(TInsn));
          offsetof(vliw_insn, slots) + src->num_slots * sizeof(TInsn));
  dst->insnbuf = src->insnbuf;
  dst->insnbuf = src->insnbuf;
  memcpy (dst->slotbuf, src->slotbuf, src->num_slots * sizeof(xtensa_insnbuf));
  memcpy (dst->slotbuf, src->slotbuf, src->num_slots * sizeof(xtensa_insnbuf));
}
}
 
 
 
 
static bfd_boolean
static bfd_boolean
vinsn_has_specific_opcodes (vliw_insn *v)
vinsn_has_specific_opcodes (vliw_insn *v)
{
{
  int i;
  int i;
 
 
  for (i = 0; i < v->num_slots; i++)
  for (i = 0; i < v->num_slots; i++)
    {
    {
      if (v->slots[i].is_specific_opcode)
      if (v->slots[i].is_specific_opcode)
        return TRUE;
        return TRUE;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
static void
static void
xg_free_vinsn (vliw_insn *v)
xg_free_vinsn (vliw_insn *v)
{
{
  int i;
  int i;
  xtensa_insnbuf_free (xtensa_default_isa, v->insnbuf);
  xtensa_insnbuf_free (xtensa_default_isa, v->insnbuf);
  for (i = 0; i < config_max_slots; i++)
  for (i = 0; i < config_max_slots; i++)
    xtensa_insnbuf_free (xtensa_default_isa, v->slotbuf[i]);
    xtensa_insnbuf_free (xtensa_default_isa, v->slotbuf[i]);
}
}
 
 
 
 
/* Encode a vliw_insn into an insnbuf.  Return TRUE if there are any symbolic
/* Encode a vliw_insn into an insnbuf.  Return TRUE if there are any symbolic
   operands.  See also the assumptions listed for tinsn_to_slotbuf.  */
   operands.  See also the assumptions listed for tinsn_to_slotbuf.  */
 
 
static bfd_boolean
static bfd_boolean
vinsn_to_insnbuf (vliw_insn *vinsn,
vinsn_to_insnbuf (vliw_insn *vinsn,
                  char *frag_offset,
                  char *frag_offset,
                  fragS *fragP,
                  fragS *fragP,
                  bfd_boolean record_fixup)
                  bfd_boolean record_fixup)
{
{
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_format fmt = vinsn->format;
  xtensa_format fmt = vinsn->format;
  xtensa_insnbuf insnbuf = vinsn->insnbuf;
  xtensa_insnbuf insnbuf = vinsn->insnbuf;
  int slot;
  int slot;
  bfd_boolean has_fixup = FALSE;
  bfd_boolean has_fixup = FALSE;
 
 
  xtensa_format_encode (isa, fmt, insnbuf);
  xtensa_format_encode (isa, fmt, insnbuf);
 
 
  for (slot = 0; slot < vinsn->num_slots; slot++)
  for (slot = 0; slot < vinsn->num_slots; slot++)
    {
    {
      TInsn *tinsn = &vinsn->slots[slot];
      TInsn *tinsn = &vinsn->slots[slot];
      expressionS *extra_arg = &tinsn->extra_arg;
      expressionS *extra_arg = &tinsn->extra_arg;
      bfd_boolean tinsn_has_fixup =
      bfd_boolean tinsn_has_fixup =
        tinsn_to_slotbuf (vinsn->format, slot, tinsn,
        tinsn_to_slotbuf (vinsn->format, slot, tinsn,
                          vinsn->slotbuf[slot]);
                          vinsn->slotbuf[slot]);
 
 
      xtensa_format_set_slot (isa, fmt, slot,
      xtensa_format_set_slot (isa, fmt, slot,
                              insnbuf, vinsn->slotbuf[slot]);
                              insnbuf, vinsn->slotbuf[slot]);
      if (extra_arg->X_op != O_illegal && extra_arg->X_op != O_register)
      if (extra_arg->X_op != O_illegal && extra_arg->X_op != O_register)
        {
        {
          if (vinsn->num_slots != 1)
          if (vinsn->num_slots != 1)
            as_bad (_("TLS relocation not allowed in FLIX bundle"));
            as_bad (_("TLS relocation not allowed in FLIX bundle"));
          else if (record_fixup)
          else if (record_fixup)
            /* Instructions that generate TLS relocations should always be
            /* Instructions that generate TLS relocations should always be
               relaxed in the front-end.  If "record_fixup" is set, then this
               relaxed in the front-end.  If "record_fixup" is set, then this
               function is being called during back-end relaxation, so flag
               function is being called during back-end relaxation, so flag
               the unexpected behavior as an error.  */
               the unexpected behavior as an error.  */
            as_bad (_("unexpected TLS relocation"));
            as_bad (_("unexpected TLS relocation"));
          else
          else
            fix_new (fragP, frag_offset - fragP->fr_literal,
            fix_new (fragP, frag_offset - fragP->fr_literal,
                     xtensa_format_length (isa, fmt),
                     xtensa_format_length (isa, fmt),
                     extra_arg->X_add_symbol, extra_arg->X_add_number,
                     extra_arg->X_add_symbol, extra_arg->X_add_number,
                     FALSE, map_operator_to_reloc (extra_arg->X_op, FALSE));
                     FALSE, map_operator_to_reloc (extra_arg->X_op, FALSE));
        }
        }
      if (tinsn_has_fixup)
      if (tinsn_has_fixup)
        {
        {
          int i;
          int i;
          xtensa_opcode opcode = tinsn->opcode;
          xtensa_opcode opcode = tinsn->opcode;
          int noperands = xtensa_opcode_num_operands (isa, opcode);
          int noperands = xtensa_opcode_num_operands (isa, opcode);
          has_fixup = TRUE;
          has_fixup = TRUE;
 
 
          for (i = 0; i < noperands; i++)
          for (i = 0; i < noperands; i++)
            {
            {
              expressionS* expr = &tinsn->tok[i];
              expressionS* expr = &tinsn->tok[i];
              switch (expr->X_op)
              switch (expr->X_op)
                {
                {
                case O_symbol:
                case O_symbol:
                case O_lo16:
                case O_lo16:
                case O_hi16:
                case O_hi16:
                  if (get_relaxable_immed (opcode) == i)
                  if (get_relaxable_immed (opcode) == i)
                    {
                    {
                      /* Add a fix record for the instruction, except if this
                      /* Add a fix record for the instruction, except if this
                         function is being called prior to relaxation, i.e.,
                         function is being called prior to relaxation, i.e.,
                         if record_fixup is false, and the instruction might
                         if record_fixup is false, and the instruction might
                         be relaxed later.  */
                         be relaxed later.  */
                      if (record_fixup
                      if (record_fixup
                          || tinsn->is_specific_opcode
                          || tinsn->is_specific_opcode
                          || !xg_is_relaxable_insn (tinsn, 0))
                          || !xg_is_relaxable_insn (tinsn, 0))
                        {
                        {
                          xg_add_opcode_fix (tinsn, i, fmt, slot, expr, fragP,
                          xg_add_opcode_fix (tinsn, i, fmt, slot, expr, fragP,
                                             frag_offset - fragP->fr_literal);
                                             frag_offset - fragP->fr_literal);
                        }
                        }
                      else
                      else
                        {
                        {
                          if (expr->X_op != O_symbol)
                          if (expr->X_op != O_symbol)
                            as_bad (_("invalid operand"));
                            as_bad (_("invalid operand"));
                          tinsn->symbol = expr->X_add_symbol;
                          tinsn->symbol = expr->X_add_symbol;
                          tinsn->offset = expr->X_add_number;
                          tinsn->offset = expr->X_add_number;
                        }
                        }
                    }
                    }
                  else
                  else
                    as_bad (_("symbolic operand not allowed"));
                    as_bad (_("symbolic operand not allowed"));
                  break;
                  break;
 
 
                case O_constant:
                case O_constant:
                case O_register:
                case O_register:
                  break;
                  break;
 
 
                default:
                default:
                  as_bad (_("expression too complex"));
                  as_bad (_("expression too complex"));
                  break;
                  break;
                }
                }
            }
            }
        }
        }
    }
    }
 
 
  return has_fixup;
  return has_fixup;
}
}
 
 
 
 
static void
static void
vinsn_from_chars (vliw_insn *vinsn, char *f)
vinsn_from_chars (vliw_insn *vinsn, char *f)
{
{
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf insnbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  static xtensa_insnbuf slotbuf = NULL;
  int i;
  int i;
  xtensa_format fmt;
  xtensa_format fmt;
  xtensa_isa isa = xtensa_default_isa;
  xtensa_isa isa = xtensa_default_isa;
 
 
  if (!insnbuf)
  if (!insnbuf)
    {
    {
      insnbuf = xtensa_insnbuf_alloc (isa);
      insnbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
      slotbuf = xtensa_insnbuf_alloc (isa);
    }
    }
 
 
  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) f, 0);
  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) f, 0);
  fmt = xtensa_format_decode (isa, insnbuf);
  fmt = xtensa_format_decode (isa, insnbuf);
  if (fmt == XTENSA_UNDEFINED)
  if (fmt == XTENSA_UNDEFINED)
    as_fatal (_("cannot decode instruction format"));
    as_fatal (_("cannot decode instruction format"));
  vinsn->format = fmt;
  vinsn->format = fmt;
  vinsn->num_slots = xtensa_format_num_slots (isa, fmt);
  vinsn->num_slots = xtensa_format_num_slots (isa, fmt);
 
 
  for (i = 0; i < vinsn->num_slots; i++)
  for (i = 0; i < vinsn->num_slots; i++)
    {
    {
      TInsn *tinsn = &vinsn->slots[i];
      TInsn *tinsn = &vinsn->slots[i];
      xtensa_format_get_slot (isa, fmt, i, insnbuf, slotbuf);
      xtensa_format_get_slot (isa, fmt, i, insnbuf, slotbuf);
      tinsn_from_insnbuf (tinsn, slotbuf, fmt, i);
      tinsn_from_insnbuf (tinsn, slotbuf, fmt, i);
    }
    }
}
}
 
 


/* Expression utilities.  */
/* Expression utilities.  */
 
 
/* Return TRUE if the expression is an integer constant.  */
/* Return TRUE if the expression is an integer constant.  */
 
 
bfd_boolean
bfd_boolean
expr_is_const (const expressionS *s)
expr_is_const (const expressionS *s)
{
{
  return (s->X_op == O_constant);
  return (s->X_op == O_constant);
}
}
 
 
 
 
/* Get the expression constant.
/* Get the expression constant.
   Calling this is illegal if expr_is_const () returns TRUE.  */
   Calling this is illegal if expr_is_const () returns TRUE.  */
 
 
offsetT
offsetT
get_expr_const (const expressionS *s)
get_expr_const (const expressionS *s)
{
{
  gas_assert (expr_is_const (s));
  gas_assert (expr_is_const (s));
  return s->X_add_number;
  return s->X_add_number;
}
}
 
 
 
 
/* Set the expression to a constant value.  */
/* Set the expression to a constant value.  */
 
 
void
void
set_expr_const (expressionS *s, offsetT val)
set_expr_const (expressionS *s, offsetT val)
{
{
  s->X_op = O_constant;
  s->X_op = O_constant;
  s->X_add_number = val;
  s->X_add_number = val;
  s->X_add_symbol = NULL;
  s->X_add_symbol = NULL;
  s->X_op_symbol = NULL;
  s->X_op_symbol = NULL;
}
}
 
 
 
 
bfd_boolean
bfd_boolean
expr_is_register (const expressionS *s)
expr_is_register (const expressionS *s)
{
{
  return (s->X_op == O_register);
  return (s->X_op == O_register);
}
}
 
 
 
 
/* Get the expression constant.
/* Get the expression constant.
   Calling this is illegal if expr_is_const () returns TRUE.  */
   Calling this is illegal if expr_is_const () returns TRUE.  */
 
 
offsetT
offsetT
get_expr_register (const expressionS *s)
get_expr_register (const expressionS *s)
{
{
  gas_assert (expr_is_register (s));
  gas_assert (expr_is_register (s));
  return s->X_add_number;
  return s->X_add_number;
}
}
 
 
 
 
/* Set the expression to a symbol + constant offset.  */
/* Set the expression to a symbol + constant offset.  */
 
 
void
void
set_expr_symbol_offset (expressionS *s, symbolS *sym, offsetT offset)
set_expr_symbol_offset (expressionS *s, symbolS *sym, offsetT offset)
{
{
  s->X_op = O_symbol;
  s->X_op = O_symbol;
  s->X_add_symbol = sym;
  s->X_add_symbol = sym;
  s->X_op_symbol = NULL;        /* unused */
  s->X_op_symbol = NULL;        /* unused */
  s->X_add_number = offset;
  s->X_add_number = offset;
}
}
 
 
 
 
/* Return TRUE if the two expressions are equal.  */
/* Return TRUE if the two expressions are equal.  */
 
 
bfd_boolean
bfd_boolean
expr_is_equal (expressionS *s1, expressionS *s2)
expr_is_equal (expressionS *s1, expressionS *s2)
{
{
  if (s1->X_op != s2->X_op)
  if (s1->X_op != s2->X_op)
    return FALSE;
    return FALSE;
  if (s1->X_add_symbol != s2->X_add_symbol)
  if (s1->X_add_symbol != s2->X_add_symbol)
    return FALSE;
    return FALSE;
  if (s1->X_op_symbol != s2->X_op_symbol)
  if (s1->X_op_symbol != s2->X_op_symbol)
    return FALSE;
    return FALSE;
  if (s1->X_add_number != s2->X_add_number)
  if (s1->X_add_number != s2->X_add_number)
    return FALSE;
    return FALSE;
  return TRUE;
  return TRUE;
}
}
 
 
 
 
static void
static void
copy_expr (expressionS *dst, const expressionS *src)
copy_expr (expressionS *dst, const expressionS *src)
{
{
  memcpy (dst, src, sizeof (expressionS));
  memcpy (dst, src, sizeof (expressionS));
}
}
 
 


/* Support for the "--rename-section" option.  */
/* Support for the "--rename-section" option.  */
 
 
struct rename_section_struct
struct rename_section_struct
{
{
  char *old_name;
  char *old_name;
  char *new_name;
  char *new_name;
  struct rename_section_struct *next;
  struct rename_section_struct *next;
};
};
 
 
static struct rename_section_struct *section_rename;
static struct rename_section_struct *section_rename;
 
 
 
 
/* Parse the string "oldname=new_name(:oldname2=new_name2)*" and add
/* Parse the string "oldname=new_name(:oldname2=new_name2)*" and add
   entries to the section_rename list.  Note: Specifying multiple
   entries to the section_rename list.  Note: Specifying multiple
   renamings separated by colons is not documented and is retained only
   renamings separated by colons is not documented and is retained only
   for backward compatibility.  */
   for backward compatibility.  */
 
 
static void
static void
build_section_rename (const char *arg)
build_section_rename (const char *arg)
{
{
  struct rename_section_struct *r;
  struct rename_section_struct *r;
  char *this_arg = NULL;
  char *this_arg = NULL;
  char *next_arg = NULL;
  char *next_arg = NULL;
 
 
  for (this_arg = xstrdup (arg); this_arg != NULL; this_arg = next_arg)
  for (this_arg = xstrdup (arg); this_arg != NULL; this_arg = next_arg)
    {
    {
      char *old_name, *new_name;
      char *old_name, *new_name;
 
 
      if (this_arg)
      if (this_arg)
        {
        {
          next_arg = strchr (this_arg, ':');
          next_arg = strchr (this_arg, ':');
          if (next_arg)
          if (next_arg)
            {
            {
              *next_arg = '\0';
              *next_arg = '\0';
              next_arg++;
              next_arg++;
            }
            }
        }
        }
 
 
      old_name = this_arg;
      old_name = this_arg;
      new_name = strchr (this_arg, '=');
      new_name = strchr (this_arg, '=');
 
 
      if (*old_name == '\0')
      if (*old_name == '\0')
        {
        {
          as_warn (_("ignoring extra '-rename-section' delimiter ':'"));
          as_warn (_("ignoring extra '-rename-section' delimiter ':'"));
          continue;
          continue;
        }
        }
      if (!new_name || new_name[1] == '\0')
      if (!new_name || new_name[1] == '\0')
        {
        {
          as_warn (_("ignoring invalid '-rename-section' specification: '%s'"),
          as_warn (_("ignoring invalid '-rename-section' specification: '%s'"),
                   old_name);
                   old_name);
          continue;
          continue;
        }
        }
      *new_name = '\0';
      *new_name = '\0';
      new_name++;
      new_name++;
 
 
      /* Check for invalid section renaming.  */
      /* Check for invalid section renaming.  */
      for (r = section_rename; r != NULL; r = r->next)
      for (r = section_rename; r != NULL; r = r->next)
        {
        {
          if (strcmp (r->old_name, old_name) == 0)
          if (strcmp (r->old_name, old_name) == 0)
            as_bad (_("section %s renamed multiple times"), old_name);
            as_bad (_("section %s renamed multiple times"), old_name);
          if (strcmp (r->new_name, new_name) == 0)
          if (strcmp (r->new_name, new_name) == 0)
            as_bad (_("multiple sections remapped to output section %s"),
            as_bad (_("multiple sections remapped to output section %s"),
                    new_name);
                    new_name);
        }
        }
 
 
      /* Now add it.  */
      /* Now add it.  */
      r = (struct rename_section_struct *)
      r = (struct rename_section_struct *)
        xmalloc (sizeof (struct rename_section_struct));
        xmalloc (sizeof (struct rename_section_struct));
      r->old_name = xstrdup (old_name);
      r->old_name = xstrdup (old_name);
      r->new_name = xstrdup (new_name);
      r->new_name = xstrdup (new_name);
      r->next = section_rename;
      r->next = section_rename;
      section_rename = r;
      section_rename = r;
    }
    }
}
}
 
 
 
 
char *
char *
xtensa_section_rename (char *name)
xtensa_section_rename (char *name)
{
{
  struct rename_section_struct *r = section_rename;
  struct rename_section_struct *r = section_rename;
 
 
  for (r = section_rename; r != NULL; r = r->next)
  for (r = section_rename; r != NULL; r = r->next)
    {
    {
      if (strcmp (r->old_name, name) == 0)
      if (strcmp (r->old_name, name) == 0)
        return r->new_name;
        return r->new_name;
    }
    }
 
 
  return name;
  return name;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.