* config/tc-xtensa.c (xg_assembly_relax): Comment termination rules.
[deliverable/binutils-gdb.git] / gas / config / tc-xtensa.c
index 6f9198a23cee31f1d9ca63d9ec0524e748aefd23..aef6b65e77bff99c6627d2fe34dd5a4e54ece9f4 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-xtensa.c -- Assemble Xtensa instructions.
 /* tc-xtensa.c -- Assemble Xtensa instructions.
-   Copyright 2003 Free Software Foundation, Inc.
+   Copyright 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
 
    This file is part of GAS, the GNU Assembler.
 
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 59 Temple Place - Suite 330, Boston, 
-   MA 02111-1307, USA.  */
+   the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
 
-#include <string.h>
+#include <limits.h>
 #include "as.h"
 #include "sb.h"
 #include "safe-ctype.h"
 #include "tc-xtensa.h"
 #include "as.h"
 #include "sb.h"
 #include "safe-ctype.h"
 #include "tc-xtensa.h"
-#include "frags.h"
 #include "subsegs.h"
 #include "xtensa-relax.h"
 #include "xtensa-istack.h"
 #include "subsegs.h"
 #include "xtensa-relax.h"
 #include "xtensa-istack.h"
 #include "struc-symbol.h"
 #include "xtensa-config.h"
 
 #include "struc-symbol.h"
 #include "xtensa-config.h"
 
+/* Provide default values for new configuration settings.  */
+#ifndef XSHAL_ABI
+#define XSHAL_ABI 0
+#endif
+
 #ifndef uint32
 #define uint32 unsigned int
 #endif
 #ifndef uint32
 #define uint32 unsigned int
 #endif
 
 /* Notes:
 
 
 /* Notes:
 
-   There are 3 forms for instructions,
-   1) the MEMORY format -- this is the encoding 2 or 3 byte instruction
-   2) the TInsn -- handles instructions/labels and literals;
-      all operands are assumed to be expressions
-   3) the IStack -- a stack of TInsn.  this allows us to 
-      reason about the generated expansion instructions
-  
    Naming conventions (used somewhat inconsistently):
       The xtensa_ functions are exported
       The xg_ functions are internal
    Naming conventions (used somewhat inconsistently):
       The xtensa_ functions are exported
       The xg_ functions are internal
@@ -71,17 +68,25 @@ const char EXP_CHARS[] = "eE";
 const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 
 const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 
-/* Flag to indicate whether the hardware supports the density option.
-   If not, enabling density instructions (via directives or --density flag)
-   is illegal.  */
+/* Flags to indicate whether the hardware supports the density and
+   absolute literals options.  */
 
 
-#if STATIC_LIBISA
 bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
 bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
-#else
-bfd_boolean density_supported = TRUE;
-#endif
+bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
+
+/* Maximum width we would pad an unreachable frag to get alignment.  */
+#define UNREACHABLE_MAX_WIDTH  8
+
+static vliw_insn cur_vinsn;
+
+unsigned xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
 
 
-#define XTENSA_FETCH_WIDTH 4
+static enum debug_info_type xt_saved_debug_type = DEBUG_NONE;
+
+/* Some functions are only valid in the front end.  This variable
+   allows us to assert that we haven't crossed over into the
+   back end.  */
+static bfd_boolean past_xtensa_end = FALSE;
 
 /* Flags for properties of the last instruction in a segment.  */
 #define FLAG_IS_A0_WRITER      0x1
 
 /* Flags for properties of the last instruction in a segment.  */
 #define FLAG_IS_A0_WRITER      0x1
@@ -94,31 +99,33 @@ bfd_boolean density_supported = TRUE;
    their own special .fini.literal and .init.literal sections.  */
 
 #define LITERAL_SECTION_NAME           xtensa_section_rename (".literal")
    their own special .fini.literal and .init.literal sections.  */
 
 #define LITERAL_SECTION_NAME           xtensa_section_rename (".literal")
-#define FINI_SECTION_NAME              xtensa_section_rename (".fini")
+#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_LITERAL_SECTION_NAME      xtensa_section_rename (".fini.literal")
-#define INIT_LITERAL_SECTION_NAME      xtensa_section_rename (".init.literal")
+#define FINI_SECTION_NAME              xtensa_section_rename (".fini")
 
 
 
 
-/* This type is used for the directive_stack to keep track of the 
-   state of the literal collection pools.  */
+/* 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
+   used to determine the literal section names; otherwise, the literal
+   sections are determined based on the current text section.  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
+   values are valid.  */
 
 typedef struct lit_state_struct
 {
 
 typedef struct lit_state_struct
 {
-  const char *lit_seg_name;
-  const char *init_lit_seg_name;
-  const char *fini_lit_seg_name;
+  char *lit_prefix;
+  segT current_text_seg;
   segT lit_seg;
   segT lit_seg;
-  segT init_lit_seg;
-  segT fini_lit_seg;
+  segT lit4_seg;
 } lit_state;
 
 static lit_state default_lit_sections;
 
 
 } lit_state;
 
 static lit_state default_lit_sections;
 
 
-/* We keep lists of literal segments.  The seg_list type is the node
-   for such a list.  The *_literal_head locals are the heads of the
-   various lists.  All of these lists have a dummy node at the start.  */
+/* 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,
+   with the literal_head_h dummy node at the start.  */
 
 typedef struct seg_list_struct
 {
 
 typedef struct seg_list_struct
 {
@@ -128,10 +135,6 @@ typedef struct seg_list_struct
 
 static seg_list literal_head_h;
 static seg_list *literal_head = &literal_head_h;
 
 static seg_list literal_head_h;
 static seg_list *literal_head = &literal_head_h;
-static seg_list init_literal_head_h;
-static seg_list *init_literal_head = &init_literal_head_h;
-static seg_list fini_literal_head_h;
-static seg_list *fini_literal_head = &fini_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
@@ -153,9 +156,148 @@ 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
+   if both options are available.  */
+int prefer_const16 = 0;
+int prefer_l32r = 0;
+
 /* Global flag to indicate when we are emitting literals.  */
 int generating_literals = 0;
 
 /* Global flag to indicate when we are emitting literals.  */
 int generating_literals = 0;
 
+/* The following PROPERTY table definitions are copied from
+   <elf/xtensa.h> and must be kept in sync with the code there.  */
+
+/* Flags in the property tables to specify whether blocks of memory
+   are literals, instructions, data, or unreachable.  For
+   instructions, blocks that begin loop targets and branch targets are
+   designated.  Blocks that do not allow density, instruction
+   reordering or transformation are also specified.  Finally, for
+   branch targets, branch target alignment priority is included.
+   Alignment of the next block is specified in the current block
+   and the size of the current block does not include any fill required
+   to align to the next block.  */
+
+#define XTENSA_PROP_LITERAL            0x00000001
+#define XTENSA_PROP_INSN               0x00000002
+#define XTENSA_PROP_DATA               0x00000004
+#define XTENSA_PROP_UNREACHABLE                0x00000008
+/* Instruction only properties at beginning of code.  */
+#define XTENSA_PROP_INSN_LOOP_TARGET   0x00000010
+#define XTENSA_PROP_INSN_BRANCH_TARGET 0x00000020
+/* Instruction only properties about code.  */
+#define XTENSA_PROP_INSN_NO_DENSITY    0x00000040
+#define XTENSA_PROP_INSN_NO_REORDER    0x00000080
+/* Historically, NO_TRANSFORM was a property of instructions,
+   but it should apply to literals under certain circumstances.  */
+#define XTENSA_PROP_NO_TRANSFORM       0x00000100
+
+/*  Branch target alignment information.  This transmits information
+    to the linker optimization about the priority of aligning a
+    particular block for branch target alignment: None, low priority,
+    high priority, or required.  These only need to be checked in
+    instruction blocks marked as XTENSA_PROP_INSN_BRANCH_TARGET.
+    Common usage is
+
+    switch (GET_XTENSA_PROP_BT_ALIGN (flags))
+    case XTENSA_PROP_BT_ALIGN_NONE:
+    case XTENSA_PROP_BT_ALIGN_LOW:
+    case XTENSA_PROP_BT_ALIGN_HIGH:
+    case XTENSA_PROP_BT_ALIGN_REQUIRE:
+*/
+#define XTENSA_PROP_BT_ALIGN_MASK       0x00000600
+
+/* No branch target alignment.  */
+#define XTENSA_PROP_BT_ALIGN_NONE       0x0
+/* Low priority branch target alignment.  */
+#define XTENSA_PROP_BT_ALIGN_LOW        0x1
+/* High priority branch target alignment.  */
+#define XTENSA_PROP_BT_ALIGN_HIGH       0x2
+/* Required branch target alignment.  */
+#define XTENSA_PROP_BT_ALIGN_REQUIRE    0x3
+
+#define GET_XTENSA_PROP_BT_ALIGN(flag) \
+  (((unsigned) ((flag) & (XTENSA_PROP_BT_ALIGN_MASK))) >> 9)
+#define SET_XTENSA_PROP_BT_ALIGN(flag, align) \
+  (((flag) & (~XTENSA_PROP_BT_ALIGN_MASK)) | \
+    (((align) << 9) & XTENSA_PROP_BT_ALIGN_MASK))
+
+
+/* Alignment is specified in the block BEFORE the one that needs
+   alignment.  Up to 5 bits.  Use GET_XTENSA_PROP_ALIGNMENT(flags) to
+   get the required alignment specified as a power of 2.  Use
+   SET_XTENSA_PROP_ALIGNMENT(flags, pow2) to set the required
+   alignment.  Be careful of side effects since the SET will evaluate
+   flags twice.  Also, note that the SIZE of a block in the property
+   table does not include the alignment size, so the alignment fill
+   must be calculated to determine if two blocks are contiguous.
+   TEXT_ALIGN is not currently implemented but is a placeholder for a
+   possible future implementation.  */
+
+#define XTENSA_PROP_ALIGN              0x00000800
+
+#define XTENSA_PROP_ALIGNMENT_MASK      0x0001f000
+
+#define GET_XTENSA_PROP_ALIGNMENT(flag) \
+  (((unsigned) ((flag) & (XTENSA_PROP_ALIGNMENT_MASK))) >> 12)
+#define SET_XTENSA_PROP_ALIGNMENT(flag, align) \
+  (((flag) & (~XTENSA_PROP_ALIGNMENT_MASK)) | \
+    (((align) << 12) & XTENSA_PROP_ALIGNMENT_MASK))
+
+#define XTENSA_PROP_INSN_ABSLIT 0x00020000
+
+
+/* Structure for saving instruction and alignment per-fragment data
+   that will be written to the object file.  This structure is
+   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
+   in frag_flags_to_number.  */
+
+typedef struct frag_flags_struct frag_flags;
+
+struct frag_flags_struct
+{
+  /* is_literal should only be used after xtensa_move_literals.
+     If you need to check if you are generating a literal fragment,
+     then use the generating_literals global.  */
+
+  unsigned is_literal : 1;
+  unsigned is_insn : 1;
+  unsigned is_data : 1;
+  unsigned is_unreachable : 1;
+
+  /* is_specific_opcode implies no_transform.  */
+  unsigned is_no_transform : 1;
+
+  struct
+  {
+    unsigned is_loop_target : 1;
+    unsigned is_branch_target : 1; /* Branch targets have a priority.  */
+    unsigned bt_align_priority : 2;
+
+    unsigned is_no_density : 1;
+    /* no_longcalls flag does not need to be placed in the object file.  */
+
+    unsigned is_no_reorder : 1;
+
+    /* Uses absolute literal addressing for l32r.  */
+    unsigned is_abslit : 1;
+  } insn;
+  unsigned is_align : 1;
+  unsigned alignment : 5;
+};
+
+
+/* Structure for saving information about a block of property data
+   for frags that have the same flags.  */
+struct xtensa_block_info_struct
+{
+  segT sec;
+  bfd_vma offset;
+  size_t size;
+  frag_flags flags;
+  struct xtensa_block_info_struct *next;
+};
+
 
 /* Structure for saving the current state before emitting literals.  */
 typedef struct emit_state_struct
 
 /* Structure for saving the current state before emitting literals.  */
 typedef struct emit_state_struct
@@ -167,6 +309,71 @@ typedef struct emit_state_struct
 } emit_state;
 
 
 } emit_state;
 
 
+/* Opcode placement information */
+
+typedef unsigned long long bitfield;
+#define bit_is_set(bit, bf)    ((bf) & (0x01ll << (bit)))
+#define set_bit(bit, bf)       ((bf) |= (0x01ll << (bit)))
+#define clear_bit(bit, bf)     ((bf) &= ~(0x01ll << (bit)))
+
+#define MAX_FORMATS 32
+
+typedef struct op_placement_info_struct
+{
+  int num_formats;
+  /* A number describing how restrictive the issue is for this
+     opcode.  For example, an opcode that fits lots of different
+     formats has a high freedom, as does an opcode that fits
+     only one format but many slots in that format.  The most
+     restrictive is the opcode that fits only one slot in one
+     format.  */
+  int issuef;
+  xtensa_format narrowest;
+  char narrowest_size;
+  char narrowest_slot;
+
+  /* formats is a bitfield with the Nth bit set
+     if the opcode fits in the Nth xtensa_format.  */
+  bitfield formats;
+
+  /* slots[N]'s Mth bit is set if the op fits in the
+     Mth slot of the Nth xtensa_format.  */
+  bitfield slots[MAX_FORMATS];
+
+  /* A count of the number of slots in a given format
+     an op can fit (i.e., the bitcount of the slot field above).  */
+  char slots_in_format[MAX_FORMATS];
+
+} op_placement_info, *op_placement_info_table;
+
+op_placement_info_table op_placement_table;
+
+
+/* Extra expression types.  */
+
+#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_lo16         O_md3   /* use low 16 bits of symbolic value */
+
+struct suffix_reloc_map
+{
+  char *suffix;
+  int length;
+  bfd_reloc_code_real_type reloc;
+  unsigned char operator;
+};
+
+#define SUFFIX_MAP(str, reloc, op) { str, sizeof (str) - 1, reloc, op }
+
+static struct suffix_reloc_map suffix_relocs[] =
+{
+  SUFFIX_MAP ("l",     BFD_RELOC_LO16,                 O_lo16),
+  SUFFIX_MAP ("h",     BFD_RELOC_HI16,                 O_hi16),
+  SUFFIX_MAP ("plt",   BFD_RELOC_XTENSA_PLT,           O_pltrel),
+  { (char *) 0, 0,     BFD_RELOC_UNUSED,               0 }
+};
+
+
 /* Directives.  */
 
 typedef enum
 /* Directives.  */
 
 typedef enum
@@ -174,11 +381,13 @@ typedef enum
   directive_none = 0,
   directive_literal,
   directive_density,
   directive_none = 0,
   directive_literal,
   directive_density,
-  directive_generics,
-  directive_relax,
+  directive_transform,
   directive_freeregs,
   directive_longcalls,
   directive_freeregs,
   directive_longcalls,
-  directive_literal_prefix
+  directive_literal_prefix,
+  directive_schedule,
+  directive_absolute_literals,
+  directive_last_directive
 } directiveE;
 
 typedef struct
 } directiveE;
 
 typedef struct
@@ -189,539 +398,142 @@ typedef struct
 
 const directive_infoS directive_info[] =
 {
 
 const directive_infoS directive_info[] =
 {
-  {"none",     FALSE},
-  {"literal",  FALSE},
-  {"density",  TRUE},
-  {"generics", TRUE},
-  {"relax",    TRUE},
-  {"freeregs", FALSE},
-  {"longcalls",        TRUE},
-  {"literal_prefix", FALSE}
+  { "none",            FALSE },
+  { "literal",         FALSE },
+  { "density",         TRUE },
+  { "transform",       TRUE },
+  { "freeregs",                FALSE },
+  { "longcalls",       TRUE },
+  { "literal_prefix",  FALSE },
+  { "schedule",                TRUE },
+  { "absolute-literals", TRUE }
 };
 
 bfd_boolean directive_state[] =
 {
   FALSE,                       /* none */
   FALSE,                       /* literal */
 };
 
 bfd_boolean directive_state[] =
 {
   FALSE,                       /* none */
   FALSE,                       /* literal */
-#if STATIC_LIBISA && !XCHAL_HAVE_DENSITY
+#if !XCHAL_HAVE_DENSITY
   FALSE,                       /* density */
 #else
   TRUE,                                /* density */
 #endif
   FALSE,                       /* density */
 #else
   TRUE,                                /* density */
 #endif
-  TRUE,                                /* generics */
-  TRUE,                                /* relax */
+  TRUE,                                /* transform */
   FALSE,                       /* freeregs */
   FALSE,                       /* longcalls */
   FALSE,                       /* freeregs */
   FALSE,                       /* longcalls */
-  FALSE                                /* literal_prefix */
-};
-
-
-enum xtensa_relax_statesE
-{
-  RELAX_ALIGN_NEXT_OPCODE,
-  /* Use the first opcode of the next fragment to determine the
-     alignment requirements.  This is ONLY used for LOOPS
-     currently.  */
-
-  RELAX_DESIRE_ALIGN_IF_TARGET,
-  /* These are placed in front of labels.  They will all be converted
-     to RELAX_DESIRE_ALIGN / RELAX_LOOP_END or rs_fill of 0 before
-     relaxation begins.  */
-
-  RELAX_ADD_NOP_IF_A0_B_RETW,
-  /* These are placed in front of conditional branches.  It will be
-     turned into a NOP (using a1) if the branch is immediately
-     followed by a RETW or RETW.N.  Otherwise it will be turned into
-     an rs_fill of 0 before relaxation begins.  */
-
-  RELAX_ADD_NOP_IF_PRE_LOOP_END,
-  /* These are placed after JX instructions.  It will be turned into a
-     NOP if there is one instruction before a loop end label.
-     Otherwise it will be turned into an rs_fill of 0 before
-     relaxation begins.  This is used to avoid a hardware TIE
-     interlock issue prior to T1040.  */
-
-  RELAX_ADD_NOP_IF_SHORT_LOOP,
-  /* These are placed after LOOP instructions.  It will be turned into
-     a NOP when: (1) there are less than 3 instructions in the loop;
-     we place 2 of these in a row to add up to 2 NOPS in short loops;
-     or (2) The instructions in the loop do not include a branch or
-     jump.  Otherwise it will be turned into an rs_fill of 0 before
-     relaxation begins.  This is used to avoid hardware bug
-     PR3830.  */
-
-  RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
-  /* These are placed after LOOP instructions.  It will be turned into
-     a NOP if there are less than 12 bytes to the end of some other
-     loop's end.  Otherwise it will be turned into an rs_fill of 0
-     before relaxation begins.  This is used to avoid hardware bug
-     PR3830.  */
-
-  RELAX_DESIRE_ALIGN,
-  /* The next fragment like its first instruction to NOT cross a
-     4-byte boundary.  */
-
-  RELAX_LOOP_END,
-  /* This will be turned into a NOP or NOP.N if the previous
-     instruction is expanded to negate a loop.  */
-
-  RELAX_LOOP_END_ADD_NOP,
-  /* When the code density option is available, this will generate a
-     NOP.N marked RELAX_NARROW.  Otherwise, it will create an rs_fill
-     fragment with a NOP in it.  */
-
-  RELAX_LITERAL,
-  /* Another fragment could generate an expansion here but has not yet.  */
-
-  RELAX_LITERAL_NR,
-  /* Expansion has been generated by an instruction that generates a
-     literal.  However, the stretch has NOT been reported yet in this
-     fragment.  */
-
-  RELAX_LITERAL_FINAL,
-  /* Expansion has been generated by an instruction that generates a
-     literal.  */
-
-  RELAX_LITERAL_POOL_BEGIN,
-  RELAX_LITERAL_POOL_END,
-  /* Technically these are not relaxations at all, but mark a location
-     to store literals later.  Note that fr_var stores the frchain for
-     BEGIN frags and fr_var stores now_seg for END frags.  */
-
-  RELAX_NARROW,
-  /* The last instruction in this fragment (at->fr_opcode) can be
-     freely replaced with a single wider instruction if a future
-     alignment desires or needs it.  */
-
-  RELAX_IMMED,
-  /* The last instruction in this fragment (at->fr_opcode) contains
-     the value defined by fr_symbol (fr_offset = 0).  If the value
-     does not fit, use the specified expansion.  This is similar to
-     "NARROW", except that these may not be expanded in order to align
-     code.  */
-  
-  RELAX_IMMED_STEP1,
-  /* The last instruction in this fragment (at->fr_opcode) contains a
-     literal.  It has already been expanded at least 1 step.  */
-
-  RELAX_IMMED_STEP2
-  /* The last instruction in this fragment (at->fr_opcode) contains a
-     literal.  It has already been expanded at least 2 steps.  */
+  FALSE,                       /* literal_prefix */
+  FALSE,                       /* schedule */
+#if XSHAL_USE_ABSOLUTE_LITERALS
+  TRUE                         /* absolute_literals */
+#else
+  FALSE                                /* absolute_literals */
+#endif
 };
 
 };
 
-/* This is used as a stopper to bound the number of steps that
-   can be taken.  */
-#define RELAX_IMMED_MAXSTEPS (RELAX_IMMED_STEP2 - RELAX_IMMED)
-
-
-typedef bfd_boolean (*frag_predicate) (const fragS *);
-
 
 /* Directive functions.  */
 
 
 /* Directive functions.  */
 
-static bfd_boolean use_generics
-  PARAMS ((void));
-static bfd_boolean use_longcalls
-  PARAMS ((void));
-static bfd_boolean code_density_available
-  PARAMS ((void));
-static bfd_boolean can_relax
-  PARAMS ((void));
-static void directive_push
-  PARAMS ((directiveE, bfd_boolean, const void *));
-static void directive_pop
-  PARAMS ((directiveE *, bfd_boolean *, const char **,
-          unsigned int *, const void **));
-static void directive_balance
-  PARAMS ((void));
-static bfd_boolean inside_directive
-  PARAMS ((directiveE));
-static void get_directive
-  PARAMS ((directiveE *, bfd_boolean *));
-static void xtensa_begin_directive
-  PARAMS ((int));
-static void xtensa_end_directive
-  PARAMS ((int));
-static void xtensa_literal_prefix
-  PARAMS ((char const *, int));
-static void xtensa_literal_position
-  PARAMS ((int));
-static void xtensa_literal_pseudo
-  PARAMS ((int));
-
-/* Parsing and Idiom Translation Functions.  */
-
-static const char *expression_end
-  PARAMS ((const char *));
-static unsigned tc_get_register
-  PARAMS ((const char *));
-static void expression_maybe_register
-  PARAMS ((xtensa_operand, expressionS *));
-static int tokenize_arguments
-  PARAMS ((char **, char *));
-static bfd_boolean parse_arguments
-  PARAMS ((TInsn *, int, char **));
-static int xg_translate_idioms
-  PARAMS ((char **, int *, char **));
-static int xg_translate_sysreg_op
-  PARAMS ((char **, int *, char **));
-static void xg_reverse_shift_count
-  PARAMS ((char **));
-static int xg_arg_is_constant
-  PARAMS ((char *, offsetT *));
-static void xg_replace_opname
-  PARAMS ((char **, char *));
-static int xg_check_num_args
-  PARAMS ((int *, int, char *, char **));
+static void xtensa_begin_directive (int);
+static void xtensa_end_directive (int);
+static void xtensa_literal_prefix (void);
+static void xtensa_literal_position (int);
+static void xtensa_literal_pseudo (int);
+static void xtensa_frequency_pseudo (int);
+static void xtensa_elf_cons (int);
 
 
-/* Functions for dealing with the Xtensa ISA.  */
+/* Parsing and Idiom Translation.  */
 
 
-static bfd_boolean operand_is_immed
-  PARAMS ((xtensa_operand));
-static bfd_boolean operand_is_pcrel_label
-  PARAMS ((xtensa_operand));
-static int get_relaxable_immed
-  PARAMS ((xtensa_opcode));
-static xtensa_opcode get_opcode_from_buf
-  PARAMS ((const char *));
-static bfd_boolean is_direct_call_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_call_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_entry_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_loop_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_the_loop_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_jx_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_windowed_return_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_conditional_branch_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_branch_or_jump_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_reloc_code_real_type opnum_to_reloc
-  PARAMS ((int));
-static int reloc_to_opnum
-  PARAMS ((bfd_reloc_code_real_type));
-static void xtensa_insnbuf_set_operand
-  PARAMS ((xtensa_insnbuf, xtensa_opcode, xtensa_operand, int32,
-          const char *, unsigned int));
-static uint32 xtensa_insnbuf_get_operand
-  PARAMS ((xtensa_insnbuf, xtensa_opcode, int));
-static void xtensa_insnbuf_set_immediate_field
-  PARAMS ((xtensa_opcode, xtensa_insnbuf, int32, const char *,
-          unsigned int));
-static bfd_boolean is_negatable_branch
-  PARAMS ((TInsn *));
+static bfd_reloc_code_real_type xtensa_elf_suffix (char **, expressionS *);
 
 /* Various Other Internal Functions.  */
 
 
 /* Various Other Internal Functions.  */
 
-static bfd_boolean is_unique_insn_expansion
-  PARAMS ((TransitionRule *));
-static int xg_get_insn_size
-  PARAMS ((TInsn *));
-static int xg_get_build_instr_size
-  PARAMS ((BuildInstr *));
-static bfd_boolean xg_is_narrow_insn
-  PARAMS ((TInsn *));
-static bfd_boolean xg_is_single_relaxable_insn
-  PARAMS ((TInsn *));
-static int xg_get_max_narrow_insn_size
-  PARAMS ((xtensa_opcode));
-static int xg_get_max_insn_widen_size
-  PARAMS ((xtensa_opcode));
-static int xg_get_max_insn_widen_literal_size
-  PARAMS ((xtensa_opcode));
-static bfd_boolean xg_is_relaxable_insn
-  PARAMS ((TInsn *, int));
-static symbolS *get_special_literal_symbol
-  PARAMS ((void));
-static symbolS *get_special_label_symbol
-  PARAMS ((void));
-static bfd_boolean xg_build_to_insn
-  PARAMS ((TInsn *, TInsn *, BuildInstr *));
-static bfd_boolean xg_build_to_stack
-  PARAMS ((IStack *, TInsn *, BuildInstr *));
-static bfd_boolean xg_expand_to_stack
-  PARAMS ((IStack *, TInsn *, int));
-static bfd_boolean xg_expand_narrow
-  PARAMS ((TInsn *, TInsn *));
-static bfd_boolean xg_immeds_fit
-  PARAMS ((const TInsn *));
-static bfd_boolean xg_symbolic_immeds_fit
-  PARAMS ((const TInsn *, segT, fragS *, offsetT, long));
-static bfd_boolean xg_check_operand
-  PARAMS ((int32, xtensa_operand));
-static int is_dnrange
-  PARAMS ((fragS *, symbolS *, long));
-static int xg_assembly_relax
-  PARAMS ((IStack *, TInsn *, segT, fragS *, offsetT, int, long));
-static void xg_force_frag_space
-  PARAMS ((int));
-static void xg_finish_frag
-  PARAMS ((char *, enum xtensa_relax_statesE, int, bfd_boolean));
-static bfd_boolean is_branch_jmp_to_next
-  PARAMS ((TInsn *, fragS *));
-static void xg_add_branch_and_loop_targets
-  PARAMS ((TInsn *));
-static bfd_boolean xg_instruction_matches_rule
-  PARAMS ((TInsn *, TransitionRule *));
-static TransitionRule *xg_instruction_match
-  PARAMS ((TInsn *));
-static bfd_boolean xg_build_token_insn
-  PARAMS ((BuildInstr *, TInsn *, TInsn *));
-static bfd_boolean xg_simplify_insn
-  PARAMS ((TInsn *, TInsn *));
-static bfd_boolean xg_expand_assembly_insn
-  PARAMS ((IStack *, TInsn *));
-static symbolS *xg_assemble_literal
-  PARAMS ((TInsn *));
-static void xg_assemble_literal_space
-  PARAMS ((int));
-static symbolS *xtensa_create_literal_symbol
-  PARAMS ((segT, fragS *));
-static void xtensa_add_literal_sym
-  PARAMS ((symbolS *));
-static void xtensa_add_insn_label
-  PARAMS ((symbolS *));
-static void xtensa_clear_insn_labels
-  PARAMS ((void));
-static bfd_boolean get_is_linkonce_section
-  PARAMS ((bfd *, segT));
-static bfd_boolean xg_emit_insn
-  PARAMS ((TInsn *, bfd_boolean));
-static bfd_boolean xg_emit_insn_to_buf
-  PARAMS ((TInsn *, char *, fragS *, offsetT, bfd_boolean));
-static bfd_boolean xg_add_opcode_fix
-  PARAMS ((xtensa_opcode, int, expressionS *, fragS *, offsetT));
-static void xg_resolve_literals
-  PARAMS ((TInsn *, symbolS *));
-static void xg_resolve_labels
-  PARAMS ((TInsn *, symbolS *));
-static void xg_assemble_tokens
-  PARAMS ((TInsn *));
-static bfd_boolean is_register_writer
-  PARAMS ((const TInsn *, const char *, int));
-static bfd_boolean is_bad_loopend_opcode
-  PARAMS ((const TInsn *));
-static bfd_boolean is_unaligned_label
-  PARAMS ((symbolS *));
-static fragS *next_non_empty_frag
-  PARAMS ((const fragS *));
-static xtensa_opcode next_frag_opcode
-  PARAMS ((const fragS *));
-static void update_next_frag_nop_state
-  PARAMS ((fragS *));
-static bfd_boolean next_frag_is_branch_target
-  PARAMS ((const fragS *));
-static bfd_boolean next_frag_is_loop_target
-  PARAMS ((const fragS *));
-static addressT next_frag_pre_opcode_bytes
-  PARAMS ((const fragS *));
-static bfd_boolean is_next_frag_target
-  PARAMS ((const fragS *, const fragS *));
-static void xtensa_mark_literal_pool_location
-  PARAMS ((void));
-static void xtensa_move_labels
-  PARAMS ((fragS *, valueT, bfd_boolean));
-static void assemble_nop
-  PARAMS ((size_t, char *));
-static addressT get_expanded_loop_offset
-  PARAMS ((xtensa_opcode));
-static fragS *get_literal_pool_location
-  PARAMS ((segT));
-static void set_literal_pool_location
-  PARAMS ((segT, fragS *));
-
-/* Helpers for xtensa_end().  */
-
-static void xtensa_cleanup_align_frags
-  PARAMS ((void));
-static void xtensa_fix_target_frags
-  PARAMS ((void));
-static bfd_boolean frag_can_negate_branch
-  PARAMS ((fragS *));
-static void xtensa_fix_a0_b_retw_frags
-  PARAMS ((void));
-static bfd_boolean next_instrs_are_b_retw
-  PARAMS ((fragS *));
-static void xtensa_fix_b_j_loop_end_frags
-  PARAMS ((void));
-static bfd_boolean next_instr_is_loop_end
-  PARAMS ((fragS *));
-static void xtensa_fix_close_loop_end_frags
-  PARAMS ((void));
-static size_t min_bytes_to_other_loop_end
-  PARAMS ((fragS *, fragS *, offsetT, size_t));
-static size_t unrelaxed_frag_min_size
-  PARAMS ((fragS *));
-static void xtensa_fix_short_loop_frags
-  PARAMS ((void));
-static size_t count_insns_to_loop_end
-  PARAMS ((fragS *, bfd_boolean, size_t));
-static size_t unrelaxed_frag_min_insn_count
-  PARAMS ((fragS *));
-static bfd_boolean branch_before_loop_end
-  PARAMS ((fragS *));
-static bfd_boolean unrelaxed_frag_has_b_j
-  PARAMS ((fragS *));
-static void xtensa_sanity_check
-  PARAMS ((void));
-static bfd_boolean is_empty_loop
-  PARAMS ((const TInsn *, fragS *));
-static bfd_boolean is_local_forward_loop
-  PARAMS ((const TInsn *, fragS *));
+extern bfd_boolean xg_is_single_relaxable_insn (TInsn *, TInsn *, bfd_boolean);
+static bfd_boolean xg_build_to_insn (TInsn *, TInsn *, BuildInstr *);
+static void xtensa_mark_literal_pool_location (void);
+static addressT get_expanded_loop_offset (xtensa_opcode);
+static fragS *get_literal_pool_location (segT);
+static void set_literal_pool_location (segT, fragS *);
+static void xtensa_set_frag_assembly_state (fragS *);
+static void finish_vinsn (vliw_insn *);
+static bfd_boolean emit_single_op (TInsn *);
+static int total_frag_text_expansion (fragS *);
 
 /* Alignment Functions.  */
 
 
 /* Alignment Functions.  */
 
-static size_t get_text_align_power
-  PARAMS ((int));
-static addressT get_text_align_max_fill_size
-  PARAMS ((int, bfd_boolean, bfd_boolean));
-static addressT get_text_align_fill_size
-  PARAMS ((addressT, int, int, bfd_boolean, bfd_boolean));
-static size_t get_text_align_nop_count
-  PARAMS ((size_t, bfd_boolean));
-static size_t get_text_align_nth_nop_size
-  PARAMS ((size_t, size_t, bfd_boolean));
-static addressT get_noop_aligned_address
-  PARAMS ((fragS *, addressT));
-static addressT get_widen_aligned_address
-  PARAMS ((fragS *, addressT));
+static int get_text_align_power (unsigned);
+static int get_text_align_max_fill_size (int, bfd_boolean, bfd_boolean);
+static int branch_align_power (segT);
 
 /* Helpers for xtensa_relax_frag().  */
 
 
 /* Helpers for xtensa_relax_frag().  */
 
-static long relax_frag_text_align
-  PARAMS ((fragS *, long));
-static long relax_frag_add_nop
-  PARAMS ((fragS *));
-static long relax_frag_narrow
-  PARAMS ((fragS *, long));
-static bfd_boolean future_alignment_required
-  PARAMS ((fragS *, long));
-static long relax_frag_immed
-  PARAMS ((segT, fragS *, long, int, int *));
+static long relax_frag_add_nop (fragS *);
 
 
-/* Helpers for md_convert_frag().  */
+/* Accessors for additional per-subsegment information.  */
 
 
-static void convert_frag_align_next_opcode
-  PARAMS ((fragS *));
-static void convert_frag_narrow
-  PARAMS ((fragS *));
-static void convert_frag_immed
-  PARAMS ((segT, fragS *, int));
-static fixS *fix_new_exp_in_seg
-  PARAMS ((segT, subsegT, fragS *, int, int, expressionS *, int,
-          bfd_reloc_code_real_type));
-static void convert_frag_immed_finish_loop
-  PARAMS ((segT, fragS *, TInsn *));
-static offsetT get_expression_value
-  PARAMS ((segT, expressionS *));
+static unsigned get_last_insn_flags (segT, subsegT);
+static void set_last_insn_flags (segT, subsegT, unsigned, bfd_boolean);
+static float get_subseg_total_freq (segT, subsegT);
+static float get_subseg_target_freq (segT, subsegT);
+static void set_subseg_freq (segT, subsegT, float, float);
 
 
-/* Flags for the Last Instruction in Each Subsegment.  */
+/* Segment list functions.  */
 
 
-static unsigned get_last_insn_flags
-  PARAMS ((segT, subsegT));
-static void set_last_insn_flags
-  PARAMS ((segT, subsegT, unsigned, bfd_boolean));
+static void xtensa_move_literals (void);
+static void xtensa_reorder_segments (void);
+static void xtensa_switch_to_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_restore_emit_state (emit_state *);
+static segT cache_literal_section (bfd_boolean);
 
 
-/* Segment list functions.  */
+/* Import from elf32-xtensa.c in BFD library.  */
 
 
-static void xtensa_remove_section
-  PARAMS ((segT));
-static void xtensa_insert_section
-  PARAMS ((segT, segT));
-static void xtensa_move_seg_list_to_beginning
-  PARAMS ((seg_list *));
-static void xtensa_move_literals
-  PARAMS ((void));
-static void mark_literal_frags
-  PARAMS ((seg_list *));
-static void xtensa_reorder_seg_list
-  PARAMS ((seg_list *, segT));
-static void xtensa_reorder_segments
-  PARAMS ((void));
-static segT get_last_sec
-  PARAMS ((void));
-static void xtensa_switch_to_literal_fragment
-  PARAMS ((emit_state *));
-static void xtensa_switch_section_emit_state
-  PARAMS ((emit_state *, segT, subsegT));
-static void xtensa_restore_emit_state
-  PARAMS ((emit_state *));
-static void cache_literal_section
-  PARAMS ((seg_list *, const char *, segT *));
-static segT retrieve_literal_seg
-  PARAMS ((seg_list *, const char *));
-static segT seg_present
-  PARAMS ((const char *));
-static void add_seg_list
-  PARAMS ((seg_list *, segT));
-
-/* Property Table (e.g., ".xt.insn" and ".xt.lit") Functions.  */
+extern asection *xtensa_get_property_section (asection *, const char *);
 
 
-static void xtensa_create_property_segments
-  PARAMS ((frag_predicate, const char *, xt_section_type));
-static segment_info_type *retrieve_segment_info
-  PARAMS ((segT));
-static segT retrieve_xtensa_section
-  PARAMS ((char *));
-static bfd_boolean section_has_property
-  PARAMS ((segT sec, frag_predicate));
-static void add_xt_block_frags
-  PARAMS ((segT, segT, xtensa_block_info **, frag_predicate));
-static bfd_boolean get_frag_is_literal
-  PARAMS ((const fragS *));
-static bfd_boolean get_frag_is_insn
-  PARAMS ((const fragS *));
+/* op_placement_info functions.  */
 
 
-/* Import from elf32-xtensa.c in BFD library.  */
-extern char *xtensa_get_property_section_name
-  PARAMS ((asection *, const char *));
+static void init_op_placement_info_table (void);
+extern bfd_boolean opcode_fits_format_slot (xtensa_opcode, xtensa_format, int);
+static int xg_get_single_size (xtensa_opcode);
+static xtensa_format xg_get_single_format (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
-  PARAMS ((const TInsn *));
-static bfd_boolean tinsn_has_invalid_symbolic_operands
-  PARAMS ((const TInsn *));
-static bfd_boolean tinsn_has_complex_operands
-  PARAMS ((const TInsn *));
-static bfd_boolean tinsn_to_insnbuf
-  PARAMS ((TInsn *, xtensa_insnbuf));
-static bfd_boolean tinsn_check_arguments
-  PARAMS ((const TInsn *));
-static void tinsn_from_chars
-  PARAMS ((TInsn *, char *));
-static void tinsn_immed_from_frag
-  PARAMS ((TInsn *, fragS *));
-static int get_num_stack_text_bytes
-  PARAMS ((IStack *));
-static int get_num_stack_literal_bytes
-  PARAMS ((IStack *));
+
+static bfd_boolean tinsn_has_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_to_insnbuf (TInsn *, xtensa_insnbuf);
+static bfd_boolean tinsn_check_arguments (const TInsn *);
+static void tinsn_from_chars (TInsn *, char *, int);
+static void tinsn_immed_from_frag (TInsn *, fragS *, int);
+static int get_num_stack_text_bytes (IStack *);
+static int get_num_stack_literal_bytes (IStack *);
+
+/* vliw_insn functions.  */
+
+static void xg_init_vinsn (vliw_insn *);
+static void xg_clear_vinsn (vliw_insn *);
+static bfd_boolean vinsn_has_specific_opcodes (vliw_insn *);
+static void xg_free_vinsn (vliw_insn *);
+static bfd_boolean vinsn_to_insnbuf
+  (vliw_insn *, char *, fragS *, bfd_boolean);
+static void vinsn_from_chars (vliw_insn *, char *);
 
 /* Expression Utilities.  */
 
 /* Expression Utilities.  */
-bfd_boolean expr_is_const
-  PARAMS ((const expressionS *));
-offsetT get_expr_const
-  PARAMS ((const expressionS *));
-void set_expr_const
-  PARAMS ((expressionS *, offsetT));
-void set_expr_symbol_offset
-  PARAMS ((expressionS *, symbolS *, offsetT));
-bfd_boolean expr_is_equal
-  PARAMS ((expressionS *, expressionS *));
-static void copy_expr
-  PARAMS ((expressionS *, const expressionS *));
-
-#ifdef XTENSA_SECTION_RENAME
-static void build_section_rename
-  PARAMS ((const char *));
-static void add_section_rename
-  PARAMS ((char *, char *));
-#endif
+
+bfd_boolean expr_is_const (const expressionS *);
+offsetT get_expr_const (const expressionS *);
+void set_expr_const (expressionS *, offsetT);
+bfd_boolean expr_is_register (const expressionS *);
+offsetT get_expr_register (const expressionS *);
+void set_expr_symbol_offset (expressionS *, symbolS *, offsetT);
+bfd_boolean expr_is_equal (expressionS *, expressionS *);
+static void copy_expr (expressionS *, const expressionS *);
+
+/* Section renaming.  */
+
+static void build_section_rename (const char *);
 
 
 /* ISA imported from bfd.  */
 
 
 /* ISA imported from bfd.  */
@@ -739,20 +551,24 @@ static xtensa_opcode xtensa_callx0_opcode;
 static xtensa_opcode xtensa_callx4_opcode;
 static xtensa_opcode xtensa_callx8_opcode;
 static xtensa_opcode xtensa_callx12_opcode;
 static xtensa_opcode xtensa_callx4_opcode;
 static xtensa_opcode xtensa_callx8_opcode;
 static xtensa_opcode xtensa_callx12_opcode;
+static xtensa_opcode xtensa_const16_opcode;
 static xtensa_opcode xtensa_entry_opcode;
 static xtensa_opcode xtensa_entry_opcode;
+static xtensa_opcode xtensa_movi_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_jx_opcode;
 static xtensa_opcode xtensa_jx_opcode;
+static xtensa_opcode xtensa_l32r_opcode;
 static xtensa_opcode xtensa_loop_opcode;
 static xtensa_opcode xtensa_loopnez_opcode;
 static xtensa_opcode xtensa_loopgtz_opcode;
 static xtensa_opcode xtensa_loop_opcode;
 static xtensa_opcode xtensa_loopnez_opcode;
 static xtensa_opcode xtensa_loopgtz_opcode;
+static xtensa_opcode xtensa_nop_opcode;
 static xtensa_opcode xtensa_nop_n_opcode;
 static xtensa_opcode xtensa_or_opcode;
 static xtensa_opcode xtensa_ret_opcode;
 static xtensa_opcode xtensa_ret_n_opcode;
 static xtensa_opcode xtensa_retw_opcode;
 static xtensa_opcode xtensa_retw_n_opcode;
 static xtensa_opcode xtensa_nop_n_opcode;
 static xtensa_opcode xtensa_or_opcode;
 static xtensa_opcode xtensa_ret_opcode;
 static xtensa_opcode xtensa_ret_n_opcode;
 static xtensa_opcode xtensa_retw_opcode;
 static xtensa_opcode xtensa_retw_n_opcode;
-static xtensa_opcode xtensa_rsr_opcode;
+static xtensa_opcode xtensa_rsr_lcount_opcode;
 static xtensa_opcode xtensa_waiti_opcode;
 
 \f
 static xtensa_opcode xtensa_waiti_opcode;
 
 \f
@@ -760,34 +576,43 @@ static xtensa_opcode xtensa_waiti_opcode;
 
 bfd_boolean use_literal_section = TRUE;
 static bfd_boolean align_targets = TRUE;
 
 bfd_boolean use_literal_section = TRUE;
 static bfd_boolean align_targets = TRUE;
-static bfd_boolean align_only_targets = FALSE;
-static bfd_boolean software_a0_b_retw_interlock = TRUE;
+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 = TRUE;
+static bfd_boolean workaround_a0_b_retw = FALSE;
+static bfd_boolean workaround_b_j_loop_end = FALSE;
+static bfd_boolean workaround_short_loop = FALSE;
+static bfd_boolean maybe_has_short_loop = FALSE;
+static bfd_boolean workaround_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 software_avoid_b_j_loop_end = TRUE;
-static bfd_boolean workaround_b_j_loop_end = TRUE;
-static bfd_boolean maybe_has_b_j_loop_end = FALSE;
+/* When workaround_short_loops is TRUE, all loops with early exits must
+   have at least 3 instructions.  workaround_all_short_loops is a modifier
+   to the workaround_short_loop flag.  In addition to the
+   workaround_short_loop actions, all straightline loopgtz and loopnez
+   must have at least 3 instructions.  */
 
 
-static bfd_boolean software_avoid_short_loop = TRUE;
-static bfd_boolean workaround_short_loop = TRUE;
-static bfd_boolean maybe_has_short_loop = FALSE;
+static bfd_boolean workaround_all_short_loops = FALSE;
 
 
-static bfd_boolean software_avoid_close_loop_end = TRUE;
-static bfd_boolean workaround_close_loop_end = TRUE;
-static bfd_boolean maybe_has_close_loop_end = FALSE;
 
 
-/* When avoid_short_loops is true, all loops with early exits must
-   have at least 3 instructions.  avoid_all_short_loops is a modifier
-   to the avoid_short_loop flag.  In addition to the avoid_short_loop
-   actions, all straightline loopgtz and loopnez must have at least 3
-   instructions.  */
+static void
+xtensa_setup_hw_workarounds (int earliest, int latest)
+{
+  if (earliest > latest)
+    as_fatal (_("illegal range of target hardware versions"));
 
 
-static bfd_boolean software_avoid_all_short_loops = TRUE;
-static bfd_boolean workaround_all_short_loops = TRUE;
+  /* Enable all workarounds for pre-T1050.0 hardware.  */
+  if (earliest < 105000 || latest < 105000)
+    {
+      workaround_a0_b_retw |= TRUE;
+      workaround_b_j_loop_end |= TRUE;
+      workaround_short_loop |= TRUE;
+      workaround_close_loop_end |= TRUE;
+      workaround_all_short_loops |= TRUE;
+      enforce_three_byte_loop_align = TRUE;
+    }
+}
 
 
-/* This is on a per-instruction basis.  */
-static bfd_boolean specific_opcode = FALSE;
 
 enum
 {
 
 enum
 {
@@ -797,17 +622,25 @@ enum
   option_relax,
   option_no_relax,
 
   option_relax,
   option_no_relax,
 
+  option_link_relax,
+  option_no_link_relax,
+
   option_generics,
   option_no_generics,
 
   option_generics,
   option_no_generics,
 
+  option_transform,
+  option_no_transform,
+
   option_text_section_literals,
   option_no_text_section_literals,
 
   option_text_section_literals,
   option_no_text_section_literals,
 
+  option_absolute_literals,
+  option_no_absolute_literals,
+
   option_align_targets,
   option_no_align_targets,
 
   option_align_targets,
   option_no_align_targets,
 
-  option_align_only_targets,
-  option_no_align_only_targets,
+  option_warn_unaligned_targets,
 
   option_longcalls,
   option_no_longcalls,
 
   option_longcalls,
   option_no_longcalls,
@@ -829,119 +662,115 @@ enum
 
   option_no_workarounds,
 
 
   option_no_workarounds,
 
-#ifdef XTENSA_SECTION_RENAME
-  option_literal_section_name,
-  option_text_section_name,
-  option_data_section_name,
-  option_bss_section_name,
   option_rename_section_name,
   option_rename_section_name,
-#endif
 
 
-  option_eb,
-  option_el
+  option_prefer_l32r,
+  option_prefer_const16,
+
+  option_target_hardware
 };
 
 const char *md_shortopts = "";
 
 struct option md_longopts[] =
 {
 };
 
 const char *md_shortopts = "";
 
 struct option md_longopts[] =
 {
-  {"density", no_argument, NULL, option_density},
-  {"no-density", no_argument, NULL, option_no_density},
-  /* At least as early as alameda, --[no-]relax didn't work as
-     documented, so as of albany, --[no-]relax is equivalent to
-     --[no-]generics.  Both of these will be deprecated in
-     BearValley.  */
-  {"relax", no_argument, NULL, option_generics},
-  {"no-relax", no_argument, NULL, option_no_generics},
-  {"generics", no_argument, NULL, option_generics},
-  {"no-generics", no_argument, NULL, option_no_generics},
-  {"text-section-literals", no_argument, NULL, option_text_section_literals},
-  {"no-text-section-literals", no_argument, NULL,
-   option_no_text_section_literals},
+  { "density", no_argument, NULL, option_density },
+  { "no-density", no_argument, NULL, option_no_density },
+
+  /* Both "relax" and "generics" are deprecated and treated as equivalent
+     to the "transform" option.  */
+  { "relax", no_argument, NULL, option_relax },
+  { "no-relax", no_argument, NULL, option_no_relax },
+  { "generics", no_argument, NULL, option_generics },
+  { "no-generics", no_argument, NULL, option_no_generics },
+
+  { "transform", no_argument, NULL, option_transform },
+  { "no-transform", no_argument, NULL, option_no_transform },
+  { "text-section-literals", no_argument, NULL, option_text_section_literals },
+  { "no-text-section-literals", no_argument, NULL,
+    option_no_text_section_literals },
+  { "absolute-literals", no_argument, NULL, option_absolute_literals },
+  { "no-absolute-literals", no_argument, NULL, option_no_absolute_literals },
   /* This option was changed from -align-target to -target-align
      because it conflicted with the "-al" option.  */
   /* This option was changed from -align-target to -target-align
      because it conflicted with the "-al" option.  */
-  {"target-align", no_argument, NULL, option_align_targets},
-  {"no-target-align", no_argument, NULL,
-   option_no_align_targets},
-#if 0
-  /* This option  should do a better job aligning targets because
-     it will only attempt to align targets that are the target of a 
-     branch.  */
-   { "target-align-only", no_argument, NULL, option_align_only_targets },
-   { "no-target-align-only", no_argument, NULL, option_no_align_only_targets },
-#endif /* 0 */
-  {"longcalls", no_argument, NULL, option_longcalls},
-  {"no-longcalls", no_argument, NULL, option_no_longcalls},
-
-  {"no-workaround-a0-b-retw", no_argument, NULL,
-   option_no_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,
-   option_no_workaround_b_j_loop_end},
-  {"workaround-b-j-loop-end", no_argument, NULL,
-   option_workaround_b_j_loop_end},
-  
-  {"no-workaround-short-loops", no_argument, NULL,
-   option_no_workaround_short_loop},
-  {"workaround-short-loops", no_argument, NULL, option_workaround_short_loop},
-
-  {"no-workaround-all-short-loops", no_argument, NULL,
-   option_no_workaround_all_short_loops},
-  {"workaround-all-short-loop", no_argument, NULL,
-   option_workaround_all_short_loops},
-
-  {"no-workaround-close-loop-end", no_argument, NULL,
-   option_no_workaround_close_loop_end},
-  {"workaround-close-loop-end", no_argument, NULL,
-   option_workaround_close_loop_end},
-
-  {"no-workarounds", no_argument, NULL, option_no_workarounds},
-
-#ifdef XTENSA_SECTION_RENAME
-  {"literal-section-name", required_argument, NULL,
-   option_literal_section_name},
-  {"text-section-name", required_argument, NULL,
-   option_text_section_name},
-  {"data-section-name", required_argument, NULL,
-   option_data_section_name},
-  {"rename-section", required_argument, NULL,
-   option_rename_section_name},
-  {"bss-section-name", required_argument, NULL,
-   option_bss_section_name},
-#endif /* XTENSA_SECTION_RENAME */
-
-  {NULL, no_argument, NULL, 0}
+  { "target-align", no_argument, NULL, option_align_targets },
+  { "no-target-align", no_argument, NULL, option_no_align_targets },
+  { "warn-unaligned-targets", no_argument, NULL,
+    option_warn_unaligned_targets },
+  { "longcalls", no_argument, NULL, option_longcalls },
+  { "no-longcalls", no_argument, NULL, option_no_longcalls },
+
+  { "no-workaround-a0-b-retw", no_argument, NULL,
+    option_no_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,
+    option_no_workaround_b_j_loop_end },
+  { "workaround-b-j-loop-end", no_argument, NULL,
+    option_workaround_b_j_loop_end },
+
+  { "no-workaround-short-loops", no_argument, NULL,
+    option_no_workaround_short_loop },
+  { "workaround-short-loops", no_argument, NULL,
+    option_workaround_short_loop },
+
+  { "no-workaround-all-short-loops", no_argument, NULL,
+    option_no_workaround_all_short_loops },
+  { "workaround-all-short-loop", no_argument, NULL,
+    option_workaround_all_short_loops },
+
+  { "prefer-l32r", no_argument, NULL, option_prefer_l32r },
+  { "prefer-const16", no_argument, NULL, option_prefer_const16 },
+
+  { "no-workarounds", no_argument, NULL, option_no_workarounds },
+
+  { "no-workaround-close-loop-end", no_argument, NULL,
+    option_no_workaround_close_loop_end },
+  { "workaround-close-loop-end", no_argument, NULL,
+    option_workaround_close_loop_end },
+
+  { "rename-section", required_argument, NULL, option_rename_section_name },
+
+  { "link-relax", no_argument, NULL, option_link_relax },
+  { "no-link-relax", no_argument, NULL, option_no_link_relax },
+
+  { "target-hardware", required_argument, NULL, option_target_hardware },
+
+  { NULL, no_argument, NULL, 0 }
 };
 
 size_t md_longopts_size = sizeof md_longopts;
 
 
 int
 };
 
 size_t md_longopts_size = sizeof md_longopts;
 
 
 int
-md_parse_option (c, arg)
-     int c;
-     char *arg;
+md_parse_option (int c, char *arg)
 {
   switch (c)
     {
     case option_density:
 {
   switch (c)
     {
     case option_density:
-      if (!density_supported)
-       {
-         as_bad (_("'--density' option not supported in this Xtensa "
-                 "configuration"));
-         return 0;
-       }
-      directive_state[directive_density] = TRUE;
+      as_warn (_("--density option is ignored"));
       return 1;
     case option_no_density:
       return 1;
     case option_no_density:
-      directive_state[directive_density] = FALSE;
+      as_warn (_("--no-density option is ignored"));
       return 1;
       return 1;
-    case option_generics:
-      directive_state[directive_generics] = TRUE;
+    case option_link_relax:
+      linkrelax = 1;
       return 1;
       return 1;
-    case option_no_generics:
-      directive_state[directive_generics] = FALSE;
+    case option_no_link_relax:
+      linkrelax = 0;
       return 1;
       return 1;
+    case option_generics:
+      as_warn (_("--generics is deprecated; use --transform instead"));
+      return md_parse_option (option_transform, arg);
+    case option_no_generics:
+      as_warn (_("--no-generics is deprecated; use --no-transform instead"));
+      return md_parse_option (option_no_transform, arg);
+    case option_relax:
+      as_warn (_("--relax is deprecated; use --transform instead"));
+      return md_parse_option (option_transform, arg);
+    case option_no_relax:
+      as_warn (_("--no-relax is deprecated; use --no-transform instead"));
+      return md_parse_option (option_no_transform, arg);
     case option_longcalls:
       directive_state[directive_longcalls] = TRUE;
       return 1;
     case option_longcalls:
       directive_state[directive_longcalls] = TRUE;
       return 1;
@@ -954,63 +783,60 @@ md_parse_option (c, arg)
     case option_no_text_section_literals:
       use_literal_section = TRUE;
       return 1;
     case option_no_text_section_literals:
       use_literal_section = TRUE;
       return 1;
+    case option_absolute_literals:
+      if (!absolute_literals_supported)
+       {
+         as_fatal (_("--absolute-literals option not supported in this Xtensa configuration"));
+         return 0;
+       }
+      directive_state[directive_absolute_literals] = TRUE;
+      return 1;
+    case option_no_absolute_literals:
+      directive_state[directive_absolute_literals] = FALSE;
+      return 1;
+
     case option_workaround_a0_b_retw:
       workaround_a0_b_retw = TRUE;
     case option_workaround_a0_b_retw:
       workaround_a0_b_retw = TRUE;
-      software_a0_b_retw_interlock = TRUE;
       return 1;
     case option_no_workaround_a0_b_retw:
       workaround_a0_b_retw = FALSE;
       return 1;
     case option_no_workaround_a0_b_retw:
       workaround_a0_b_retw = FALSE;
-      software_a0_b_retw_interlock = FALSE;
       return 1;
     case option_workaround_b_j_loop_end:
       workaround_b_j_loop_end = TRUE;
       return 1;
     case option_workaround_b_j_loop_end:
       workaround_b_j_loop_end = TRUE;
-      software_avoid_b_j_loop_end = TRUE;
       return 1;
     case option_no_workaround_b_j_loop_end:
       workaround_b_j_loop_end = FALSE;
       return 1;
     case option_no_workaround_b_j_loop_end:
       workaround_b_j_loop_end = FALSE;
-      software_avoid_b_j_loop_end = FALSE;
       return 1;
 
     case option_workaround_short_loop:
       workaround_short_loop = TRUE;
       return 1;
 
     case option_workaround_short_loop:
       workaround_short_loop = TRUE;
-      software_avoid_short_loop = TRUE;
       return 1;
     case option_no_workaround_short_loop:
       workaround_short_loop = FALSE;
       return 1;
     case option_no_workaround_short_loop:
       workaround_short_loop = FALSE;
-      software_avoid_short_loop = FALSE;
       return 1;
 
     case option_workaround_all_short_loops:
       workaround_all_short_loops = TRUE;
       return 1;
 
     case option_workaround_all_short_loops:
       workaround_all_short_loops = TRUE;
-      software_avoid_all_short_loops = TRUE;
       return 1;
     case option_no_workaround_all_short_loops:
       workaround_all_short_loops = FALSE;
       return 1;
     case option_no_workaround_all_short_loops:
       workaround_all_short_loops = FALSE;
-      software_avoid_all_short_loops = FALSE;
       return 1;
 
     case option_workaround_close_loop_end:
       workaround_close_loop_end = TRUE;
       return 1;
 
     case option_workaround_close_loop_end:
       workaround_close_loop_end = TRUE;
-      software_avoid_close_loop_end = TRUE;
       return 1;
     case option_no_workaround_close_loop_end:
       workaround_close_loop_end = FALSE;
       return 1;
     case option_no_workaround_close_loop_end:
       workaround_close_loop_end = FALSE;
-      software_avoid_close_loop_end = FALSE;
       return 1;
 
     case option_no_workarounds:
       workaround_a0_b_retw = FALSE;
       return 1;
 
     case option_no_workarounds:
       workaround_a0_b_retw = FALSE;
-      software_a0_b_retw_interlock = FALSE;
       workaround_b_j_loop_end = FALSE;
       workaround_b_j_loop_end = FALSE;
-      software_avoid_b_j_loop_end = FALSE;
       workaround_short_loop = FALSE;
       workaround_short_loop = FALSE;
-      software_avoid_short_loop = FALSE;
       workaround_all_short_loops = FALSE;
       workaround_all_short_loops = FALSE;
-      software_avoid_all_short_loops = FALSE;
       workaround_close_loop_end = FALSE;
       workaround_close_loop_end = FALSE;
-      software_avoid_close_loop_end = FALSE;
       return 1;
       return 1;
-      
+
     case option_align_targets:
       align_targets = TRUE;
       return 1;
     case option_align_targets:
       align_targets = TRUE;
       return 1;
@@ -1018,48 +844,69 @@ md_parse_option (c, arg)
       align_targets = FALSE;
       return 1;
 
       align_targets = FALSE;
       return 1;
 
-    case option_align_only_targets:
-      align_only_targets = TRUE;
-      return 1;
-    case option_no_align_only_targets:
-      align_only_targets = FALSE;
+    case option_warn_unaligned_targets:
+      warn_unaligned_branch_targets = TRUE;
       return 1;
 
       return 1;
 
-#ifdef XTENSA_SECTION_RENAME
-    case option_literal_section_name:
-      add_section_rename (".literal", arg);
-      as_warn (_("'--literal-section-name' is deprecated; "
-                "use '--rename-section .literal=NEWNAME'"));
+    case option_rename_section_name:
+      build_section_rename (arg);
       return 1;
 
       return 1;
 
-    case option_text_section_name:
-      add_section_rename (".text", arg);
-      as_warn (_("'--text-section-name' is deprecated; "
-                "use '--rename-section .text=NEWNAME'"));
+    case 'Q':
+      /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+         should be emitted or not.  FIXME: Not implemented.  */
       return 1;
 
       return 1;
 
-    case option_data_section_name:
-      add_section_rename (".data", arg);
-      as_warn (_("'--data-section-name' is deprecated; "
-                "use '--rename-section .data=NEWNAME'"));
+    case option_prefer_l32r:
+      if (prefer_const16)
+       as_fatal (_("prefer-l32r conflicts with prefer-const16"));
+      prefer_l32r = 1;
       return 1;
 
       return 1;
 
-    case option_bss_section_name:
-      add_section_rename (".bss", arg);
-      as_warn (_("'--bss-section-name' is deprecated; "
-                "use '--rename-section .bss=NEWNAME'"));
+    case option_prefer_const16:
+      if (prefer_l32r)
+       as_fatal (_("prefer-const16 conflicts with prefer-l32r"));
+      prefer_const16 = 1;
       return 1;
 
       return 1;
 
-    case option_rename_section_name:
-      build_section_rename (arg);
+    case option_target_hardware:
+      {
+       int earliest, latest = 0;
+       if (*arg == 0 || *arg == '-')
+         as_fatal (_("invalid target hardware version"));
+
+       earliest = strtol (arg, &arg, 0);
+
+       if (*arg == 0)
+         latest = earliest;
+       else if (*arg == '-')
+         {
+           if (*++arg == 0)
+             as_fatal (_("invalid target hardware version"));
+           latest = strtol (arg, &arg, 0);
+         }
+       if (*arg != 0)
+         as_fatal (_("invalid target hardware version"));
+
+       xtensa_setup_hw_workarounds (earliest, latest);
+       return 1;
+      }
+
+    case option_transform:
+      /* This option has no affect other than to use the defaults,
+        which are already set.  */
       return 1;
       return 1;
-#endif /* XTENSA_SECTION_RENAME */
 
 
-    case 'Q':
-      /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
-         should be emitted or not.  FIXME: Not implemented.  */
+    case option_no_transform:
+      /* This option turns off all transformations of any kind.
+        However, because we want to preserve the state of other
+        directives, we only change its own field.  Thus, before
+        you perform any transformation, always check if transform
+        is available.  If you use the functions we provide for this
+        purpose, you will be ok.  */
+      directive_state[directive_transform] = FALSE;
       return 1;
       return 1;
-      
+
     default:
       return 0;
     }
     default:
       return 0;
     }
@@ -1067,28 +914,65 @@ md_parse_option (c, arg)
 
 
 void
 
 
 void
-md_show_usage (stream)
-     FILE *stream;
-{
-  fputs ("\nXtensa options:\n"
-        "--[no-]density          [Do not] emit density instructions\n"
-        "--[no-]relax            [Do not] perform branch relaxation\n"
-        "--[no-]generics         [Do not] transform instructions\n"
-        "--[no-]longcalls        [Do not] emit 32-bit call sequences\n"
-        "--[no-]target-align     [Do not] try to align branch targets\n"
-        "--[no-]text-section-literals\n"
-        "                        [Do not] put literals in the text section\n"
-        "--no-workarounds        Do not use any Xtensa workarounds\n"
-#ifdef XTENSA_SECTION_RENAME
-        "--rename-section old=new(:old1=new1)*\n"
-        "                        Rename section 'old' to 'new'\n"
-        "\nThe following Xtensa options are deprecated\n"
-        "--literal-section-name  Name of literal section (default .literal)\n"
-        "--text-section-name     Name of text section (default .text)\n"
-        "--data-section-name     Name of data section (default .data)\n"
-        "--bss-section-name      Name of bss section (default .bss)\n"
-#endif
-        , stream);
+md_show_usage (FILE *stream)
+{
+  fputs ("\n\
+Xtensa options:\n\
+  --[no-]text-section-literals\n\
+                          [Do not] put literals in the text section\n\
+  --[no-]absolute-literals\n\
+                          [Do not] default to use non-PC-relative literals\n\
+  --[no-]target-align     [Do not] try to align branch targets\n\
+  --[no-]longcalls        [Do not] emit 32-bit call sequences\n\
+  --[no-]transform        [Do not] transform instructions\n\
+  --rename-section old=new Rename section 'old' to 'new'\n", stream);
+}
+
+\f
+/* Functions related to the list of current label symbols.  */
+
+static void
+xtensa_add_insn_label (symbolS *sym)
+{
+  sym_list *l;
+
+  if (!free_insn_labels)
+    l = (sym_list *) xmalloc (sizeof (sym_list));
+  else
+    {
+      l = free_insn_labels;
+      free_insn_labels = l->next;
+    }
+
+  l->sym = sym;
+  l->next = insn_labels;
+  insn_labels = l;
+}
+
+
+static void
+xtensa_clear_insn_labels (void)
+{
+  sym_list **pl;
+
+  for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+    ;
+  *pl = insn_labels;
+  insn_labels = NULL;
+}
+
+
+static void
+xtensa_move_labels (fragS *new_frag, valueT new_offset)
+{
+  sym_list *lit;
+
+  for (lit = insn_labels; lit; lit = lit->next)
+    {
+      symbolS *lit_sym = lit->sym;
+      S_SET_VALUE (lit_sym, new_offset);
+      symbol_set_frag (lit_sym, new_frag);
+    }
 }
 
 \f
 }
 
 \f
@@ -1109,50 +993,43 @@ 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) */
-  {"literal_position", xtensa_literal_position, 0},
-  {"frame", s_ignore, 0},      /* formerly used for STABS debugging */
-  {"word", cons, 4},
-  {"begin", xtensa_begin_directive, 0},
-  {"end", xtensa_end_directive, 0},
-  {"literal", xtensa_literal_pseudo, 0},
-  {NULL, 0, 0},
+  { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0).  */
+  { "literal_position", xtensa_literal_position, 0 },
+  { "frame", s_ignore, 0 },    /* Formerly used for STABS debugging.  */
+  { "long", xtensa_elf_cons, 4 },
+  { "word", xtensa_elf_cons, 4 },
+  { "short", xtensa_elf_cons, 2 },
+  { "begin", xtensa_begin_directive, 0 },
+  { "end", xtensa_end_directive, 0 },
+  { "literal", xtensa_literal_pseudo, 0 },
+  { "frequency", xtensa_frequency_pseudo, 0 },
+  { NULL, 0, 0 },
 };
 
 
 };
 
 
-bfd_boolean
-use_generics ()
-{
-  return directive_state[directive_generics];
-}
-
-
-bfd_boolean
-use_longcalls ()
-{
-  return directive_state[directive_longcalls];
-}
-
-
-bfd_boolean
-code_density_available ()
+static bfd_boolean
+use_transform (void)
 {
 {
-  return directive_state[directive_density];
+  /* After md_end, you should be checking frag by frag, rather
+     than state directives.  */
+  assert (!past_xtensa_end);
+  return directive_state[directive_transform];
 }
 
 
 }
 
 
-bfd_boolean
-can_relax ()
+static bfd_boolean
+do_align_targets (void)
 {
 {
-  return use_generics ();
+  /* Do not use this function after md_end; just look at align_targets
+     instead.  There is no target-align directive, so alignment is either
+     enabled for all frags or not done at all.  */
+  assert (!past_xtensa_end);
+  return align_targets && use_transform ();
 }
 
 
 static void
 }
 
 
 static void
-directive_push (directive, negated, datum)
-     directiveE directive;
-     bfd_boolean negated;
-     const void *datum;
+directive_push (directiveE directive, bfd_boolean negated, const void *datum)
 {
   char *file;
   unsigned int line;
 {
   char *file;
   unsigned int line;
@@ -1172,13 +1049,13 @@ directive_push (directive, negated, datum)
   directive_state[directive] = !negated;
 }
 
   directive_state[directive] = !negated;
 }
 
+
 static void
 static void
-directive_pop (directive, negated, file, line, datum)
-     directiveE *directive;
-     bfd_boolean *negated;
-     const char **file;
-     unsigned int *line;
-     const void **datum;
+directive_pop (directiveE *directive,
+              bfd_boolean *negated,
+              const char **file,
+              unsigned int *line,
+              const void **datum)
 {
   state_stackS *top = directive_state_stack;
 
 {
   state_stackS *top = directive_state_stack;
 
@@ -1201,7 +1078,7 @@ directive_pop (directive, negated, file, line, datum)
 
 
 static void
 
 
 static void
-directive_balance ()
+directive_balance (void)
 {
   while (directive_state_stack)
     {
 {
   while (directive_state_stack)
     {
@@ -1219,8 +1096,7 @@ directive_balance ()
 
 
 static bfd_boolean
 
 
 static bfd_boolean
-inside_directive (dir)
-     directiveE dir;
+inside_directive (directiveE dir)
 {
   state_stackS *top = directive_state_stack;
 
 {
   state_stackS *top = directive_state_stack;
 
@@ -1232,12 +1108,11 @@ inside_directive (dir)
 
 
 static void
 
 
 static void
-get_directive (directive, negated)
-     directiveE *directive;
-     bfd_boolean *negated;
+get_directive (directiveE *directive, bfd_boolean *negated)
 {
   int len;
   unsigned i;
 {
   int len;
   unsigned i;
+  char *directive_string;
 
   if (strncmp (input_line_pointer, "no-", 3) != 0)
     *negated = FALSE;
 
   if (strncmp (input_line_pointer, "no-", 3) != 0)
     *negated = FALSE;
@@ -1248,16 +1123,33 @@ get_directive (directive, negated)
     }
 
   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
+     equivalent to .begin [no-]transform.  We should remove it when
+     we stop accepting those options.  */
+
+  if (strncmp (input_line_pointer, "generics", strlen ("generics")) == 0)
+    {
+      as_warn (_("[no-]generics is deprecated; use [no-]transform instead"));
+      directive_string = "transform";
+    }
+  else if (strncmp (input_line_pointer, "relax", strlen ("relax")) == 0)
+    {
+      as_warn (_("[no-]relax is deprecated; use [no-]transform instead"));
+      directive_string = "transform";
+    }
+  else
+    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 (input_line_pointer, directive_info[i].name, len) == 0)
+      if (strncmp (directive_string, directive_info[i].name, len) == 0)
        {
          input_line_pointer += len;
          *directive = (directiveE) i;
          if (*negated && !directive_info[i].can_be_negated)
        {
          input_line_pointer += len;
          *directive = (directiveE) i;
          if (*negated && !directive_info[i].can_be_negated)
-           as_bad (_("directive %s can't be negated"),
+           as_bad (_("directive %s cannot be negated"),
                    directive_info[i].name);
          return;
        }
                    directive_info[i].name);
          return;
        }
@@ -1269,17 +1161,13 @@ get_directive (directive, negated)
 
 
 static void
 
 
 static void
-xtensa_begin_directive (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+xtensa_begin_directive (int ignore ATTRIBUTE_UNUSED)
 {
   directiveE directive;
   bfd_boolean negated;
   emit_state *state;
 {
   directiveE directive;
   bfd_boolean negated;
   emit_state *state;
-  int len;
   lit_state *ls;
 
   lit_state *ls;
 
-  md_flush_pending_output ();
-
   get_directive (&directive, &negated);
   if (directive == (directiveE) XTENSA_UNDEFINED)
     {
   get_directive (&directive, &negated);
   if (directive == (directiveE) XTENSA_UNDEFINED)
     {
@@ -1287,6 +1175,9 @@ xtensa_begin_directive (ignore)
       return;
     }
 
       return;
     }
 
+  if (cur_vinsn.inside_bundle)
+    as_bad (_("directives are not valid inside bundles"));
+
   switch (directive)
     {
     case directive_literal:
   switch (directive)
     {
     case directive_literal:
@@ -1297,15 +1188,19 @@ xtensa_begin_directive (ignore)
          saved_insn_labels = insn_labels;
          insn_labels = NULL;
        }
          saved_insn_labels = insn_labels;
          insn_labels = NULL;
        }
+      as_warn (_(".begin literal is deprecated; use .literal instead"));
       state = (emit_state *) xmalloc (sizeof (emit_state));
       xtensa_switch_to_literal_fragment (state);
       directive_push (directive_literal, negated, state);
       break;
 
     case directive_literal_prefix:
       state = (emit_state *) xmalloc (sizeof (emit_state));
       xtensa_switch_to_literal_fragment (state);
       directive_push (directive_literal, negated, state);
       break;
 
     case directive_literal_prefix:
+      /* Have to flush pending output because a movi relaxed to an l32r
+        might produce a literal.  */
+      md_flush_pending_output ();
       /* Check to see if the current fragment is a literal
         fragment.  If it is, then this operation is not allowed.  */
       /* Check to see if the current fragment is a literal
         fragment.  If it is, then this operation is not allowed.  */
-      if (frag_now->tc_frag_data.is_literal)
+      if (generating_literals)
        {
          as_bad (_("cannot set literal_prefix inside literal fragment"));
          return;
        {
          as_bad (_("cannot set literal_prefix inside literal fragment"));
          return;
@@ -1317,20 +1212,10 @@ xtensa_begin_directive (ignore)
       assert (ls);
 
       *ls = default_lit_sections;
       assert (ls);
 
       *ls = default_lit_sections;
-
       directive_push (directive_literal_prefix, negated, ls);
 
       directive_push (directive_literal_prefix, negated, ls);
 
-      /* Parse the new prefix from the input_line_pointer.  */
-      SKIP_WHITESPACE ();
-      len = strspn (input_line_pointer,
-                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                   "abcdefghijklmnopqrstuvwxyz_/0123456789.$");
-
       /* Process the new prefix.  */
       /* Process the new prefix.  */
-      xtensa_literal_prefix (input_line_pointer, len);
-
-      /* Skip the name in the input line.  */
-      input_line_pointer += len;
+      xtensa_literal_prefix ();
       break;
 
     case directive_freeregs:
       break;
 
     case directive_freeregs:
@@ -1341,15 +1226,32 @@ xtensa_begin_directive (ignore)
       directive_push (directive_freeregs, negated, 0);
       break;
 
       directive_push (directive_freeregs, negated, 0);
       break;
 
+    case directive_schedule:
+      md_flush_pending_output ();
+      frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
+               frag_now->fr_symbol, frag_now->fr_offset, NULL);
+      directive_push (directive_schedule, negated, 0);
+      xtensa_set_frag_assembly_state (frag_now);
+      break;
+
     case directive_density:
     case directive_density:
-      if (!density_supported && !negated)
+      as_warn (_(".begin [no-]density is ignored"));
+      break;
+
+    case directive_absolute_literals:
+      md_flush_pending_output ();
+      if (!absolute_literals_supported && !negated)
        {
        {
-         as_warn (_("Xtensa density option not supported; ignored"));
+         as_warn (_("Xtensa absolute literals option not supported; ignored"));
          break;
        }
          break;
        }
-      /* fall through */
+      xtensa_set_frag_assembly_state (frag_now);
+      directive_push (directive, negated, 0);
+      break;
 
     default:
 
     default:
+      md_flush_pending_output ();
+      xtensa_set_frag_assembly_state (frag_now);
       directive_push (directive, negated, 0);
       break;
     }
       directive_push (directive, negated, 0);
       break;
     }
@@ -1359,34 +1261,50 @@ xtensa_begin_directive (ignore)
 
 
 static void
 
 
 static void
-xtensa_end_directive (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+xtensa_end_directive (int ignore ATTRIBUTE_UNUSED)
 {
   directiveE begin_directive, end_directive;
   bfd_boolean begin_negated, end_negated;
   const char *file;
   unsigned int line;
   emit_state *state;
 {
   directiveE begin_directive, end_directive;
   bfd_boolean begin_negated, end_negated;
   const char *file;
   unsigned int line;
   emit_state *state;
+  emit_state **state_ptr;
   lit_state *s;
 
   lit_state *s;
 
-  md_flush_pending_output ();
+  if (cur_vinsn.inside_bundle)
+    as_bad (_("directives are not valid inside bundles"));
 
   get_directive (&end_directive, &end_negated);
 
   get_directive (&end_directive, &end_negated);
-  if (end_directive == (directiveE) XTENSA_UNDEFINED)
+
+  md_flush_pending_output ();
+
+  switch (end_directive)
     {
     {
+    case (directiveE) XTENSA_UNDEFINED:
       discard_rest_of_line ();
       return;
       discard_rest_of_line ();
       return;
-    }
 
 
-  if (end_directive == directive_density && !density_supported && !end_negated)
-    {
-      as_warn (_("Xtensa density option not supported; ignored"));
+    case directive_density:
+      as_warn (_(".end [no-]density is ignored"));
       demand_empty_rest_of_line ();
       demand_empty_rest_of_line ();
-      return;
+      break;
+
+    case directive_absolute_literals:
+      if (!absolute_literals_supported && !end_negated)
+       {
+         as_warn (_("Xtensa absolute literals option not supported; ignored"));
+         demand_empty_rest_of_line ();
+         return;
+       }
+      break;
+
+    default:
+      break;
     }
 
     }
 
+  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);
+                (const void **) state_ptr);
 
   if (begin_directive != directive_none)
     {
 
   if (begin_directive != directive_none)
     {
@@ -1403,6 +1321,7 @@ xtensa_end_directive (ignore)
            case directive_literal:
              frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL);
              xtensa_restore_emit_state (state);
            case directive_literal:
              frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL);
              xtensa_restore_emit_state (state);
+             xtensa_set_frag_assembly_state (frag_now);
              free (state);
              if (!inside_directive (directive_literal))
                {
              free (state);
              if (!inside_directive (directive_literal))
                {
@@ -1412,22 +1331,23 @@ xtensa_end_directive (ignore)
                }
              break;
 
                }
              break;
 
-           case directive_freeregs:
-             break;
-
            case directive_literal_prefix:
              /* Restore the default collection sections from saved state.  */
              s = (lit_state *) state;
              assert (s);
            case directive_literal_prefix:
              /* Restore the default collection sections from saved state.  */
              s = (lit_state *) state;
              assert (s);
+             default_lit_sections = *s;
 
 
-             if (use_literal_section)
-               default_lit_sections = *s;
-
-             /* free the state storage */
+             /* Free the state storage.  */
+             free (s->lit_prefix);
              free (s);
              break;
 
              free (s);
              break;
 
+           case directive_schedule:
+           case directive_freeregs:
+             break;
+
            default:
            default:
+             xtensa_set_frag_assembly_state (frag_now);
              break;
            }
        }
              break;
            }
        }
@@ -1440,29 +1360,27 @@ xtensa_end_directive (ignore)
 /* Place an aligned literal fragment at the current location.  */
 
 static void
 /* Place an aligned literal fragment at the current location.  */
 
 static void
-xtensa_literal_position (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+xtensa_literal_position (int ignore ATTRIBUTE_UNUSED)
 {
 {
+  md_flush_pending_output ();
+
   if (inside_directive (directive_literal))
     as_warn (_(".literal_position inside literal directive; ignoring"));
   if (inside_directive (directive_literal))
     as_warn (_(".literal_position inside literal directive; ignoring"));
-  else if (!use_literal_section)
-    xtensa_mark_literal_pool_location ();
+  xtensa_mark_literal_pool_location ();
 
   demand_empty_rest_of_line ();
   xtensa_clear_insn_labels ();
 }
 
 
 
   demand_empty_rest_of_line ();
   xtensa_clear_insn_labels ();
 }
 
 
-/* Support .literal label, value@plt + offset.  */
+/* Support .literal label, expr, ...  */
 
 static void
 
 static void
-xtensa_literal_pseudo (ignored)
-     int ignored ATTRIBUTE_UNUSED;
+xtensa_literal_pseudo (int ignored ATTRIBUTE_UNUSED)
 {
   emit_state state;
   char *p, *base_name;
   char c;
 {
   emit_state state;
   char *p, *base_name;
   char c;
-  expressionS expP;
   segT dest_seg;
 
   if (inside_directive (directive_literal))
   segT dest_seg;
 
   if (inside_directive (directive_literal))
@@ -1472,6 +1390,8 @@ xtensa_literal_pseudo (ignored)
       return;
     }
 
       return;
     }
 
+  md_flush_pending_output ();
+
   /* Previous labels go with whatever follows this directive, not with
      the literal, so save them now.  */
   saved_insn_labels = insn_labels;
   /* Previous labels go with whatever follows this directive, not with
      the literal, so save them now.  */
   saved_insn_labels = insn_labels;
@@ -1484,14 +1404,14 @@ xtensa_literal_pseudo (ignored)
 
   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)
+  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
-     which is handled by switch to literal fragment.  */
-  /* frag_align (2, 0, 0);  */
+  /* All literals are aligned to four-byte boundaries.  */
+  frag_align (2, 0, 0);
+  record_alignment (now_seg, 2);
 
   c = get_symbol_end ();
   /* Just after name is now '\0'.  */
 
   c = get_symbol_end ();
   /* Just after name is now '\0'.  */
@@ -1511,20 +1431,10 @@ xtensa_literal_pseudo (ignored)
 
   colon (base_name);
 
 
   colon (base_name);
 
-  do 
-    {
-      input_line_pointer++;            /* skip ',' or ':' */
-      
-      expr (0, &expP);
-
-      /* We only support 4-byte literals with .literal.  */
-      emit_expr (&expP, 4);
-    }
-  while (*input_line_pointer == ',');
-
   *p = c;
   *p = c;
+  input_line_pointer++;                /* skip ',' or ':' */
 
 
-  demand_empty_rest_of_line ();
+  xtensa_elf_cons (4);
 
   xtensa_restore_emit_state (&state);
 
 
   xtensa_restore_emit_state (&state);
 
@@ -1535,65 +1445,237 @@ xtensa_literal_pseudo (ignored)
 
 
 static void
 
 
 static void
-xtensa_literal_prefix (start, len)
-     char const *start;
-     int len;
+xtensa_literal_prefix (void)
 {
 {
-  segT s_now;                  /* Storage for the current seg and subseg.  */
-  subsegT ss_now;
-  char *name;                  /* Pointer to the name itself.  */
-  char *newname;
-
-  if (!use_literal_section)
-    return;
+  char *name;
+  int len;
 
 
-  /* Store away the current section and subsection.  */
-  s_now = now_seg;
-  ss_now = now_subseg;
+  /* Parse the new prefix from the input_line_pointer.  */
+  SKIP_WHITESPACE ();
+  len = strspn (input_line_pointer,
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+               "abcdefghijklmnopqrstuvwxyz_/0123456789.$");
 
   /* Get a null-terminated copy of the name.  */
   name = xmalloc (len + 1);
   assert (name);
 
   /* Get a null-terminated copy of the name.  */
   name = xmalloc (len + 1);
   assert (name);
-
-  strncpy (name, start, len);
+  strncpy (name, input_line_pointer, len);
   name[len] = 0;
 
   name[len] = 0;
 
-  /* Allocate the sections (interesting note: the memory pointing to
-     the name is actually used for the name by the new section). */
-  newname = xmalloc (len + strlen (".literal") + 1);
-  strcpy (newname, name);
-  strcpy (newname + len, ".literal");
+  /* Skip the name in the input line.  */
+  input_line_pointer += len;
 
 
-  /* Note that retrieve_literal_seg does not create a segment if 
-     it already exists.  */
-  default_lit_sections.lit_seg = NULL; /* retrieved on demand */
+  default_lit_sections.lit_prefix = name;
 
 
-  /* Canonicalizing section names allows renaming literal
-     sections to occur correctly.  */
-  default_lit_sections.lit_seg_name =
-    tc_canonicalize_symbol_name (newname);
+  /* Clear cached literal sections, since the prefix has changed.  */
+  default_lit_sections.lit_seg = NULL;
+  default_lit_sections.lit4_seg = NULL;
+}
 
 
-  free (name);
 
 
-  /* Restore the current section and subsection and set the 
-     generation into the old segment.  */
-  subseg_set (s_now, ss_now);
-}
+/* Support ".frequency branch_target_frequency fall_through_frequency".  */
 
 
-\f
+static void
+xtensa_frequency_pseudo (int ignored ATTRIBUTE_UNUSED)
+{
+  float fall_through_f, target_f;
+
+  fall_through_f = (float) strtod (input_line_pointer, &input_line_pointer);
+  if (fall_through_f < 0)
+    {
+      as_bad (_("fall through frequency must be greater than 0"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  target_f = (float) strtod (input_line_pointer, &input_line_pointer);
+  if (target_f < 0)
+    {
+      as_bad (_("branch target frequency must be greater than 0"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  set_subseg_freq (now_seg, now_subseg, target_f + fall_through_f, target_f);
+
+  demand_empty_rest_of_line ();
+}
+
+
+/* Like normal .long/.short/.word, except support @plt, etc.
+   Clobbers input_line_pointer, checks end-of-line.  */
+
+static void
+xtensa_elf_cons (int nbytes)
+{
+  expressionS exp;
+  bfd_reloc_code_real_type reloc;
+
+  md_flush_pending_output ();
+
+  if (cur_vinsn.inside_bundle)
+    as_bad (_("directives are not valid inside bundles"));
+
+  if (is_it_end_of_statement ())
+    {
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  do
+    {
+      expression (&exp);
+      if (exp.X_op == O_symbol
+         && *input_line_pointer == '@'
+         && ((reloc = xtensa_elf_suffix (&input_line_pointer, &exp))
+             != BFD_RELOC_NONE))
+       {
+         reloc_howto_type *reloc_howto =
+           bfd_reloc_type_lookup (stdoutput, reloc);
+
+         if (reloc == BFD_RELOC_UNUSED || !reloc_howto)
+           as_bad (_("unsupported relocation"));
+         else if ((reloc >= BFD_RELOC_XTENSA_SLOT0_OP
+                   && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
+                  || (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
+                      && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT))
+           as_bad (_("opcode-specific %s relocation used outside "
+                     "an instruction"), reloc_howto->name);
+         else if (nbytes != (int) bfd_get_reloc_size (reloc_howto))
+           as_bad (_("%s relocations do not fit in %d bytes"),
+                   reloc_howto->name, nbytes);
+         else
+           {
+             char *p = frag_more ((int) nbytes);
+             xtensa_set_frag_assembly_state (frag_now);
+             fix_new_exp (frag_now, p - frag_now->fr_literal,
+                          nbytes, &exp, 0, reloc);
+           }
+       }
+      else
+       emit_expr (&exp, (unsigned int) nbytes);
+    }
+  while (*input_line_pointer++ == ',');
+
+  input_line_pointer--;                /* Put terminator back into stream.  */
+  demand_empty_rest_of_line ();
+}
+
+\f
 /* Parsing and Idiom Translation.  */
 
 /* Parsing and Idiom Translation.  */
 
+/* Parse @plt, etc. and return the desired relocation.  */
+static bfd_reloc_code_real_type
+xtensa_elf_suffix (char **str_p, expressionS *exp_p)
+{
+  char ident[20];
+  char *str = *str_p;
+  char *str2;
+  int ch;
+  int len;
+  struct suffix_reloc_map *ptr;
+
+  if (*str++ != '@')
+    return BFD_RELOC_NONE;
+
+  for (ch = *str, str2 = ident;
+       (str2 < ident + sizeof (ident) - 1
+       && (ISALNUM (ch) || ch == '@'));
+       ch = *++str)
+    {
+      *str2++ = (ISLOWER (ch)) ? ch : TOLOWER (ch);
+    }
+
+  *str2 = '\0';
+  len = str2 - ident;
+
+  ch = ident[0];
+  for (ptr = &suffix_relocs[0]; ptr->length > 0; ptr++)
+    if (ch == ptr->suffix[0]
+       && len == ptr->length
+       && memcmp (ident, ptr->suffix, ptr->length) == 0)
+      {
+       /* Now check for "identifier@suffix+constant".  */
+       if (*str == '-' || *str == '+')
+         {
+           char *orig_line = input_line_pointer;
+           expressionS new_exp;
+
+           input_line_pointer = str;
+           expression (&new_exp);
+           if (new_exp.X_op == O_constant)
+             {
+               exp_p->X_add_number += new_exp.X_add_number;
+               str = input_line_pointer;
+             }
+
+           if (&input_line_pointer != str_p)
+             input_line_pointer = orig_line;
+         }
+
+       *str_p = str;
+       return ptr->reloc;
+      }
+
+  return BFD_RELOC_UNUSED;
+}
+
+
+/* Find the matching operator type.  */
+static unsigned char
+map_suffix_reloc_to_operator (bfd_reloc_code_real_type reloc)
+{
+  struct suffix_reloc_map *sfx;
+  unsigned char operator = (unsigned char) -1;
+  
+  for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
+    {
+      if (sfx->reloc == reloc)
+       {
+         operator = sfx->operator;
+         break;
+       }
+    }
+  assert (operator != (unsigned char) -1);
+  return operator;
+}
+
+
+/* Find the matching reloc type.  */
+static bfd_reloc_code_real_type
+map_operator_to_reloc (unsigned char operator)
+{
+  struct suffix_reloc_map *sfx;
+  bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
+
+  for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
+    {
+      if (sfx->operator == operator)
+       {
+         reloc = sfx->reloc;
+         break;
+       }
+    }
+
+  if (reloc == BFD_RELOC_UNUSED)
+    return BFD_RELOC_32;
+
+  return reloc;
+}
+
+
 static const char *
 static const char *
-expression_end (name)
-     const char *name;
+expression_end (const char *name)
 {
   while (1)
     {
       switch (*name)
        {
 {
   while (1)
     {
       switch (*name)
        {
+       case '}':
        case ';':
        case '\0':
        case ',':
        case ';':
        case '\0':
        case ',':
+       case ':':
          return name;
        case ' ':
        case '\t':
          return name;
        case ' ':
        case '\t':
@@ -1609,8 +1691,7 @@ expression_end (name)
 #define ERROR_REG_NUM ((unsigned) -1)
 
 static unsigned
 #define ERROR_REG_NUM ((unsigned) -1)
 
 static unsigned
-tc_get_register (prefix)
-     const char *prefix;
+tc_get_register (const char *prefix)
 {
   unsigned reg;
   const char *next_expr;
 {
   unsigned reg;
   const char *next_expr;
@@ -1664,45 +1745,62 @@ tc_get_register (prefix)
 }
 
 
 }
 
 
-#define PLT_SUFFIX "@PLT"
-#define plt_suffix "@plt"
-
 static void
 static void
-expression_maybe_register (opnd, tok)
-     xtensa_operand opnd;
-     expressionS *tok;
+expression_maybe_register (xtensa_opcode opc, int opnd, expressionS *tok)
 {
 {
-  char *kind = xtensa_operand_kind (opnd);
+  xtensa_isa isa = xtensa_default_isa;
 
 
-  if ((strlen (kind) == 1)
-      && (*kind == 'l' || *kind == 'L' || *kind == 'i' || *kind == 'r'))
+  /* Check if this is an immediate operand.  */
+  if (xtensa_operand_is_register (isa, opc, opnd) == 0)
     {
     {
+      bfd_reloc_code_real_type reloc;
       segT t = expression (tok);
       segT t = expression (tok);
-      if (t == absolute_section && operand_is_pcrel_label (opnd))
+      if (t == absolute_section
+         && xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
        {
          assert (tok->X_op == O_constant);
          tok->X_op = O_symbol;
          tok->X_add_symbol = &abs_symbol;
        }
        {
          assert (tok->X_op == O_constant);
          tok->X_op = O_symbol;
          tok->X_add_symbol = &abs_symbol;
        }
-      if (tok->X_op == O_symbol 
-         && (!strncmp (input_line_pointer, PLT_SUFFIX,
-                       strlen (PLT_SUFFIX) - 1)
-             || !strncmp (input_line_pointer, plt_suffix,
-                          strlen (plt_suffix) - 1)))
+
+      if ((tok->X_op == O_constant || tok->X_op == O_symbol)
+         && ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
+             != BFD_RELOC_NONE))
        {
        {
-         symbol_get_tc (tok->X_add_symbol)->plt = 1;
-         input_line_pointer += strlen (plt_suffix);
+         if (reloc == BFD_RELOC_UNUSED)
+           {
+             as_bad (_("unsupported relocation"));
+             return;
+           }
+
+         if (tok->X_op == O_constant)
+           {
+             switch (reloc)
+               {
+               case BFD_RELOC_LO16:
+                 tok->X_add_number &= 0xffff;
+                 return;
+
+               case BFD_RELOC_HI16:
+                 tok->X_add_number = ((unsigned) tok->X_add_number) >> 16;
+                 return;
+
+               default:
+                 break;
+               }
+           }
+         tok->X_op = map_suffix_reloc_to_operator (reloc);
        }
     }
   else
     {
        }
     }
   else
     {
-      unsigned reg = tc_get_register (kind);
+      xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
+      unsigned reg = tc_get_register (xtensa_regfile_shortname (isa, opnd_rf));
 
       if (reg != ERROR_REG_NUM)        /* Already errored */
        {
          uint32 buf = reg;
 
       if (reg != ERROR_REG_NUM)        /* Already errored */
        {
          uint32 buf = reg;
-         if ((xtensa_operand_encode (opnd, &buf) != xtensa_encode_result_ok)
-             || (reg != xtensa_operand_decode (opnd, buf)))
+         if (xtensa_operand_encode (isa, opc, opnd, &buf))
            as_bad (_("register number out of range"));
        }
 
            as_bad (_("register number out of range"));
        }
 
@@ -1716,18 +1814,17 @@ expression_maybe_register (opnd, tok)
 /* Split up the arguments for an opcode or pseudo-op.  */
 
 static int
 /* Split up the arguments for an opcode or pseudo-op.  */
 
 static int
-tokenize_arguments (args, str)
-     char **args;
-     char *str;
+tokenize_arguments (char **args, char *str)
 {
   char *old_input_line_pointer;
   bfd_boolean saw_comma = FALSE;
   bfd_boolean saw_arg = FALSE;
 {
   char *old_input_line_pointer;
   bfd_boolean saw_comma = FALSE;
   bfd_boolean saw_arg = FALSE;
+  bfd_boolean saw_colon = FALSE;
   int num_args = 0;
   char *arg_end, *arg;
   int arg_len;
   int num_args = 0;
   char *arg_end, *arg;
   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;
   input_line_pointer = str;
 
   old_input_line_pointer = input_line_pointer;
   input_line_pointer = str;
 
@@ -1737,110 +1834,239 @@ tokenize_arguments (args, str)
       switch (*input_line_pointer)
        {
        case '\0':
       switch (*input_line_pointer)
        {
        case '\0':
+       case '}':
          goto fini;
 
          goto fini;
 
+       case ':':
+         input_line_pointer++;
+         if (saw_comma || saw_colon || !saw_arg)
+           goto err;
+         saw_colon = TRUE;
+         break;
+
        case ',':
          input_line_pointer++;
        case ',':
          input_line_pointer++;
-         if (saw_comma || !saw_arg)
+         if (saw_comma || saw_colon || !saw_arg)
            goto err;
          saw_comma = TRUE;
          break;
 
        default:
            goto err;
          saw_comma = TRUE;
          break;
 
        default:
-         if (!saw_comma && saw_arg)
+         if (!saw_comma && !saw_colon && saw_arg)
            goto err;
 
          arg_end = input_line_pointer + 1;
          while (!expression_end (arg_end))
            arg_end += 1;
            goto err;
 
          arg_end = input_line_pointer + 1;
          while (!expression_end (arg_end))
            arg_end += 1;
+
          arg_len = arg_end - input_line_pointer;
          arg_len = arg_end - input_line_pointer;
-         arg = (char *) xmalloc (arg_len + 1);
+         arg = (char *) xmalloc ((saw_colon ? 1 : 0) + arg_len + 1);
          args[num_args] = arg;
 
          args[num_args] = arg;
 
+         if (saw_colon)
+           *arg++ = ':';
          strncpy (arg, input_line_pointer, arg_len);
          arg[arg_len] = '\0';
          strncpy (arg, input_line_pointer, arg_len);
          arg[arg_len] = '\0';
+
          input_line_pointer = arg_end;
          num_args += 1;
          input_line_pointer = arg_end;
          num_args += 1;
-         saw_comma = FALSE; 
-         saw_arg = TRUE; 
+         saw_comma = FALSE;
+         saw_colon = FALSE;
+         saw_arg = TRUE;
          break;
        }
     }
 
 fini:
          break;
        }
     }
 
 fini:
-  if (saw_comma)
+  if (saw_comma || saw_colon)
     goto err;
   input_line_pointer = old_input_line_pointer;
   return num_args;
 
 err:
     goto err;
   input_line_pointer = old_input_line_pointer;
   return num_args;
 
 err:
+  if (saw_comma)
+    as_bad (_("extra comma"));
+  else if (saw_colon)
+    as_bad (_("extra colon"));
+  else if (!saw_arg)
+    as_bad (_("missing argument"));
+  else
+    as_bad (_("missing comma or colon"));
   input_line_pointer = old_input_line_pointer;
   return -1;
 }
 
 
   input_line_pointer = old_input_line_pointer;
   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 (insn, num_args, arg_strings)
-     TInsn *insn;
-     int num_args;
-     char **arg_strings;
+parse_arguments (TInsn *insn, int num_args, char **arg_strings)
 {
 {
-  expressionS *tok = insn->tok;
+  expressionS *tok, *last_tok;
   xtensa_opcode opcode = insn->opcode;
   bfd_boolean had_error = TRUE;
   xtensa_opcode opcode = insn->opcode;
   bfd_boolean had_error = TRUE;
-  xtensa_isa isa = xtensa_default_isa; 
-  int n;
+  xtensa_isa isa = xtensa_default_isa;
+  int n, num_regs = 0;
   int opcode_operand_count;
   int opcode_operand_count;
-  int actual_operand_count = 0;
-  xtensa_operand opnd = NULL; 
+  int opnd_cnt, last_opnd_cnt;
+  unsigned int next_reg = 0;
   char *old_input_line_pointer;
 
   if (insn->insn_type == ITYPE_LITERAL)
     opcode_operand_count = 1;
   else
   char *old_input_line_pointer;
 
   if (insn->insn_type == ITYPE_LITERAL)
     opcode_operand_count = 1;
   else
-    opcode_operand_count = xtensa_num_operands (isa, opcode);
+    opcode_operand_count = xtensa_opcode_num_operands (isa, opcode);
 
 
+  tok = insn->tok;
   memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
 
   /* Save and restore input_line_pointer around this function.  */
   memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
 
   /* 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_opnd_cnt = -1;
+  opnd_cnt = 0;
+
+  /* Skip invisible operands.  */
+  while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0)
+    {
+      opnd_cnt += 1;
+      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 == ':')
+       {
+         xtensa_regfile opnd_rf;
+         input_line_pointer++;
+         if (num_regs == 0)
+           goto err;
+         assert (opnd_cnt > 0);
+         num_regs--;
+         opnd_rf = xtensa_operand_regfile (isa, opcode, last_opnd_cnt);
+         if (next_reg
+             != tc_get_register (xtensa_regfile_shortname (isa, opnd_rf)))
+           as_warn (_("incorrect register number, ignoring"));
+         next_reg++;
+       }
+      else
+       {
+         if (opnd_cnt >= opcode_operand_count)
+           {
+             as_warn (_("too many arguments"));
+             goto err;
+           }
+         assert (opnd_cnt < MAX_INSN_ARGS);
 
 
-      if (actual_operand_count >= opcode_operand_count)
-       { 
-         as_warn (_("too many arguments")); 
-         goto err;
-       } 
-      assert (actual_operand_count < MAX_INSN_ARGS);
+         expression_maybe_register (opcode, opnd_cnt, tok);
+         next_reg = tok->X_add_number + 1;
 
 
-      opnd = xtensa_get_operand (isa, opcode, actual_operand_count); 
-      expression_maybe_register (opnd, tok);
+         if (tok->X_op == O_illegal || tok->X_op == O_absent)
+           goto err;
+         if (xtensa_operand_is_register (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 */
+           }
+         else
+           num_regs = 0;
+
+         last_tok = tok;
+         last_opnd_cnt = opnd_cnt;
+
+         do
+           {
+             opnd_cnt += 1;
+             tok++;
+           }
+         while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0);
+       }
+    }
 
 
-      if (tok->X_op == O_illegal || tok->X_op == O_absent) 
-       goto err; 
-      actual_operand_count++;
-      tok++; 
-    } 
+  if (num_regs > 0 && ((int) next_reg != last_tok->X_add_number + 1))
+    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
+get_invisible_operands (TInsn *insn)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  static xtensa_insnbuf slotbuf = NULL;
+  xtensa_format fmt;
+  xtensa_opcode opc = insn->opcode;
+  int slot, opnd, fmt_found;
+  unsigned val;
+
+  if (!slotbuf)
+    slotbuf = xtensa_insnbuf_alloc (isa);
+
+  /* Find format/slot where this can be encoded.  */
+  fmt_found = 0;
+  slot = 0;
+  for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
+    {
+      for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+       {
+         if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opc) == 0)
+           {
+             fmt_found = 1;
+             break;
+           }
+       }
+      if (fmt_found) break;
+    }
+
+  if (!fmt_found)
+    {
+      as_bad (_("cannot encode opcode \"%s\""), xtensa_opcode_name (isa, opc));
+      return -1;
+    }
+
+  /* First encode all the visible operands
+     (to deal with shared field operands).  */
+  for (opnd = 0; opnd < insn->ntok; opnd++)
+    {
+      if (xtensa_operand_is_visible (isa, opc, opnd) == 1
+         && (insn->tok[opnd].X_op == O_register
+             || insn->tok[opnd].X_op == O_constant))
+       {
+         val = insn->tok[opnd].X_add_number;
+         xtensa_operand_encode (isa, opc, opnd, &val);
+         xtensa_operand_set_field (isa, opc, opnd, fmt, slot, slotbuf, val);
+       }
+    }
+
+  /* Then pull out the values for the invisible ones.  */
+  for (opnd = 0; opnd < insn->ntok; opnd++)
+    {
+      if (xtensa_operand_is_visible (isa, opc, opnd) == 0)
+       {
+         xtensa_operand_get_field (isa, opc, opnd, fmt, slot, slotbuf, &val);
+         xtensa_operand_decode (isa, opc, opnd, &val);
+         insn->tok[opnd].X_add_number = val;
+         if (xtensa_operand_is_register (isa, opc, opnd) == 1)
+           insn->tok[opnd].X_op = O_register;
+         else
+           insn->tok[opnd].X_op = O_constant;
+       }
+    }
+
+  return 0;
+}
+
+
 static void
 static void
-xg_reverse_shift_count (cnt_argp)
-     char **cnt_argp;
+xg_reverse_shift_count (char **cnt_argp)
 {
   char *cnt_arg, *new_arg;
   cnt_arg = *cnt_argp;
 {
   char *cnt_arg, *new_arg;
   cnt_arg = *cnt_argp;
@@ -1858,9 +2084,7 @@ xg_reverse_shift_count (cnt_argp)
    in *valp.  */
 
 static int
    in *valp.  */
 
 static int
-xg_arg_is_constant (arg, valp)
-     char *arg;
-     offsetT *valp;
+xg_arg_is_constant (char *arg, offsetT *valp)
 {
   expressionS exp;
   char *save_ptr = input_line_pointer;
 {
   expressionS exp;
   char *save_ptr = input_line_pointer;
@@ -1880,9 +2104,7 @@ xg_arg_is_constant (arg, valp)
 
 
 static void
 
 
 static void
-xg_replace_opname (popname, newop)
-     char **popname;
-     char *newop;
+xg_replace_opname (char **popname, char *newop)
 {
   free (*popname);
   *popname = (char *) xmalloc (strlen (newop) + 1);
 {
   free (*popname);
   *popname = (char *) xmalloc (strlen (newop) + 1);
@@ -1891,15 +2113,14 @@ xg_replace_opname (popname, newop)
 
 
 static int
 
 
 static int
-xg_check_num_args (pnum_args, expected_num, opname, arg_strings)
-     int *pnum_args;
-     int expected_num; 
-     char *opname;
-     char **arg_strings;
+xg_check_num_args (int *pnum_args,
+                  int expected_num,
+                  char *opname,
+                  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"),
              num_args, opname, expected_num);
     {
       as_bad (_("not enough operands (%d) for '%s'; expected %d"),
              num_args, opname, expected_num);
@@ -1923,56 +2144,160 @@ xg_check_num_args (pnum_args, expected_num, opname, arg_strings)
 }
 
 
 }
 
 
+/* If the register is not specified as part of the opcode,
+   then get it from the operand and move it to the opcode.  */
+
 static int
 static int
-xg_translate_sysreg_op (popname, pnum_args, arg_strings)
-     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_sysreg sr;
   char *opname, *new_opname;
   char *opname, *new_opname;
-  offsetT val;
-  bfd_boolean has_underbar = FALSE;
+  const char *sr_name;
+  int is_user, is_write;
 
   opname = *popname;
   if (*opname == '_')
 
   opname = *popname;
   if (*opname == '_')
+    opname += 1;
+  is_user = (opname[1] == 'u');
+  is_write = (opname[0] == 'w');
+
+  /* Opname == [rw]ur or [rwx]sr... */
+
+  if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
+    return -1;
+
+  /* Check if the argument is a symbolic register name.  */
+  sr = xtensa_sysreg_lookup_name (isa, arg_strings[1]);
+  /* Handle WSR to "INTSET" as a special case.  */
+  if (sr == XTENSA_UNDEFINED && is_write && !is_user
+      && !strcasecmp (arg_strings[1], "intset"))
+    sr = xtensa_sysreg_lookup_name (isa, "interrupt");
+  if (sr == XTENSA_UNDEFINED
+      || (xtensa_sysreg_is_user (isa, sr) == 1) != is_user)
+    {
+      /* Maybe it's a register number.... */
+      offsetT val;
+      if (!xg_arg_is_constant (arg_strings[1], &val))
+       {
+         as_bad (_("invalid register '%s' for '%s' instruction"),
+                 arg_strings[1], opname);
+         return -1;
+       }
+      sr = xtensa_sysreg_lookup (isa, val, is_user);
+      if (sr == XTENSA_UNDEFINED)
+       {
+         as_bad (_("invalid register number (%ld) for '%s' instruction"),
+                 (long) val, opname);
+         return -1;
+       }
+    }
+
+  /* Remove the last argument, which is now part of the opcode.  */
+  free (arg_strings[1]);
+  arg_strings[1] = 0;
+  *pnum_args = 1;
+
+  /* Translate the opcode.  */
+  sr_name = xtensa_sysreg_name (isa, sr);
+  /* Another special case for "WSR.INTSET"....  */
+  if (is_write && !is_user && !strcasecmp ("interrupt", sr_name))
+    sr_name = "intset";
+  new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+  sprintf (new_opname, "%s.%s", *popname, sr_name);
+  free (*popname);
+  *popname = new_opname;
+
+  return 0;
+}
+
+
+static int
+xtensa_translate_old_userreg_ops (char **popname)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_sysreg sr;
+  char *opname, *new_opname;
+  const char *sr_name;
+  bfd_boolean has_underbar = FALSE;
+
+  opname = *popname;
+  if (opname[0] == '_')
     {
       has_underbar = TRUE;
       opname += 1;
     }
 
     {
       has_underbar = TRUE;
       opname += 1;
     }
 
-  /* Opname == [rw]ur... */
-
-  if (opname[3] == '\0')
+  sr = xtensa_sysreg_lookup_name (isa, opname + 1);
+  if (sr != XTENSA_UNDEFINED)
     {
     {
-      /* If the register is not specified as part of the opcode,
-        then get it from the operand and move it to the opcode.  */
+      /* The new default name ("nnn") is different from the old default
+        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)
+        default.  */
+      static char namebuf[10];
+      sprintf (namebuf, "%d", xtensa_sysreg_number (isa, sr));
+      if (strcmp (namebuf, opname + 1) == 0)
+       return 0;
+    }
+  else
+    {
+      offsetT val;
+      char *end;
 
 
-      if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
-       return -1;
+      /* Only continue if the reg name is "URnnn".  */
+      if (opname[1] != 'u' || opname[2] != 'r')
+       return 0;
+      val = strtoul (opname + 3, &end, 10);
+      if (*end != '\0')
+       return 0;
 
 
-      if (!xg_arg_is_constant (arg_strings[1], &val))
-       {
-         as_bad (_("register number for `%s' is not a constant"), opname);
-         return -1;
-       }
-      if ((unsigned) val > 255)
+      sr = xtensa_sysreg_lookup (isa, val, 1);
+      if (sr == XTENSA_UNDEFINED)
        {
        {
-         as_bad (_("register number (%ld) for `%s' is out of range"),
-                 val, opname);
+         as_bad (_("invalid register number (%ld) for '%s'"),
+                 (long) val, opname);
          return -1;
        }
          return -1;
        }
+    }
 
 
-      /* Remove the last argument, which is now part of the opcode.  */
-      free (arg_strings[1]);
-      arg_strings[1] = 0;
-      *pnum_args = 1;
+  /* Translate the opcode.  */
+  sr_name = xtensa_sysreg_name (isa, sr);
+  new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+  sprintf (new_opname, "%s%cur.%s", (has_underbar ? "_" : ""),
+          opname[0], sr_name);
+  free (*popname);
+  *popname = new_opname;
+
+  return 0;
+}
+
+
+static int
+xtensa_translate_zero_immed (char *old_op,
+                            char *new_op,
+                            char **popname,
+                            int *pnum_args,
+                            char **arg_strings)
+{
+  char *opname;
+  offsetT val;
 
 
-      /* Translate the opcode.  */
-      new_opname = (char *) xmalloc (8);
-      sprintf (new_opname, "%s%cur%u", (has_underbar ? "_" : ""),
-              opname[0], (unsigned) val);
-      free (*popname);
-      *popname = new_opname;
+  opname = *popname;
+  assert (opname[0] != '_');
+
+  if (strcmp (opname, old_op) != 0)
+    return 0;
+
+  if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
+    return -1;
+  if (xg_arg_is_constant (arg_strings[1], &val) && val == 0)
+    {
+      xg_replace_opname (popname, new_op);
+      free (arg_strings[1]);
+      arg_strings[1] = arg_strings[2];
+      arg_strings[2] = 0;
+      *pnum_args = 2;
     }
 
   return 0;
     }
 
   return 0;
@@ -1983,10 +2308,7 @@ xg_translate_sysreg_op (popname, pnum_args, arg_strings)
    Returns non-zero if an error was found.  */
 
 static int
    Returns non-zero if an error was found.  */
 
 static int
-xg_translate_idioms (popname, pnum_args, arg_strings)
-     char **popname;
-     int *pnum_args;
-     char **arg_strings;
+xg_translate_idioms (char **popname, int *pnum_args, char **arg_strings)
 {
   char *opname = *popname;
   bfd_boolean has_underbar = FALSE;
 {
   char *opname = *popname;
   bfd_boolean has_underbar = FALSE;
@@ -1999,7 +2321,7 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
 
   if (strcmp (opname, "mov") == 0)
     {
 
   if (strcmp (opname, "mov") == 0)
     {
-      if (!has_underbar && code_density_available ())
+      if (use_transform () && !has_underbar && density_supported)
        xg_replace_opname (popname, "mov.n");
       else
        {
        xg_replace_opname (popname, "mov.n");
       else
        {
@@ -2033,9 +2355,14 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
       return 0;
     }
 
       return 0;
     }
 
-  if (strcmp (opname, "nop") == 0)
+  /* Don't do anything special with NOPs inside FLIX instructions.  They
+     are handled elsewhere.  Real NOP instructions are always available 
+     in configurations with FLIX, so this should never be an issue but
+     check for it anyway.  */
+  if (!cur_vinsn.inside_bundle && xtensa_nop_opcode == XTENSA_UNDEFINED
+      && strcmp (opname, "nop") == 0)
     {
     {
-      if (!has_underbar && code_density_available ())
+      if (use_transform () && !has_underbar && density_supported)
        xg_replace_opname (popname, "nop.n");
       else
        {
        xg_replace_opname (popname, "nop.n");
       else
        {
@@ -2053,613 +2380,606 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
       return 0;
     }
 
       return 0;
     }
 
-  if ((opname[0] == 'r' || opname[0] == 'w')
-      && opname[1] == 'u'
-      && opname[2] == 'r')
+  /* Recognize [RW]UR and [RWX]SR.  */
+  if ((((opname[0] == 'r' || opname[0] == 'w')
+       && (opname[1] == 'u' || opname[1] == 's'))
+       || (opname[0] == 'x' && opname[1] == 's'))
+      && opname[2] == 'r'
+      && 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
+     [RW]<name> if <name> is the non-default name of a user register.  */
+  if ((opname[0] == 'r' || opname[0] == 'w')
+      && xtensa_opcode_lookup (xtensa_default_isa, opname) == XTENSA_UNDEFINED)
+    return xtensa_translate_old_userreg_ops (popname);
 
 
-  /* WIDENING DENSITY OPCODES
-
-     questionable relaxations (widening) from old "tai" idioms:
-
-       ADD.N --> ADD
-       BEQZ.N --> BEQZ
-       RET.N --> RET
-       RETW.N --> RETW
-       MOVI.N --> MOVI
-       MOV.N --> MOV
-       NOP.N --> NOP
+  /* Relax branches that don't allow comparisons against an immediate value
+     of zero to the corresponding branches with implicit zero immediates.  */
+  if (!has_underbar && use_transform ())
+    {
+      if (xtensa_translate_zero_immed ("bnei", "bnez", popname,
+                                      pnum_args, arg_strings))
+       return -1;
 
 
-     Note: this incomplete list was imported to match the "tai"
-     behavior; other density opcodes are not handled.
+      if (xtensa_translate_zero_immed ("beqi", "beqz", popname,
+                                      pnum_args, arg_strings))
+       return -1;
 
 
-     The xtensa-relax code may know how to do these but it doesn't do
-     anything when these density opcodes appear inside a no-density
-     region.  Somehow GAS should either print an error when that happens
-     or do the widening.  The old "tai" behavior was to do the widening.
-     For now, I'll make it widen but print a warning.
+      if (xtensa_translate_zero_immed ("bgei", "bgez", popname,
+                                      pnum_args, arg_strings))
+       return -1;
 
 
-     FIXME: GAS needs to detect density opcodes inside no-density
-     regions and treat them as errors.  This code should be removed
-     when that is done.  */
+      if (xtensa_translate_zero_immed ("blti", "bltz", popname,
+                                      pnum_args, arg_strings))
+       return -1;
+    }
 
 
-  if (use_generics ()
-      && !has_underbar
-      && density_supported
-      && !code_density_available ())
-    {
-      if (strcmp (opname, "add.n") == 0)
-       xg_replace_opname (popname, "add");
+  return 0;
+}
 
 
-      else if (strcmp (opname, "beqz.n") == 0)
-       xg_replace_opname (popname, "beqz");
+\f
+/* Functions for dealing with the Xtensa ISA.  */
 
 
-      else if (strcmp (opname, "ret.n") == 0)
-       xg_replace_opname (popname, "ret");
+/* Currently the assembler only allows us to use a single target per
+   fragment.  Because of this, only one operand for a given
+   instruction may be symbolic.  If there is a PC-relative operand,
+   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
+   return -1.  */
 
 
-      else if (strcmp (opname, "retw.n") == 0)
-       xg_replace_opname (popname, "retw");
+static int
+get_relaxable_immed (xtensa_opcode opcode)
+{
+  int last_immed = -1;
+  int noperands, opi;
 
 
-      else if (strcmp (opname, "movi.n") == 0)
-       xg_replace_opname (popname, "movi");
+  if (opcode == XTENSA_UNDEFINED)
+    return -1;
 
 
-      else if (strcmp (opname, "mov.n") == 0)
-       {
-         if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
-           return -1;
-         xg_replace_opname (popname, "or");
-         arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1);
-         strcpy (arg_strings[2], arg_strings[1]);
-         *pnum_args = 3;
-       }
-
-      else if (strcmp (opname, "nop.n") == 0)
-       {
-         if (xg_check_num_args (pnum_args, 0, opname, arg_strings))
-           return -1;
-         xg_replace_opname (popname, "or");
-         arg_strings[0] = (char *) xmalloc (3);
-         arg_strings[1] = (char *) xmalloc (3);
-         arg_strings[2] = (char *) xmalloc (3);
-         strcpy (arg_strings[0], "a1");
-         strcpy (arg_strings[1], "a1");
-         strcpy (arg_strings[2], "a1");
-         *pnum_args = 3;
-       }
-    }
-
-  return 0;
-}
-
-\f
-/* Functions for dealing with the Xtensa ISA.  */
-
-/* Return true if the given operand is an immed or target instruction,
-   i.e., has a reloc associated with it.  Currently, this is only true
-   if the operand kind is "i, "l" or "L".  */
-
-static bfd_boolean
-operand_is_immed (opnd)
-     xtensa_operand opnd;
-{
-  const char *opkind = xtensa_operand_kind (opnd);
-  if (opkind[0] == '\0' || opkind[1] != '\0')
-    return FALSE;
-  switch (opkind[0])
-    {
-    case 'i':
-    case 'l':
-    case 'L':
-      return TRUE;
-    }
-  return FALSE;
-}
-
-
-/* Return true if the given operand is a pc-relative label.  This is
-   true for "l", "L", and "r" operand kinds.  */
-
-bfd_boolean
-operand_is_pcrel_label (opnd)
-     xtensa_operand opnd;
-{
-  const char *opkind = xtensa_operand_kind (opnd);
-  if (opkind[0] == '\0' || opkind[1] != '\0')
-    return FALSE;
-  switch (opkind[0])
-    {
-    case 'r':
-    case 'l':
-    case 'L':
-      return TRUE;
-    }
-  return FALSE;
-}
-
-
-/* Currently the assembler only allows us to use a single target per
-   fragment.  Because of this, only one operand for a given
-   instruction may be symbolic.  If there is an operand of kind "lrL",
-   the last one is chosen.  Otherwise, the result is the number of the
-   last operand of type "i", and if there are none of those, we fail
-   and return -1.  */
-
-int
-get_relaxable_immed (opcode)
-     xtensa_opcode opcode;
-{
-  int last_immed = -1;
-  int noperands, opi;
-  xtensa_operand operand;
-
-  if (opcode == XTENSA_UNDEFINED)
-    return -1;
-
-  noperands = xtensa_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--)
     {
-      operand = xtensa_get_operand (xtensa_default_isa, opcode, opi);
-      if (operand_is_pcrel_label (operand))
+      if (xtensa_operand_is_visible (xtensa_default_isa, opcode, opi) == 0)
+       continue;
+      if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, opi) == 1)
        return opi;
        return opi;
-      if (last_immed == -1 && operand_is_immed (operand))
+      if (last_immed == -1
+         && xtensa_operand_is_register (xtensa_default_isa, opcode, opi) == 0)
        last_immed = opi;
     }
   return last_immed;
 }
 
 
        last_immed = opi;
     }
   return last_immed;
 }
 
 
-xtensa_opcode
-get_opcode_from_buf (buf)
-     const char *buf;
+static xtensa_opcode
+get_opcode_from_buf (const char *buf, int slot)
 {
   static xtensa_insnbuf insnbuf = NULL;
 {
   static xtensa_insnbuf insnbuf = NULL;
-  xtensa_opcode opcode;
+  static xtensa_insnbuf slotbuf = NULL;
   xtensa_isa isa = xtensa_default_isa;
   xtensa_isa isa = xtensa_default_isa;
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (isa);
-
-  xtensa_insnbuf_from_chars (isa, insnbuf, buf);
-  opcode = xtensa_decode_insn (isa, insnbuf);
-  return opcode;
-}
-
-
-static bfd_boolean
-is_direct_call_opcode (opcode)
-     xtensa_opcode opcode;
-{
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
-
-  return (opcode == xtensa_call0_opcode
-         || opcode == xtensa_call4_opcode
-         || opcode == xtensa_call8_opcode
-         || opcode == xtensa_call12_opcode);
-}
-
-
-static bfd_boolean
-is_call_opcode (opcode)
-     xtensa_opcode opcode;
-{
-  if (is_direct_call_opcode (opcode))
-    return TRUE;
-
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
-
-  return (opcode == xtensa_callx0_opcode
-         || opcode == xtensa_callx4_opcode
-         || opcode == xtensa_callx8_opcode
-         || opcode == xtensa_callx12_opcode);
-}
+  xtensa_format fmt;
 
 
+  if (!insnbuf)
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
 
 
-/* Return true if the opcode is an entry opcode.  This is used because
-   "entry" adds an implicit ".align 4" and also the entry instruction
-   has an extra check for an operand value.  */
+  xtensa_insnbuf_from_chars (isa, insnbuf, (const unsigned char *) buf, 0);
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (fmt == XTENSA_UNDEFINED)
+    return XTENSA_UNDEFINED;
 
 
-static bfd_boolean
-is_entry_opcode (opcode)
-     xtensa_opcode opcode;
-{
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
+  if (slot >= xtensa_format_num_slots (isa, fmt))
+    return XTENSA_UNDEFINED;
 
 
-  return (opcode == xtensa_entry_opcode);
+  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+  return xtensa_opcode_decode (isa, fmt, slot, slotbuf);
 }
 
 
 }
 
 
-/* Return true if it is one of the loop opcodes.  Loops are special
-   because they need automatic alignment and they have a relaxation so
-   complex that we hard-coded it.  */
-
-static bfd_boolean
-is_loop_opcode (opcode)
-     xtensa_opcode opcode;
-{
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
-
-  return (opcode == xtensa_loop_opcode
-         || opcode == xtensa_loopnez_opcode
-         || opcode == xtensa_loopgtz_opcode);
-}
+#ifdef TENSILICA_DEBUG
 
 
+/* For debugging, print out the mapping of opcode numbers to opcodes.  */
 
 
-static bfd_boolean
-is_the_loop_opcode (opcode)
-     xtensa_opcode opcode;
+static void
+xtensa_print_insn_table (void)
 {
 {
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
+  int num_opcodes, num_operands;
+  xtensa_opcode opcode;
+  xtensa_isa isa = xtensa_default_isa;
 
 
-  return (opcode == xtensa_loop_opcode);
+  num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
+  for (opcode = 0; opcode < num_opcodes; opcode++)
+    {
+      int opn;
+      fprintf (stderr, "%d: %s: ", opcode, xtensa_opcode_name (isa, opcode));
+      num_operands = xtensa_opcode_num_operands (isa, opcode);
+      for (opn = 0; opn < num_operands; opn++)
+       {
+         if (xtensa_operand_is_visible (isa, opcode, opn) == 0)
+           continue;
+         if (xtensa_operand_is_register (isa, opcode, opn) == 1)
+           {
+             xtensa_regfile opnd_rf =
+               xtensa_operand_regfile (isa, opcode, opn);
+             fprintf (stderr, "%s ", xtensa_regfile_shortname (isa, opnd_rf));
+           }
+         else if (xtensa_operand_is_PCrelative (isa, opcode, opn) == 1)
+           fputs ("[lLr] ", stderr);
+         else
+           fputs ("i ", stderr);
+       }
+      fprintf (stderr, "\n");
+    }
 }
 
 
 }
 
 
-static bfd_boolean
-is_jx_opcode (opcode)
-     xtensa_opcode opcode;
+static void
+print_vliw_insn (xtensa_insnbuf vbuf)
 {
 {
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
-
-  return (opcode == xtensa_jx_opcode);
-}
-
-
-/* Return true if the opcode is a retw or retw.n.
-   Needed to add nops to avoid a hardware interlock issue.  */
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format f = xtensa_format_decode (isa, vbuf);
+  xtensa_insnbuf sbuf = xtensa_insnbuf_alloc (isa);
+  int op;
 
 
-static bfd_boolean
-is_windowed_return_opcode (opcode)
-     xtensa_opcode opcode;
-{
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
+  fprintf (stderr, "format = %d\n", f);
 
 
-  return (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode);
+  for (op = 0; op < xtensa_format_num_slots (isa, f); op++)
+    {
+      xtensa_opcode opcode;
+      const char *opname;
+      int operands;
+
+      xtensa_format_get_slot (isa, f, op, vbuf, sbuf);
+      opcode = xtensa_opcode_decode (isa, f, op, sbuf);
+      opname = xtensa_opcode_name (isa, opcode);
+
+      fprintf (stderr, "op in slot %i is %s;\n", op, opname);
+      fprintf (stderr, "   operands = ");
+      for (operands = 0;
+          operands < xtensa_opcode_num_operands (isa, opcode);
+          operands++)
+       {
+         unsigned int val;
+         if (xtensa_operand_is_visible (isa, opcode, operands) == 0)
+           continue;
+         xtensa_operand_get_field (isa, opcode, operands, f, op, sbuf, &val);
+         xtensa_operand_decode (isa, opcode, operands, &val);
+         fprintf (stderr, "%d ", val);
+       }
+      fprintf (stderr, "\n");
+    }
+  xtensa_insnbuf_free (isa, sbuf);
 }
 
 }
 
+#endif /* TENSILICA_DEBUG */
 
 
-/* Return true if the opcode type is "l" and the opcode is NOT a jump.  */
 
 static bfd_boolean
 
 static bfd_boolean
-is_conditional_branch_opcode (opcode)
-     xtensa_opcode opcode;
+is_direct_call_opcode (xtensa_opcode opcode)
 {
   xtensa_isa isa = xtensa_default_isa;
 {
   xtensa_isa isa = xtensa_default_isa;
-  int num_ops, i;
+  int n, num_operands;
 
 
-  if (opcode == xtensa_j_opcode && opcode != XTENSA_UNDEFINED)
+  if (xtensa_opcode_is_call (isa, opcode) != 1)
     return FALSE;
 
     return FALSE;
 
-  num_ops = xtensa_num_operands (isa, opcode);
-  for (i = 0; i < num_ops; i++)
+  num_operands = xtensa_opcode_num_operands (isa, opcode);
+  for (n = 0; n < num_operands; n++)
     {
     {
-      xtensa_operand operand = xtensa_get_operand (isa, opcode, i);
-      if (strcmp (xtensa_operand_kind (operand), "l") == 0)
+      if (xtensa_operand_is_register (isa, opcode, n) == 0
+         && xtensa_operand_is_PCrelative (isa, opcode, n) == 1)
        return TRUE;
     }
   return FALSE;
 }
 
 
        return TRUE;
     }
   return FALSE;
 }
 
 
-/* Return true if the given opcode is a conditional branch
-   instruction, i.e., currently this is true if the instruction 
-   is a jx or has an operand with 'l' type and is not a loop.  */
+/* Convert from BFD relocation type code to slot and operand number.
+   Returns non-zero on failure.  */
 
 
-bfd_boolean
-is_branch_or_jump_opcode (opcode)
-     xtensa_opcode opcode;
+static int
+decode_reloc (bfd_reloc_code_real_type reloc, int *slot, bfd_boolean *is_alt)
 {
 {
-  int opn, op_count;
-
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
-
-  if (is_loop_opcode (opcode))
-    return FALSE;
-
-  if (is_jx_opcode (opcode))
-    return TRUE;
-
-  op_count = xtensa_num_operands (xtensa_default_isa, opcode);
-  for (opn = 0; opn < op_count; opn++)
+  if (reloc >= BFD_RELOC_XTENSA_SLOT0_OP
+      && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
     {
     {
-      xtensa_operand opnd =
-       xtensa_get_operand (xtensa_default_isa, opcode, opn);
-      const char *opkind = xtensa_operand_kind (opnd);
-      if (opkind && opkind[0] == 'l' && opkind[1] == '\0')
-       return TRUE;
+      *slot = reloc - BFD_RELOC_XTENSA_SLOT0_OP;
+      *is_alt = FALSE;
     }
     }
-  return FALSE;
+  else if (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
+      && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT)
+    {
+      *slot = reloc - BFD_RELOC_XTENSA_SLOT0_ALT;
+      *is_alt = TRUE;
+    }
+  else
+    return -1;
+
+  return 0;
 }
 
 
 }
 
 
-/* Convert from operand numbers to BFD relocation type code.
-   Return BFD_RELOC_NONE on failure.  */
+/* Convert from slot number to BFD relocation type code for the
+   standard PC-relative relocations.  Return BFD_RELOC_NONE on
+   failure.  */
 
 
-bfd_reloc_code_real_type
-opnum_to_reloc (opnum)
-     int opnum;
+static bfd_reloc_code_real_type
+encode_reloc (int slot)
 {
 {
-  switch (opnum)
-    {
-    case 0:
-      return BFD_RELOC_XTENSA_OP0;
-    case 1:
-      return BFD_RELOC_XTENSA_OP1;
-    case 2:
-      return BFD_RELOC_XTENSA_OP2;
-    default:
-      break;
-    }
-  return BFD_RELOC_NONE;
+  if (slot < 0 || slot > 14)
+    return BFD_RELOC_NONE;
+
+  return BFD_RELOC_XTENSA_SLOT0_OP + slot;
 }
 
 
 }
 
 
-/* Convert from BFD relocation type code to operand number.
-   Return -1 on failure.  */
+/* Convert from slot numbers to BFD relocation type code for the
+   "alternate" relocations.  Return BFD_RELOC_NONE on failure.  */
 
 
-int
-reloc_to_opnum (reloc)
-     bfd_reloc_code_real_type reloc;
+static bfd_reloc_code_real_type
+encode_alt_reloc (int slot)
 {
 {
-  switch (reloc)
-    {
-    case BFD_RELOC_XTENSA_OP0:
-      return 0;
-    case BFD_RELOC_XTENSA_OP1:
-      return 1;
-    case BFD_RELOC_XTENSA_OP2:
-      return 2;
-    default:
-      break;
-    }
-  return -1;
+  if (slot < 0 || slot > 14)
+    return BFD_RELOC_NONE;
+
+  return BFD_RELOC_XTENSA_SLOT0_ALT + slot;
 }
 
 
 static void
 }
 
 
 static void
-xtensa_insnbuf_set_operand (insnbuf, opcode, operand, value, file, line)
-     xtensa_insnbuf insnbuf;
-     xtensa_opcode opcode;
-     xtensa_operand operand;
-     int32 value;
-     const char *file;
-     unsigned int line;
-{
-  xtensa_encode_result encode_result;
+xtensa_insnbuf_set_operand (xtensa_insnbuf slotbuf,
+                           xtensa_format fmt,
+                           int slot,
+                           xtensa_opcode opcode,
+                           int operand,
+                           uint32 value,
+                           const char *file,
+                           unsigned int line)
+{
   uint32 valbuf = value;
 
   uint32 valbuf = value;
 
-  encode_result = xtensa_operand_encode (operand, &valbuf);
-
-  switch (encode_result)
+  if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
     {
     {
-    case xtensa_encode_result_ok:
-      break;
-    case xtensa_encode_result_align:
-      as_bad_where ((char *) file, line,
-                   _("operand %d not properly aligned for '%s'"),
-                   value, xtensa_opcode_name (xtensa_default_isa, opcode));
-      break;
-    case xtensa_encode_result_not_in_table:
-      as_bad_where ((char *) file, line,
-                   _("operand %d not in immediate table for '%s'"),
-                   value, xtensa_opcode_name (xtensa_default_isa, opcode));
-      break;
-    case xtensa_encode_result_too_high:
-      as_bad_where ((char *) file, line,
-                   _("operand %d too large for '%s'"), value,
-                   xtensa_opcode_name (xtensa_default_isa, opcode));
-      break;
-    case xtensa_encode_result_too_low:
-      as_bad_where ((char *) file, line,
-                   _("operand %d too small for '%s'"), value,
-                   xtensa_opcode_name (xtensa_default_isa, opcode));
-      break;
-    case xtensa_encode_result_not_ok:
-      as_bad_where ((char *) file, line,
-                   _("operand %d is invalid for '%s'"), value,
-                   xtensa_opcode_name (xtensa_default_isa, opcode));
-      break;
-    default:
-      abort ();
+      if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, operand)
+         == 1)
+       as_bad_where ((char *) file, line,
+                     _("operand %d of '%s' has out of range value '%u'"), 
+                     operand + 1,
+                     xtensa_opcode_name (xtensa_default_isa, opcode),
+                     value);
+      else
+       as_bad_where ((char *) file, line,
+                     _("operand %d of '%s' has invalid value '%u'"),
+                     operand + 1,
+                     xtensa_opcode_name (xtensa_default_isa, opcode),
+                     value);
+      return;
     }
 
     }
 
-  xtensa_operand_set_field (operand, insnbuf, valbuf);
+  xtensa_operand_set_field (xtensa_default_isa, opcode, operand, fmt, slot,
+                           slotbuf, valbuf);
 }
 
 
 static uint32
 }
 
 
 static uint32
-xtensa_insnbuf_get_operand (insnbuf, opcode, opnum)
-     xtensa_insnbuf insnbuf;
-     xtensa_opcode opcode;
-     int opnum;
+xtensa_insnbuf_get_operand (xtensa_insnbuf slotbuf,
+                           xtensa_format fmt,
+                           int slot,
+                           xtensa_opcode opcode,
+                           int opnum)
 {
 {
-  xtensa_operand op = xtensa_get_operand (xtensa_default_isa, opcode, opnum);
-  return xtensa_operand_decode (op, xtensa_operand_get_field (op, insnbuf));
+  uint32 val = 0;
+  (void) xtensa_operand_get_field (xtensa_default_isa, opcode, opnum,
+                                  fmt, slot, slotbuf, &val);
+  (void) xtensa_operand_decode (xtensa_default_isa, opcode, opnum, &val);
+  return val;
 }
 
 }
 
+\f
+/* Checks for rules from xtensa-relax tables.  */
+
+/* The routine xg_instruction_matches_option_term must return TRUE
+   when a given option term is true.  The meaning of all of the option
+   terms is given interpretation by this function.  This is needed when
+   an option depends on the state of a directive, but there are no such
+   options in use right now.  */
 
 
-static void
-xtensa_insnbuf_set_immediate_field (opcode, insnbuf, value, file, line)
-     xtensa_opcode opcode;
-     xtensa_insnbuf insnbuf;
-     int32 value;
-     const char *file;
-     unsigned int line;
+static bfd_boolean
+xg_instruction_matches_option_term (TInsn *insn ATTRIBUTE_UNUSED,
+                                   const ReqOrOption *option)
 {
 {
-  xtensa_isa isa = xtensa_default_isa;
-  int last_opnd = xtensa_num_operands (isa, opcode) - 1;
-  xtensa_operand operand = xtensa_get_operand (isa, opcode, last_opnd);
-  xtensa_insnbuf_set_operand (insnbuf, opcode, operand, value, file, line);
+  if (strcmp (option->option_name, "realnop") == 0
+      || strncmp (option->option_name, "IsaUse", 6) == 0)
+    {
+      /* These conditions were evaluated statically when building the
+        relaxation table.  There's no need to reevaluate them now.  */
+      return TRUE;
+    }
+  else
+    {
+      as_fatal (_("internal error: unknown option name '%s'"),
+               option->option_name);
+    }
 }
 
 
 static bfd_boolean
 }
 
 
 static bfd_boolean
-is_negatable_branch (insn)
-     TInsn *insn;
+xg_instruction_matches_or_options (TInsn *insn,
+                                  const ReqOrOptionList *or_option)
 {
 {
-  xtensa_isa isa = xtensa_default_isa;
-  int i;
-  int num_ops = xtensa_num_operands (isa, insn->opcode);
-
-  for (i = 0; i < num_ops; i++)
+  const ReqOrOption *option;
+  /* Must match each of the AND terms.  */
+  for (option = or_option; option != NULL; option = option->next)
     {
     {
-      xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
-      char *kind = xtensa_operand_kind (opnd);
-      if (strlen (kind) == 1 && *kind == 'l')
+      if (xg_instruction_matches_option_term (insn, option))
        return TRUE;
     }
   return FALSE;
 }
 
        return TRUE;
     }
   return FALSE;
 }
 
-\f
-/* Various Other Internal Functions.  */
 
 static bfd_boolean
 
 static bfd_boolean
-is_unique_insn_expansion (r)
-     TransitionRule *r;
+xg_instruction_matches_options (TInsn *insn, const ReqOptionList *options)
 {
 {
-  if (!r->to_instr || r->to_instr->next != NULL)
-    return FALSE;
-  if (r->to_instr->typ != INSTR_INSTR)
-    return FALSE;
+  const ReqOption *req_options;
+  /* Must match each of the AND terms.  */
+  for (req_options = options;
+       req_options != NULL;
+       req_options = req_options->next)
+    {
+      /* Must match one of the OR clauses.  */
+      if (!xg_instruction_matches_or_options (insn,
+                                             req_options->or_option_terms))
+       return FALSE;
+    }
   return TRUE;
 }
 
 
   return TRUE;
 }
 
 
-static int
-xg_get_insn_size (insn)
-     TInsn *insn;
-{
-  assert (insn->insn_type == ITYPE_INSN);
-  return xtensa_insn_length (xtensa_default_isa, insn->opcode);
-}
-
+/* Return the transition rule that matches or NULL if none matches.  */
 
 
-static int
-xg_get_build_instr_size (insn)
-     BuildInstr *insn;
+static bfd_boolean
+xg_instruction_matches_rule (TInsn *insn, TransitionRule *rule)
 {
 {
-  assert (insn->typ == INSTR_INSTR);
-  return xtensa_insn_length (xtensa_default_isa, insn->opcode);
-}
-
+  PreconditionList *condition_l;
 
 
-bfd_boolean
-xg_is_narrow_insn (insn)
-     TInsn *insn;
-{
-  TransitionTable *table = xg_build_widen_table ();
-  TransitionList *l;
-  int num_match = 0;
-  assert (insn->insn_type == ITYPE_INSN);
-  assert (insn->opcode < table->num_opcodes);
+  if (rule->opcode != insn->opcode)
+    return FALSE;
 
 
-  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
+  for (condition_l = rule->conditions;
+       condition_l != NULL;
+       condition_l = condition_l->next)
     {
     {
-      TransitionRule *rule = l->rule;
+      expressionS *exp1;
+      expressionS *exp2;
+      Precondition *cond = condition_l->precond;
 
 
-      if (xg_instruction_matches_rule (insn, rule)
-         && is_unique_insn_expansion (rule))
+      switch (cond->typ)
        {
        {
-         /* It only generates one instruction... */
-         assert (insn->insn_type == ITYPE_INSN);
-         /* ...and it is a larger instruction.  */
-         if (xg_get_insn_size (insn)
-             < xg_get_build_instr_size (rule->to_instr))
+       case OP_CONSTANT:
+         /* The expression must be the constant.  */
+         assert (cond->op_num < insn->ntok);
+         exp1 = &insn->tok[cond->op_num];
+         if (expr_is_const (exp1))
            {
            {
-             num_match++;
-             if (num_match > 1)
-               return FALSE;
+             switch (cond->cmp)
+               {
+               case OP_EQUAL:
+                 if (get_expr_const (exp1) != cond->op_data)
+                   return FALSE;
+                 break;
+               case OP_NOTEQUAL:
+                 if (get_expr_const (exp1) == cond->op_data)
+                   return FALSE;
+                 break;
+               default:
+                 return FALSE;
+               }
            }
            }
-       }
-    }
-  return (num_match == 1);
-}
-
+         else if (expr_is_register (exp1))
+           {
+             switch (cond->cmp)
+               {
+               case OP_EQUAL:
+                 if (get_expr_register (exp1) != cond->op_data)
+                   return FALSE;
+                 break;
+               case OP_NOTEQUAL:
+                 if (get_expr_register (exp1) == cond->op_data)
+                   return FALSE;
+                 break;
+               default:
+                 return FALSE;
+               }
+           }
+         else
+           return FALSE;
+         break;
 
 
-bfd_boolean
-xg_is_single_relaxable_insn (insn)
-     TInsn *insn;
-{
-  TransitionTable *table = xg_build_widen_table ();
-  TransitionList *l;
-  int num_match = 0;
-  assert (insn->insn_type == ITYPE_INSN);
-  assert (insn->opcode < table->num_opcodes);
+       case OP_OPERAND:
+         assert (cond->op_num < insn->ntok);
+         assert (cond->op_data < insn->ntok);
+         exp1 = &insn->tok[cond->op_num];
+         exp2 = &insn->tok[cond->op_data];
 
 
-  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
-    {
-      TransitionRule *rule = l->rule;
+         switch (cond->cmp)
+           {
+           case OP_EQUAL:
+             if (!expr_is_equal (exp1, exp2))
+               return FALSE;
+             break;
+           case OP_NOTEQUAL:
+             if (expr_is_equal (exp1, exp2))
+               return FALSE;
+             break;
+           }
+         break;
 
 
-      if (xg_instruction_matches_rule (insn, rule)
-         && is_unique_insn_expansion (rule))
+       case OP_LITERAL:
+       case OP_LABEL:
+       default:
+         return FALSE;
+       }
+    }
+  if (!xg_instruction_matches_options (insn, rule->options))
+    return FALSE;
+
+  return TRUE;
+}
+
+
+static int
+transition_rule_cmp (const TransitionRule *a, const TransitionRule *b)
+{
+  bfd_boolean a_greater = FALSE;
+  bfd_boolean b_greater = FALSE;
+
+  ReqOptionList *l_a = a->options;
+  ReqOptionList *l_b = b->options;
+
+  /* We only care if they both are the same except for
+     a const16 vs. an l32r.  */
+
+  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_b = l_b->or_option_terms;
+      while (l_or_a && l_or_b && ((l_a->next == NULL) == (l_b->next == NULL)))
        {
        {
-         assert (insn->insn_type == ITYPE_INSN);
-         /* ... and it is a larger instruction.  */
-         if (xg_get_insn_size (insn)
-             <= xg_get_build_instr_size (rule->to_instr))
+         if (l_or_a->is_true != l_or_b->is_true)
+           return 0;
+         if (strcmp (l_or_a->option_name, l_or_b->option_name) != 0)
            {
            {
-             num_match++;
-             if (num_match > 1)
-               return FALSE;
+             /* This is the case we care about.  */
+             if (strcmp (l_or_a->option_name, "IsaUseConst16") == 0
+                 && strcmp (l_or_b->option_name, "IsaUseL32R") == 0)
+               {
+                 if (prefer_const16)
+                   a_greater = TRUE;
+                 else
+                   b_greater = TRUE;
+               }
+             else if (strcmp (l_or_a->option_name, "IsaUseL32R") == 0
+                      && strcmp (l_or_b->option_name, "IsaUseConst16") == 0)
+               {
+                 if (prefer_const16)
+                   b_greater = TRUE;
+                 else
+                   a_greater = TRUE;
+               }
+             else
+               return 0;
            }
            }
+         l_or_a = l_or_a->next;
+         l_or_b = l_or_b->next;
        }
        }
+      if (l_or_a || l_or_b)
+       return 0;
+
+      l_a = l_a->next;
+      l_b = l_b->next;
     }
     }
-  return (num_match == 1);
+  if (l_a || l_b)
+    return 0;
+
+  /* Incomparable if the substitution was used differently in two cases.  */
+  if (a_greater && b_greater)
+    return 0;
+
+  if (b_greater)
+    return 1;
+  if (a_greater)
+    return -1;
+
+  return 0;
 }
 
 
 }
 
 
-/* Return the largest size instruction that this instruction can
-   expand to.  Currently, in all cases, this is 3 bytes.  Of course we
-   could just calculate this once and generate a table.  */
+static TransitionRule *
+xg_instruction_match (TInsn *insn)
+{
+  TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
+  TransitionList *l;
+  assert (insn->opcode < table->num_opcodes);
 
 
-int
-xg_get_max_narrow_insn_size (opcode)
-     xtensa_opcode opcode;
+  /* Walk through all of the possible transitions.  */
+  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
+    {
+      TransitionRule *rule = l->rule;
+      if (xg_instruction_matches_rule (insn, rule))
+       return rule;
+    }
+  return NULL;
+}
+
+\f
+/* Various Other Internal Functions.  */
+
+static bfd_boolean
+is_unique_insn_expansion (TransitionRule *r)
+{
+  if (!r->to_instr || r->to_instr->next != NULL)
+    return FALSE;
+  if (r->to_instr->typ != INSTR_INSTR)
+    return FALSE;
+  return TRUE;
+}
+
+
+/* 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
+   non-null, go ahead and generate the relaxed instruction into TARG.  If
+   NARROW_ONLY is true, then only consider relaxations that widen a narrow
+   instruction, i.e., ignore relaxations that convert to an instruction of
+   equal size.  In some contexts where this function is used, only
+   a single widening is allowed and the NARROW_ONLY argument is used to
+   exclude cases like ADDI being "widened" to an ADDMI, which may
+   later be relaxed to an ADDMI/ADDI pair.  */
+
+bfd_boolean
+xg_is_single_relaxable_insn (TInsn *insn, TInsn *targ, bfd_boolean narrow_only)
 {
 {
-  /* Go ahead and compute it, but it better be 3.  */
-  TransitionTable *table = xg_build_widen_table ();
+  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
   TransitionList *l;
   TransitionList *l;
-  int old_size = xtensa_insn_length (xtensa_default_isa, opcode);
-  assert (opcode < table->num_opcodes);
+  TransitionRule *match = 0;
 
 
-  /* Actually we can do better. Check to see of Only one applies.  */
-  for (l = table->table[opcode]; l != NULL; l = l->next)
+  assert (insn->insn_type == ITYPE_INSN);
+  assert (insn->opcode < table->num_opcodes);
+
+  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
     {
       TransitionRule *rule = l->rule;
 
     {
       TransitionRule *rule = l->rule;
 
-      /* If it only generates one instruction.  */
-      if (is_unique_insn_expansion (rule))
+      if (xg_instruction_matches_rule (insn, rule)
+         && is_unique_insn_expansion (rule)
+         && (xg_get_single_size (insn->opcode) + (narrow_only ? 1 : 0)
+             <= xg_get_single_size (rule->to_instr->opcode)))
        {
        {
-         int new_size = xtensa_insn_length (xtensa_default_isa,
-                                            rule->to_instr->opcode);
-         if (new_size > old_size)
-           {
-             assert (new_size == 3);
-             return 3;
-           }
+         if (match)
+           return FALSE;
+         match = rule;
        }
     }
        }
     }
-  return old_size;
+  if (!match)
+    return FALSE;
+
+  if (targ)
+    xg_build_to_insn (targ, insn, match->to_instr);
+  return TRUE;
 }
 
 
 /* Return the maximum number of bytes this opcode can expand to.  */
 
 }
 
 
 /* Return the maximum number of bytes this opcode can expand to.  */
 
-int
-xg_get_max_insn_widen_size (opcode)
-     xtensa_opcode opcode;
+static int
+xg_get_max_insn_widen_size (xtensa_opcode opcode)
 {
 {
-  TransitionTable *table = xg_build_widen_table ();
+  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
   TransitionList *l;
   TransitionList *l;
-  int max_size = xtensa_insn_length (xtensa_default_isa, opcode);
+  int max_size = xg_get_single_size (opcode);
 
   assert (opcode < table->num_opcodes);
 
 
   assert (opcode < table->num_opcodes);
 
@@ -2683,9 +3003,7 @@ xg_get_max_insn_widen_size (opcode)
            switch (build_list->typ)
              {
              case INSTR_INSTR:
            switch (build_list->typ)
              {
              case INSTR_INSTR:
-               this_size += xtensa_insn_length (xtensa_default_isa,
-                                                build_list->opcode);
-
+               this_size += xg_get_single_size (build_list->opcode);
                break;
              case INSTR_LITERAL_DEF:
              case INSTR_LABEL_DEF:
                break;
              case INSTR_LITERAL_DEF:
              case INSTR_LABEL_DEF:
@@ -2702,11 +3020,10 @@ xg_get_max_insn_widen_size (opcode)
 
 /* Return the maximum number of literal bytes this opcode can generate.  */
 
 
 /* Return the maximum number of literal bytes this opcode can generate.  */
 
-int
-xg_get_max_insn_widen_literal_size (opcode)
-     xtensa_opcode opcode;
+static int
+xg_get_max_insn_widen_literal_size (xtensa_opcode opcode)
 {
 {
-  TransitionTable *table = xg_build_widen_table ();
+  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
   TransitionList *l;
   int max_size = 0;
 
   TransitionList *l;
   int max_size = 0;
 
@@ -2732,7 +3049,7 @@ xg_get_max_insn_widen_literal_size (opcode)
            switch (build_list->typ)
              {
              case INSTR_LITERAL_DEF:
            switch (build_list->typ)
              {
              case INSTR_LITERAL_DEF:
-               /* hard coded 4-byte literal.  */
+               /* Hard-coded 4-byte literal.  */
                this_size += 4;
                break;
              case INSTR_INSTR:
                this_size += 4;
                break;
              case INSTR_INSTR:
@@ -2748,13 +3065,11 @@ xg_get_max_insn_widen_literal_size (opcode)
 }
 
 
 }
 
 
-bfd_boolean
-xg_is_relaxable_insn (insn, lateral_steps)
-     TInsn *insn;
-     int lateral_steps;
+static bfd_boolean
+xg_is_relaxable_insn (TInsn *insn, int lateral_steps)
 {
   int steps_taken = 0;
 {
   int steps_taken = 0;
-  TransitionTable *table = xg_build_widen_table ();
+  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
   TransitionList *l;
 
   assert (insn->insn_type == ITYPE_INSN);
   TransitionList *l;
 
   assert (insn->insn_type == ITYPE_INSN);
@@ -2776,7 +3091,7 @@ xg_is_relaxable_insn (insn, lateral_steps)
 
 
 static symbolS *
 
 
 static symbolS *
-get_special_literal_symbol ()
+get_special_literal_symbol (void)
 {
   static symbolS *sym = NULL;
 
 {
   static symbolS *sym = NULL;
 
@@ -2787,7 +3102,7 @@ get_special_literal_symbol ()
 
 
 static symbolS *
 
 
 static symbolS *
-get_special_label_symbol ()
+get_special_label_symbol (void)
 {
   static symbolS *sym = NULL;
 
 {
   static symbolS *sym = NULL;
 
@@ -2797,18 +3112,182 @@ get_special_label_symbol ()
 }
 
 
 }
 
 
-/* Return true on success.  */
+static bfd_boolean
+xg_valid_literal_expression (const expressionS *exp)
+{
+  switch (exp->X_op)
+    {
+    case O_constant:
+    case O_symbol:
+    case O_big:
+    case O_uminus:
+    case O_subtract:
+    case O_pltrel:
+      return TRUE;
+    default:
+      return FALSE;
+    }
+}
+
+
+/* This will check to see if the value can be converted into the
+   operand type.  It will return TRUE if it does not fit.  */
 
 
-bfd_boolean
-xg_build_to_insn (targ, insn, bi)
-     TInsn *targ;
-     TInsn *insn;
-     BuildInstr *bi;
+static bfd_boolean
+xg_check_operand (int32 value, xtensa_opcode opcode, int operand)
+{
+  uint32 valbuf = value;
+  if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
+    return TRUE;
+  return FALSE;
+}
+
+
+/* Assumes: All immeds are constants.  Check that all constants fit
+   into their immeds; return FALSE if not.  */
+
+static bfd_boolean
+xg_immeds_fit (const TInsn *insn)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  int i;
+
+  int n = insn->ntok;
+  assert (insn->insn_type == ITYPE_INSN);
+  for (i = 0; i < n; ++i)
+    {
+      const expressionS *expr = &insn->tok[i];
+      if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
+       continue;
+
+      switch (expr->X_op)
+       {
+       case O_register:
+       case O_constant:
+         if (xg_check_operand (expr->X_add_number, insn->opcode, i))
+           return FALSE;
+         break;
+
+       default:
+         /* The symbol should have a fixup associated with it.  */
+         assert (FALSE);
+         break;
+       }
+    }
+  return TRUE;
+}
+
+
+/* This should only be called after we have an initial
+   estimate of the addresses.  */
+
+static bfd_boolean
+xg_symbolic_immeds_fit (const TInsn *insn,
+                       segT pc_seg,
+                       fragS *pc_frag,
+                       offsetT pc_offset,
+                       long stretch)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  symbolS *symbolP;
+  fragS *sym_frag;
+  offsetT target, pc;
+  uint32 new_offset;
+  int i;
+  int n = insn->ntok;
+
+  assert (insn->insn_type == ITYPE_INSN);
+
+  for (i = 0; i < n; ++i)
+    {
+      const expressionS *expr = &insn->tok[i];
+      if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
+       continue;
+
+      switch (expr->X_op)
+       {
+       case O_register:
+       case O_constant:
+         if (xg_check_operand (expr->X_add_number, insn->opcode, i))
+           return FALSE;
+         break;
+
+       case O_lo16:
+       case O_hi16:
+         /* Check for the worst case.  */
+         if (xg_check_operand (0xffff, insn->opcode, i))
+           return FALSE;
+         break;
+
+       case O_symbol:
+         /* We only allow symbols for PC-relative references.
+            If pc_frag == 0, then we don't have frag locations yet.  */
+         if (pc_frag == 0
+             || xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 0)
+           return FALSE;
+
+         /* If it is a weak symbol, then assume it won't reach.  */
+         if (S_IS_WEAK (expr->X_add_symbol))
+           return FALSE;
+
+         if (is_direct_call_opcode (insn->opcode)
+             && ! pc_frag->tc_frag_data.use_longcalls)
+           {
+             /* If callee is undefined or in a different segment, be
+                optimistic and assume it will be in range.  */
+             if (S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
+               return TRUE;
+           }
+
+         /* Only references within a segment can be known to fit in the
+            operands at assembly time.  */
+         if (S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
+           return FALSE;
+
+         symbolP = expr->X_add_symbol;
+         sym_frag = symbol_get_frag (symbolP);
+         target = S_GET_VALUE (symbolP) + expr->X_add_number;
+         pc = pc_frag->fr_address + pc_offset;
+
+         /* 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,
+            it will be because some frag between grows, and that will
+            force another pass.  Beware zero-length frags.  There
+            should be a faster way to do this.  */
+
+         if (stretch != 0
+             && sym_frag->relax_marker != pc_frag->relax_marker
+             && S_GET_SEGMENT (symbolP) == pc_seg)
+           {
+             target += stretch;
+           }
+
+         new_offset = target;
+         xtensa_operand_do_reloc (isa, insn->opcode, i, &new_offset, pc);
+         if (xg_check_operand (new_offset, insn->opcode, i))
+           return FALSE;
+         break;
+
+       default:
+         /* The symbol should have a fixup associated with it.  */
+         return FALSE;
+       }
+    }
+
+  return TRUE;
+}
+
+
+/* Return TRUE on success.  */
+
+static bfd_boolean
+xg_build_to_insn (TInsn *targ, TInsn *insn, BuildInstr *bi)
 {
   BuildOp *op;
   symbolS *sym;
 
 {
   BuildOp *op;
   symbolS *sym;
 
-  memset (targ, 0, sizeof (TInsn));
+  tinsn_init (targ);
+  targ->linenum = insn->linenum;
   switch (bi->typ)
     {
     case INSTR_INSTR:
   switch (bi->typ)
     {
     case INSTR_INSTR:
@@ -2844,6 +3323,38 @@ xg_build_to_insn (targ, insn, bi)
              sym = get_special_label_symbol ();
              set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
              break;
              sym = get_special_label_symbol ();
              set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
              break;
+           case OP_OPERAND_HI16U:
+           case OP_OPERAND_LOW16U:
+             assert (op_data < insn->ntok);
+             if (expr_is_const (&insn->tok[op_data]))
+               {
+                 long val;
+                 copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+                 val = xg_apply_userdef_op_fn (op->typ,
+                                               targ->tok[op_num].
+                                               X_add_number);
+                 targ->tok[op_num].X_add_number = val;
+               }
+             else
+               {
+                 /* For const16 we can create relocations for these.  */
+                 if (targ->opcode == XTENSA_UNDEFINED
+                     || (targ->opcode != xtensa_const16_opcode))
+                   return FALSE;
+                 assert (op_data < insn->ntok);
+                 /* Need to build a O_lo16 or O_hi16.  */
+                 copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+                 if (targ->tok[op_num].X_op == O_symbol)
+                   {
+                     if (op->typ == OP_OPERAND_HI16U)
+                       targ->tok[op_num].X_op = O_hi16;
+                     else if (op->typ == OP_OPERAND_LOW16U)
+                       targ->tok[op_num].X_op = O_lo16;
+                     else
+                       return FALSE;
+                   }
+               }
+             break;
            default:
              /* currently handles:
                 OP_OPERAND_LOW8
            default:
              /* currently handles:
                 OP_OPERAND_LOW8
@@ -2889,6 +3400,9 @@ xg_build_to_insn (targ, insn, bi)
            {
            case OP_OPERAND:
              assert (op_data < insn->ntok);
            {
            case OP_OPERAND:
              assert (op_data < insn->ntok);
+             /* We can only pass resolvable literals through.  */
+             if (!xg_valid_literal_expression (&insn->tok[op_data]))
+               return FALSE;
              copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
              break;
            case OP_LITERAL:
              copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
              break;
            case OP_LITERAL:
@@ -2906,7 +3420,7 @@ xg_build_to_insn (targ, insn, bi)
       targ->opcode = XTENSA_UNDEFINED;
       targ->insn_type = ITYPE_LABEL;
       targ->is_specific_opcode = FALSE;
       targ->opcode = XTENSA_UNDEFINED;
       targ->insn_type = ITYPE_LABEL;
       targ->is_specific_opcode = FALSE;
-      /* Literal with no ops. is a label?  */
+      /* Literal with no ops is a label?  */
       assert (op == NULL);
       break;
 
       assert (op == NULL);
       break;
 
@@ -2918,13 +3432,10 @@ xg_build_to_insn (targ, insn, bi)
 }
 
 
 }
 
 
-/* Return true on success.  */
+/* Return TRUE on success.  */
 
 
-bfd_boolean
-xg_build_to_stack (istack, insn, bi)
-     IStack *istack;
-     TInsn *insn;
-     BuildInstr *bi;
+static bfd_boolean
+xg_build_to_stack (IStack *istack, TInsn *insn, BuildInstr *bi)
 {
   for (; bi != NULL; bi = bi->next)
     {
 {
   for (; bi != NULL; bi = bi->next)
     {
@@ -2937,17 +3448,14 @@ xg_build_to_stack (istack, insn, bi)
 }
 
 
 }
 
 
-/* Return true on valid expansion.  */
+/* Return TRUE on valid expansion.  */
 
 
-bfd_boolean
-xg_expand_to_stack (istack, insn, lateral_steps)
-     IStack *istack;
-     TInsn *insn;
-     int lateral_steps;
+static bfd_boolean
+xg_expand_to_stack (IStack *istack, TInsn *insn, int lateral_steps)
 {
   int stack_size = istack->ninsn;
   int steps_taken = 0;
 {
   int stack_size = istack->ninsn;
   int steps_taken = 0;
-  TransitionTable *table = xg_build_widen_table ();
+  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
   TransitionList *l;
 
   assert (insn->insn_type == ITYPE_INSN);
   TransitionList *l;
 
   assert (insn->insn_type == ITYPE_INSN);
@@ -2988,246 +3496,50 @@ xg_expand_to_stack (istack, insn, lateral_steps)
   return FALSE;
 }
 
   return FALSE;
 }
 
+\f
+/* Relax the assembly instruction at least "min_steps".
+   Return the number of steps taken.
+
+   For relaxation to correctly terminate, every relaxation chain must
+   terminate in one of two ways:
+
+   1.  If the chain from one instruction to the next consists entirely of
+       single instructions, then the chain *must* handle all possible
+       immediates without failing.  It must not ever fail because an
+       immediate is out of range.  The MOVI.N -> MOVI -> L32R relaxation
+       chain is one example.  L32R loads 32 bits, and there cannot be an
+       immediate larger than 32 bits, so it satisfies this condition.
+       Single instruction relaxation chains are as defined by
+       xg_is_single_relaxable_instruction.
+
+   2.  Otherwise, the chain must end in a multi-instruction expansion: e.g.,
+       BNEZ.N -> BNEZ -> BNEZ.W15 -> BENZ.N/J
+
+   Strictly speaking, in most cases you can violate condition 1 and be OK
+   -- in particular when the last two instructions have the same single
+   size.  But nevertheless, you should guarantee the above two conditions.
+
+   We could fix this so that single-instruction expansions correctly
+   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
+   branches), you need a multi-instruction expansion to get the full range
+   anyway.  And because 18-bit branches are handled identically to 15-bit
+   branches, there isn't any point in changing it.  */
 
 
-bfd_boolean
-xg_expand_narrow (targ, insn)
-     TInsn *targ;
-     TInsn *insn;
+static int
+xg_assembly_relax (IStack *istack,
+                  TInsn *insn,
+                  segT pc_seg,
+                  fragS *pc_frag,      /* if pc_frag == 0, not pc-relative */
+                  offsetT pc_offset,   /* offset in fragment */
+                  int min_steps,       /* minimum conversion steps */
+                  long stretch)        /* number of bytes stretched so far */
 {
 {
-  TransitionTable *table = xg_build_widen_table ();
-  TransitionList *l;
+  int steps_taken = 0;
 
 
-  assert (insn->insn_type == ITYPE_INSN);
-  assert (insn->opcode < table->num_opcodes);
-
-  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
-    {
-      TransitionRule *rule = l->rule;
-      if (xg_instruction_matches_rule (insn, rule)
-         && is_unique_insn_expansion (rule))
-       {
-         /* Is it a larger instruction?  */
-         if (xg_get_insn_size (insn)
-             <= xg_get_build_instr_size (rule->to_instr))
-           {
-             xg_build_to_insn (targ, insn, rule->to_instr);
-             return FALSE;
-           }
-       }
-    }
-  return TRUE;
-}
-
-
-/* Assumes: All immeds are constants.  Check that all constants fit
-   into their immeds; return false if not.  */
-
-static bfd_boolean
-xg_immeds_fit (insn)
-     const TInsn *insn;
-{
-  int i;
-
-  int n = insn->ntok;
-  assert (insn->insn_type == ITYPE_INSN);
-  for (i = 0; i < n; ++i)
-    {
-      const expressionS *expr = &insn->tok[i];
-      xtensa_operand opnd = xtensa_get_operand (xtensa_default_isa,
-                                               insn->opcode, i);
-      if (!operand_is_immed (opnd))
-       continue;
-
-      switch (expr->X_op)
-       {
-       case O_register:
-       case O_constant:
-         {
-           if (xg_check_operand (expr->X_add_number, opnd))
-             return FALSE;
-         }
-         break;
-       default:
-         /* The symbol should have a fixup associated with it.  */
-         assert (FALSE);
-         break;
-       }
-    }
-  return TRUE;
-}
-
-
-/* This should only be called after we have an initial
-   estimate of the addresses.  */
-
-static bfd_boolean
-xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
-     const TInsn *insn;
-     segT pc_seg;
-     fragS *pc_frag;
-     offsetT pc_offset;
-     long stretch;
-{
-  symbolS *symbolP;
-  offsetT target, pc, new_offset;
-  int i;
-  int n = insn->ntok;
-
-  assert (insn->insn_type == ITYPE_INSN);
-
-  for (i = 0; i < n; ++i)
-    {
-      const expressionS *expr = &insn->tok[i];
-      xtensa_operand opnd = xtensa_get_operand (xtensa_default_isa,
-                                               insn->opcode, i);
-      if (!operand_is_immed (opnd))
-       continue;
-
-      switch (expr->X_op)
-       {
-       case O_register:
-       case O_constant:
-         if (xg_check_operand (expr->X_add_number, opnd))
-           return FALSE;
-         break;
-
-       case O_symbol:
-         /* We only allow symbols for pc-relative stuff.
-            If pc_frag == 0, then we don't have frag locations yet.  */
-         if (pc_frag == 0)
-           return FALSE;
-
-         /* If it is PC-relative and the symbol is in the same segment as
-            the PC.... */
-         if (!xtensa_operand_isPCRelative (opnd)
-             || S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
-           return FALSE;
-
-         symbolP = expr->X_add_symbol;
-         target = S_GET_VALUE (symbolP) + expr->X_add_number;
-         pc = pc_frag->fr_address + pc_offset;
-
-         /* 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,
-            it will be because some frag between grows, and that will
-            force another pass.  Beware zero-length frags.  There
-            should be a faster way to do this.  */
-
-         if (stretch && is_dnrange (pc_frag, symbolP, stretch))
-           target += stretch;
-
-         new_offset = xtensa_operand_do_reloc (opnd, target, pc);
-         if (xg_check_operand (new_offset, opnd))
-           return FALSE;
-         break;
-
-       default:
-         /* The symbol should have a fixup associated with it.  */
-         return FALSE;
-       }
-    }
-
-  return TRUE;
-}
-
-
-/* This will check to see if the value can be converted into the
-   operand type.  It will return true if it does not fit.  */
-
-static bfd_boolean
-xg_check_operand (value, operand)
-     int32 value;
-     xtensa_operand operand;
-{
-  uint32 valbuf = value;
-  return (xtensa_operand_encode (operand, &valbuf) != xtensa_encode_result_ok);
-}
-
-
-/* Check if a symbol is pointing to somewhere after
-   the start frag, given that the segment has stretched 
-   by stretch during relaxation.
-
-   This is more complicated than it might appear at first blush
-   because of the stretching that goes on. Here is how the check
-   works:
-
-   If the symbol and the frag are in the same segment, then
-   the symbol could be down range. Note that this function 
-   assumes that start_frag is in now_seg.
-
-   If the symbol is pointing to a frag with an address greater than 
-   than the start_frag's address, then it _could_ be down range. 
-
-   The problem comes because target_frag may or may not have had
-   stretch bytes added to its address already, depending on if it is 
-   before or after start frag. (And if we knew that, then we wouldn't
-   need this function.) start_frag has definitely already had stretch
-   bytes added to its address.
-   
-   If target_frag's address hasn't been adjusted yet, then to 
-   determine if it comes after start_frag, we need to subtract
-   stretch from start_frag's address.
-
-   If target_frag's address has been adjusted, then it might have
-   been adjusted such that it comes after start_frag's address minus
-   stretch bytes.
-
-   So, in that case, we scan for it down stream to within 
-   stretch bytes. We could search to the end of the fr_chain, but
-   that ends up taking too much time (over a minute on some gnu 
-   tests).  */
-
-int
-is_dnrange (start_frag, sym, stretch)
-     fragS *start_frag;
-     symbolS *sym;
-     long stretch;
-{
-  if (S_GET_SEGMENT (sym) == now_seg)
-    {
-      fragS *cur_frag = symbol_get_frag (sym);
-
-      if (cur_frag->fr_address >= start_frag->fr_address - stretch)
-       {
-         int distance = stretch;
-
-         while (cur_frag && distance >= 0) 
-           {
-             distance -= cur_frag->fr_fix;
-             if (cur_frag == start_frag)
-               return 0;
-             cur_frag = cur_frag->fr_next;
-           }
-         return 1;
-       }
-    }
-  return 0;
-}
-
-\f
-/* Relax the assembly instruction at least "min_steps".
-   Return the number of steps taken.  */
-
-int
-xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
-                  stretch)
-     IStack *istack;
-     TInsn *insn;
-     segT pc_seg;
-     fragS *pc_frag;           /* If pc_frag == 0, then no pc-relative.  */
-     offsetT pc_offset;                /* Offset in fragment.  */
-     int min_steps;            /* Minimum number of conversion steps.  */
-     long stretch;             /* Number of bytes stretched so far.  */
-{
-  int steps_taken = 0;
-
-  /* assert (has no symbolic operands)
-     Some of its immeds don't fit.
-     Try to build a relaxed version.
-     This may go through a couple of stages
-     of single instruction transformations before
-     we get there.  */
+  /* Some of its immeds don't fit.  Try to build a relaxed version.
+     This may go through a couple of stages of single instruction
+     transformations before we get there.  */
 
   TInsn single_target;
   TInsn current_insn;
 
   TInsn single_target;
   TInsn current_insn;
@@ -3240,26 +3552,22 @@ xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
       istack_push (istack, insn);
       return steps_taken;
     }
       istack_push (istack, insn);
       return steps_taken;
     }
-  tinsn_copy (&current_insn, insn);
+  current_insn = *insn;
 
 
-  /* Walk through all of the single instruction expansions. */
-  while (xg_is_single_relaxable_insn (&current_insn))
+  /* Walk through all of the single instruction expansions.  */
+  while (xg_is_single_relaxable_insn (&current_insn, &single_target, FALSE))
     {
     {
-      int error_val = xg_expand_narrow (&single_target, &current_insn);
-
-      assert (!error_val);
-
+      steps_taken++;
       if (xg_symbolic_immeds_fit (&single_target, pc_seg, pc_frag, pc_offset,
                                  stretch))
        {
       if (xg_symbolic_immeds_fit (&single_target, pc_seg, pc_frag, pc_offset,
                                  stretch))
        {
-         steps_taken++;
          if (steps_taken >= min_steps)
            {
              istack_push (istack, &single_target);
              return steps_taken;
            }
        }
          if (steps_taken >= min_steps)
            {
              istack_push (istack, &single_target);
              return steps_taken;
            }
        }
-      tinsn_copy (&current_insn, &single_target);
+      current_insn = single_target;
     }
 
   /* Now check for a multi-instruction expansion.  */
     }
 
   /* Now check for a multi-instruction expansion.  */
@@ -3291,8 +3599,7 @@ xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
 
 
 static void
 
 
 static void
-xg_force_frag_space (size)
-     int size;
+xg_force_frag_space (int size)
 {
   /* This may have the side effect of creating a new fragment for the
      space to go into.  I just do not like the name of the "frag"
 {
   /* This may have the side effect of creating a new fragment for the
      space to go into.  I just do not like the name of the "frag"
@@ -3301,12 +3608,12 @@ xg_force_frag_space (size)
 }
 
 
 }
 
 
-void
-xg_finish_frag (last_insn, state, max_growth, is_insn)
-     char *last_insn;
-     enum xtensa_relax_statesE state;
-     int max_growth;
-     bfd_boolean is_insn;
+static void
+xg_finish_frag (char *last_insn,
+               enum xtensa_relax_statesE frag_state,
+               enum xtensa_relax_statesE slot0_state,
+               int max_growth,
+               bfd_boolean is_insn)
 {
   /* 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
 {
   /* 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
@@ -3314,6 +3621,7 @@ xg_finish_frag (last_insn, state, max_growth, is_insn)
      beginning of the growth area.  */
 
   fragS *old_frag;
      beginning of the growth area.  */
 
   fragS *old_frag;
+
   xg_force_frag_space (max_growth);
 
   old_frag = frag_now;
   xg_force_frag_space (max_growth);
 
   old_frag = frag_now;
@@ -3323,33 +3631,59 @@ xg_finish_frag (last_insn, state, max_growth, is_insn)
     frag_now->tc_frag_data.is_insn = TRUE;
 
   frag_var (rs_machine_dependent, max_growth, max_growth,
     frag_now->tc_frag_data.is_insn = TRUE;
 
   frag_var (rs_machine_dependent, max_growth, max_growth,
-           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;
+  xtensa_set_frag_assembly_state (frag_now);
 
   /* Just to make sure that we did not split it up.  */
   assert (old_frag->fr_next == frag_now);
 }
 
 
 
   /* Just to make sure that we did not split it up.  */
   assert (old_frag->fr_next == frag_now);
 }
 
 
+/* Return TRUE if the target frag is one of the next non-empty frags.  */
+
+static bfd_boolean
+is_next_frag_target (const fragS *fragP, const fragS *target)
+{
+  if (fragP == NULL)
+    return FALSE;
+
+  for (; fragP; fragP = fragP->fr_next)
+    {
+      if (fragP == target)
+       return TRUE;
+      if (fragP->fr_fix != 0)
+       return FALSE;
+      if (fragP->fr_type == rs_fill && fragP->fr_offset != 0)
+       return FALSE;
+      if ((fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
+         && ((fragP->fr_address % (1 << fragP->fr_offset)) != 0))
+       return FALSE;
+      if (fragP->fr_type == rs_space)
+       return FALSE;
+    }
+  return FALSE;
+}
+
+
 static bfd_boolean
 static bfd_boolean
-is_branch_jmp_to_next (insn, fragP)
-     TInsn *insn;
-     fragS *fragP;
+is_branch_jmp_to_next (TInsn *insn, fragS *fragP)
 {
   xtensa_isa isa = xtensa_default_isa;
   int i;
 {
   xtensa_isa isa = xtensa_default_isa;
   int i;
-  int num_ops = xtensa_num_operands (isa, insn->opcode);
+  int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
   int target_op = -1;
   symbolS *sym;
   fragS *target_frag;
 
   int target_op = -1;
   symbolS *sym;
   fragS *target_frag;
 
-  if (is_loop_opcode (insn->opcode))
+  if (xtensa_opcode_is_branch (isa, insn->opcode) != 1
+      && xtensa_opcode_is_jump (isa, insn->opcode) != 1)
     return FALSE;
 
   for (i = 0; i < num_ops; i++)
     {
     return FALSE;
 
   for (i = 0; i < num_ops; i++)
     {
-      xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
-      char *kind = xtensa_operand_kind (opnd);
-      if (strlen (kind) == 1 && *kind == 'l')
+      if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1)
        {
          target_op = i;
          break;
        {
          target_op = i;
          break;
@@ -3375,7 +3709,7 @@ is_branch_jmp_to_next (insn, fragP)
   if (target_frag == NULL)
     return FALSE;
 
   if (target_frag == NULL)
     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)
     return TRUE;
 
       && S_GET_VALUE (sym) == target_frag->fr_address)
     return TRUE;
 
@@ -3384,36 +3718,28 @@ is_branch_jmp_to_next (insn, fragP)
 
 
 static void
 
 
 static void
-xg_add_branch_and_loop_targets (insn)
-     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_num_operands (isa, insn->opcode);
+  int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
 
 
-  if (is_loop_opcode (insn->opcode))
+  if (xtensa_opcode_is_loop (isa, insn->opcode) == 1)
     {
       int i = 1;
     {
       int i = 1;
-      xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
-      char *kind = xtensa_operand_kind (opnd);
-      if (strlen (kind) == 1 && *kind == 'l')
-       if (insn->tok[i].X_op == O_symbol)
-         symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
+      if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
+         && insn->tok[i].X_op == O_symbol)
+       symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
       return;
     }
 
       return;
     }
 
-  /* Currently, we do not add branch targets.  This is an optimization
-     for later that tries to align only branch targets, not just any
-     label in a text section.  */
-
-  if (align_only_targets)
+  if (xtensa_opcode_is_branch (isa, insn->opcode) == 1
+      || xtensa_opcode_is_loop (isa, insn->opcode) == 1)
     {
       int i;
 
       for (i = 0; i < insn->ntok && i < num_ops; i++)
        {
     {
       int i;
 
       for (i = 0; i < insn->ntok && i < num_ops; i++)
        {
-         xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
-         char *kind = xtensa_operand_kind (opnd);
-         if (strlen (kind) == 1 && *kind == 'l'
+         if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
              && insn->tok[i].X_op == O_symbol)
            {
              symbolS *sym = insn->tok[i].X_add_symbol;
              && insn->tok[i].X_op == O_symbol)
            {
              symbolS *sym = insn->tok[i].X_add_symbol;
@@ -3426,102 +3752,10 @@ xg_add_branch_and_loop_targets (insn)
 }
 
 
 }
 
 
-/* Return the transition rule that matches or NULL if none matches.  */
-
-bfd_boolean
-xg_instruction_matches_rule (insn, rule)
-     TInsn *insn;
-     TransitionRule *rule;
-{
-  PreconditionList *condition_l;
-
-  if (rule->opcode != insn->opcode)
-    return FALSE;
-
-  for (condition_l = rule->conditions;
-       condition_l != NULL;
-       condition_l = condition_l->next)
-    {
-      expressionS *exp1;
-      expressionS *exp2;
-      Precondition *cond = condition_l->precond;
-
-      switch (cond->typ)
-       {
-       case OP_CONSTANT:
-         /* The expression must be the constant.  */
-         assert (cond->op_num < insn->ntok);
-         exp1 = &insn->tok[cond->op_num];
-         if (!expr_is_const (exp1))
-           return FALSE;
-         switch (cond->cmp)
-           {
-           case OP_EQUAL:
-             if (get_expr_const (exp1) != cond->op_data)
-               return FALSE;
-             break;
-           case OP_NOTEQUAL:
-             if (get_expr_const (exp1) == cond->op_data)
-               return FALSE;
-             break;
-           }
-         break;
-
-       case OP_OPERAND:
-         assert (cond->op_num < insn->ntok);
-         assert (cond->op_data < insn->ntok);
-         exp1 = &insn->tok[cond->op_num];
-         exp2 = &insn->tok[cond->op_data];
-
-         switch (cond->cmp)
-           {
-           case OP_EQUAL:
-             if (!expr_is_equal (exp1, exp2))
-               return FALSE;
-             break;
-           case OP_NOTEQUAL:
-             if (expr_is_equal (exp1, exp2))
-               return FALSE;
-             break;
-           }
-         break;
-
-       case OP_LITERAL:
-       case OP_LABEL:
-       default:
-         return FALSE;
-       }
-    }
-  return TRUE;
-}
-
-
-TransitionRule *
-xg_instruction_match (insn)
-     TInsn *insn;
-{
-  TransitionTable *table = xg_build_simplify_table ();
-  TransitionList *l;
-  assert (insn->opcode < table->num_opcodes);
-
-  /* Walk through all of the possible transitions.  */
-  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
-    {
-      TransitionRule *rule = l->rule;
-      if (xg_instruction_matches_rule (insn, rule))
-       return rule;
-    }
-  return NULL;
-}
-
-
-/* Return false if no error.  */
+/* Return FALSE if no error.  */
 
 
-bfd_boolean
-xg_build_token_insn (instr_spec, old_insn, new_insn)
-     BuildInstr *instr_spec;
-     TInsn *old_insn;
-     TInsn *new_insn;
+static bfd_boolean
+xg_build_token_insn (BuildInstr *instr_spec, TInsn *old_insn, TInsn *new_insn)
 {
   int num_ops = 0;
   BuildOp *b_op;
 {
   int num_ops = 0;
   BuildOp *b_op;
@@ -3532,11 +3766,13 @@ xg_build_token_insn (instr_spec, old_insn, new_insn)
       new_insn->insn_type = ITYPE_INSN;
       new_insn->opcode = instr_spec->opcode;
       new_insn->is_specific_opcode = FALSE;
       new_insn->insn_type = ITYPE_INSN;
       new_insn->opcode = instr_spec->opcode;
       new_insn->is_specific_opcode = FALSE;
+      new_insn->linenum = old_insn->linenum;
       break;
     case INSTR_LITERAL_DEF:
       new_insn->insn_type = ITYPE_LITERAL;
       new_insn->opcode = XTENSA_UNDEFINED;
       new_insn->is_specific_opcode = FALSE;
       break;
     case INSTR_LITERAL_DEF:
       new_insn->insn_type = ITYPE_LITERAL;
       new_insn->opcode = XTENSA_UNDEFINED;
       new_insn->is_specific_opcode = FALSE;
+      new_insn->linenum = old_insn->linenum;
       break;
     case INSTR_LABEL_DEF:
       as_bad (_("INSTR_LABEL_DEF not supported yet"));
       break;
     case INSTR_LABEL_DEF:
       as_bad (_("INSTR_LABEL_DEF not supported yet"));
@@ -3582,15 +3818,18 @@ xg_build_token_insn (instr_spec, old_insn, new_insn)
 }
 
 
 }
 
 
-/* Return true if it was simplified.  */
+/* Return TRUE if it was simplified.  */
 
 
-bfd_boolean
-xg_simplify_insn (old_insn, new_insn)
-     TInsn *old_insn;
-     TInsn *new_insn;
+static bfd_boolean
+xg_simplify_insn (TInsn *old_insn, TInsn *new_insn)
 {
 {
-  TransitionRule *rule = xg_instruction_match (old_insn);
+  TransitionRule *rule;
   BuildInstr *insn_spec;
   BuildInstr *insn_spec;
+
+  if (old_insn->is_specific_opcode || !density_supported)
+    return FALSE;
+
+  rule = xg_instruction_match (old_insn);
   if (rule == NULL)
     return FALSE;
 
   if (rule == NULL)
     return FALSE;
 
@@ -3609,28 +3848,25 @@ xg_simplify_insn (old_insn, new_insn)
 
 /* xg_expand_assembly_insn: (1) Simplify the instruction, i.e., l32i ->
    l32i.n. (2) Check the number of operands.  (3) Place the instruction
 
 /* xg_expand_assembly_insn: (1) Simplify the instruction, i.e., l32i ->
    l32i.n. (2) Check the number of operands.  (3) Place the instruction
-   tokens into the stack or if we can relax it at assembly time, place
-   multiple instructions/literals onto the stack.  Return false if no
-   error.  */
+   tokens into the stack or relax it and place multiple
+   instructions/literals onto the stack.  Return FALSE if no error.  */
 
 static bfd_boolean
 
 static bfd_boolean
-xg_expand_assembly_insn (istack, orig_insn)
-     IStack *istack;
-     TInsn *orig_insn;
+xg_expand_assembly_insn (IStack *istack, TInsn *orig_insn)
 {
   int noperands;
   TInsn new_insn;
 {
   int noperands;
   TInsn new_insn;
-  memset (&new_insn, 0, sizeof (TInsn));
+  bfd_boolean do_expand;
 
 
-  /* On return, we will be using the "use_tokens" with "use_ntok".
-     This will reduce things like addi to addi.n.  */
-  if (code_density_available () && !orig_insn->is_specific_opcode)
-    {
-      if (xg_simplify_insn (orig_insn, &new_insn))
-       orig_insn = &new_insn;
-    }
+  tinsn_init (&new_insn);
 
 
-  noperands = xtensa_num_operands (xtensa_default_isa, orig_insn->opcode);
+  /* Narrow it if we can.  xg_simplify_insn now does all the
+     appropriate checking (e.g., for the density option).  */
+  if (xg_simplify_insn (orig_insn, &new_insn))
+    orig_insn = &new_insn;
+
+  noperands = xtensa_opcode_num_operands (xtensa_default_isa,
+                                         orig_insn->opcode);
   if (orig_insn->ntok < noperands)
     {
       as_bad (_("found %d operands for '%s':  Expected %d"),
   if (orig_insn->ntok < noperands)
     {
       as_bad (_("found %d operands for '%s':  Expected %d"),
@@ -3645,98 +3881,173 @@ xg_expand_assembly_insn (istack, orig_insn)
             xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
             noperands);
 
             xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
             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;
 
-  /* Cases: 
-
-     Instructions with all constant immeds:
-     Assemble them and relax the instruction if possible.
-     Give error if not possible; no fixup needed.
-
-     Instructions with symbolic immeds:
-     Assemble them with a Fix up (that may cause instruction expansion).
-     Also close out the fragment if the fixup may cause instruction expansion. 
-     
-     There are some other special cases where we need alignment.
-     1) before certain instructions with required alignment (OPCODE_ALIGN)
-     2) before labels that have jumps (LABEL_ALIGN)
-     3) after call instructions (RETURN_ALIGN)
-        Multiple of these may be possible on the same fragment. 
-       If so, make sure to satisfy the required alignment. 
-       Then try to get the desired alignment.  */
-
   if (tinsn_has_invalid_symbolic_operands (orig_insn))
     return TRUE;
 
   if (tinsn_has_invalid_symbolic_operands (orig_insn))
     return TRUE;
 
-  if (orig_insn->is_specific_opcode || !can_relax ())
-    {
-      istack_push (istack, orig_insn);
-      return FALSE;
-    }
+  /* If the instruction will definitely need to be relaxed, it is better
+     to expand it now for better scheduling.  Decide whether to expand
+     now....  */
+  do_expand = (!orig_insn->is_specific_opcode && use_transform ());
+
+  /* Calls should be expanded to longcalls only in the backend relaxation
+     so that the assembly scheduler will keep the L32R/CALLX instructions
+     adjacent.  */
+  if (is_direct_call_opcode (orig_insn->opcode))
+    do_expand = FALSE;
 
   if (tinsn_has_symbolic_operands (orig_insn))
     {
 
   if (tinsn_has_symbolic_operands (orig_insn))
     {
-      if (tinsn_has_complex_operands (orig_insn))
-       xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
-      else
-       istack_push (istack, orig_insn);
+      /* The values of symbolic operands are not known yet, so only expand
+        now if an operand is "complex" (e.g., difference of symbols) and
+        will have to be stored as a literal regardless of the value.  */
+      if (!tinsn_has_complex_operands (orig_insn))
+       do_expand = FALSE;
     }
     }
+  else if (xg_immeds_fit (orig_insn))
+    do_expand = FALSE;
+
+  if (do_expand)
+    xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
   else
   else
-    {
-      if (xg_immeds_fit (orig_insn))
-       istack_push (istack, orig_insn);
-      else
-       xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
-    }
+    istack_push (istack, orig_insn);
 
 
-#if 0
-  for (i = 0; i < istack->ninsn; i++)
+  return FALSE;
+}
+
+
+/* Return TRUE if the section flags are marked linkonce
+   or the name is .gnu.linkonce.*.  */
+
+static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
+
+static bfd_boolean
+get_is_linkonce_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec)
+{
+  flagword flags, link_once_flags;
+
+  flags = bfd_get_section_flags (abfd, sec);
+  link_once_flags = (flags & SEC_LINK_ONCE);
+
+  /* Flags might not be set yet.  */
+  if (!link_once_flags
+      && strncmp (segment_name (sec), ".gnu.linkonce.", linkonce_len) == 0)
+    link_once_flags = SEC_LINK_ONCE;
+
+  return (link_once_flags != 0);
+}
+
+
+static void
+xtensa_add_literal_sym (symbolS *sym)
+{
+  sym_list *l;
+
+  l = (sym_list *) xmalloc (sizeof (sym_list));
+  l->sym = sym;
+  l->next = literal_syms;
+  literal_syms = l;
+}
+
+
+static symbolS *
+xtensa_create_literal_symbol (segT sec, fragS *frag)
+{
+  static int lit_num = 0;
+  static char name[256];
+  symbolS *symbolP;
+
+  sprintf (name, ".L_lit_sym%d", lit_num);
+
+  /* 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
+     symbol will be in the output file.  */
+  if (get_is_linkonce_section (stdoutput, sec))
     {
     {
-      if (xg_simplify_insn (&new_insn, &istack->insn[i]))
-       istack->insn[i] = new_insn;
+      symbolP = symbol_new (name, sec, 0, frag);
+      S_CLEAR_EXTERNAL (symbolP);
+      /* symbolP->local = 1; */
     }
     }
-#endif
+  else
+    symbolP = symbol_new (name, sec, 0, frag);
 
 
-  return FALSE;
+  xtensa_add_literal_sym (symbolP);
+
+  lit_num++;
+  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.  */
 
-symbolS *
-xg_assemble_literal (insn)
-     /* const */ TInsn *insn;
+static symbolS *
+xg_assemble_literal (/* const */ TInsn *insn)
 {
   emit_state state;
   symbolS *lit_sym = NULL;
 {
   emit_state state;
   symbolS *lit_sym = NULL;
+  bfd_reloc_code_real_type reloc;
+  char *p;
 
   /* size = 4 for L32R.  It could easily be larger when we move to
      larger constants.  Add a parameter later.  */
   offsetT litsize = 4;
   offsetT litalign = 2;                /* 2^2 = 4 */
   expressionS saved_loc;
 
   /* size = 4 for L32R.  It could easily be larger when we move to
      larger constants.  Add a parameter later.  */
   offsetT litsize = 4;
   offsetT litalign = 2;                /* 2^2 = 4 */
   expressionS saved_loc;
+  expressionS * emit_val;
+
   set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
 
   assert (insn->insn_type == ITYPE_LITERAL);
   set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
 
   assert (insn->insn_type == ITYPE_LITERAL);
-  assert (insn->ntok = 1);     /* must be only one token here */
+  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];
+  if (emit_val->X_op == O_big)
+    {
+      int size = emit_val->X_add_number * CHARS_PER_LITTLENUM;
+      if (size > litsize)
+       {
+         /* This happens when someone writes a "movi a2, big_number".  */
+         as_bad_where (frag_now->fr_file, frag_now->fr_line,
+                       _("invalid immediate"));
+         xtensa_restore_emit_state (&state);
+         return NULL;
+       }
+    }
+
   /* 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
      important for the way text section literals work.  */
   frag_align (litalign, 0, 0);
   /* 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
      important for the way text section literals work.  */
   frag_align (litalign, 0, 0);
+  record_alignment (now_seg, litalign);
+
+  switch (emit_val->X_op)
+    {
+    case O_pltrel:
+      p = frag_more (litsize);
+      xtensa_set_frag_assembly_state (frag_now);
+      reloc = map_operator_to_reloc (emit_val->X_op);
+      if (emit_val->X_add_symbol)
+       emit_val->X_op = O_symbol;
+      else
+       emit_val->X_op = O_constant;
+      fix_new_exp (frag_now, p - frag_now->fr_literal,
+                  litsize, emit_val, 0, reloc);
+      break;
 
 
-  emit_expr (&insn->tok[0], litsize);
+    default:
+      emit_expr (emit_val, litsize);
+      break;
+    }
 
   assert (frag_now->tc_frag_data.literal_frag == NULL);
   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);
   lit_sym = frag_now->fr_symbol;
 
   assert (frag_now->tc_frag_data.literal_frag == NULL);
   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);
   lit_sym = frag_now->fr_symbol;
-  frag_now->tc_frag_data.is_literal = TRUE;
 
   /* Go back.  */
   xtensa_restore_emit_state (&state);
 
   /* Go back.  */
   xtensa_restore_emit_state (&state);
@@ -3745,324 +4056,156 @@ xg_assemble_literal (insn)
 
 
 static void
 
 
 static void
-xg_assemble_literal_space (size)
-     /* const */ int size;
+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.  */
   offsetT litalign = 2;                /* 2^2 = 4 */
   fragS *lit_saved_frag;
 
      takes effect if something is placed here.  */
   offsetT litalign = 2;                /* 2^2 = 4 */
   fragS *lit_saved_frag;
 
-  expressionS saved_loc;
-
   assert (size % 4 == 0);
   assert (size % 4 == 0);
-  set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
 
   xtensa_switch_to_literal_fragment (&state);
 
   /* Force a 4-byte align here.  */
   frag_align (litalign, 0, 0);
 
   xtensa_switch_to_literal_fragment (&state);
 
   /* Force a 4-byte align here.  */
   frag_align (litalign, 0, 0);
+  record_alignment (now_seg, litalign);
 
   xg_force_frag_space (size);
 
   lit_saved_frag = frag_now;
   frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
 
   xg_force_frag_space (size);
 
   lit_saved_frag = frag_now;
   frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
-  frag_now->tc_frag_data.is_literal = TRUE;
   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, size, FALSE);
+  xg_finish_frag (0, RELAX_LITERAL, 0, size, FALSE);
 
   /* Go back.  */
   xtensa_restore_emit_state (&state);
 
   /* Go back.  */
   xtensa_restore_emit_state (&state);
-  frag_now->tc_frag_data.literal_frag = lit_saved_frag;
+  frag_now->tc_frag_data.literal_frags[slot] = lit_saved_frag;
 }
 
 
 }
 
 
-symbolS *
-xtensa_create_literal_symbol (sec, frag)
-     segT sec;
-     fragS *frag;
+/* Put in a fixup record based on the opcode.
+   Return TRUE on success.  */
+
+static bfd_boolean
+xg_add_opcode_fix (TInsn *tinsn,
+                  int opnum,
+                  xtensa_format fmt,
+                  int slot,
+                  expressionS *expr,
+                  fragS *fragP,
+                  offsetT offset)
 {
 {
-  static int lit_num = 0;
-  static char name[256];
-  symbolS *symbolP;
+  xtensa_opcode opcode = tinsn->opcode;
+  bfd_reloc_code_real_type reloc;
+  reloc_howto_type *howto;
+  int fmt_length;
+  fixS *the_fix;
 
 
-  sprintf (name, ".L_lit_sym%d", lit_num);
+  reloc = BFD_RELOC_NONE;
 
 
-  /* 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
-     symbol will be in the output file.  */
-  if (get_is_linkonce_section (stdoutput, sec))
+  /* First try the special cases for "alternate" relocs.  */
+  if (opcode == xtensa_l32r_opcode)
     {
     {
-      symbolP = symbol_new (name, sec, 0, frag);
-      S_CLEAR_EXTERNAL (symbolP);
-      /* symbolP->local = 1; */
+      if (fragP->tc_frag_data.use_absolute_literals)
+       reloc = encode_alt_reloc (slot);
     }
     }
-  else
-    symbolP = symbol_new (name, sec, 0, frag);
-
-  xtensa_add_literal_sym (symbolP);
-
-  frag->tc_frag_data.is_literal = TRUE;
-  lit_num++;
-  return symbolP;
-}
-
-
-static void
-xtensa_add_literal_sym (sym)
-     symbolS *sym;
-{
-  sym_list *l;
-
-  l = (sym_list *) xmalloc (sizeof (sym_list));
-  l->sym = sym;
-  l->next = literal_syms;
-  literal_syms = l;
-}
-
-
-static void
-xtensa_add_insn_label (sym)
-     symbolS *sym;
-{
-  sym_list *l;
-
-  if (!free_insn_labels)
-    l = (sym_list *) xmalloc (sizeof (sym_list));
-  else
+  else if (opcode == xtensa_const16_opcode)
     {
     {
-      l = free_insn_labels;
-      free_insn_labels = l->next;
+      if (expr->X_op == O_lo16)
+       {
+         reloc = encode_reloc (slot);
+         expr->X_op = O_symbol;
+       }
+      else if (expr->X_op == O_hi16)
+       {
+         reloc = encode_alt_reloc (slot);
+         expr->X_op = O_symbol;
+       }
     }
 
     }
 
-  l->sym = sym;
-  l->next = insn_labels;
-  insn_labels = l;
-}
-
-
-static void
-xtensa_clear_insn_labels (void)
-{
-  sym_list **pl;
-
-  for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
-    ;
-  *pl = insn_labels;
-  insn_labels = NULL;
-}
-
-
-/* Return true if the section flags are marked linkonce
-   or the name is .gnu.linkonce*.  */
-
-bfd_boolean
-get_is_linkonce_section (abfd, sec)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     segT sec;
-{
-  flagword flags, link_once_flags;
-
-  flags = bfd_get_section_flags (abfd, sec);
-  link_once_flags = (flags & SEC_LINK_ONCE);
-
-  /* Flags might not be set yet.  */
-  if (!link_once_flags)
+  if (opnum != get_relaxable_immed (opcode))
     {
     {
-      static size_t len = sizeof ".gnu.linkonce.t.";
-
-      if (strncmp (segment_name (sec), ".gnu.linkonce.t.", len - 1) == 0)
-       link_once_flags = SEC_LINK_ONCE;
+      as_bad (_("invalid relocation for operand %i of '%s'"),
+             opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
+      return FALSE;
     }
     }
-  return (link_once_flags != 0);
-}
 
 
-
-/* Emit an instruction to the current fragment.  If record_fix is true,
-   then this instruction will not change and we can go ahead and record
-   the fixup.  If record_fix is false, then the instruction may change
-   and we are going to close out this fragment.  Go ahead and set the
-   fr_symbol and fr_offset instead of adding a fixup.  */
-
-static bfd_boolean
-xg_emit_insn (t_insn, record_fix)
-     TInsn *t_insn;
-     bfd_boolean record_fix;
-{
-  bfd_boolean ok = TRUE;
-  xtensa_isa isa = xtensa_default_isa;
-  xtensa_opcode opcode = t_insn->opcode;
-  bfd_boolean has_fixup = FALSE;
-  int noperands;
-  int i, byte_count;
-  fragS *oldfrag;
-  size_t old_size;
-  char *f;
-  static xtensa_insnbuf insnbuf = NULL;
-  
-  /* Use a static pointer to the insn buffer so we don't have to call 
-     malloc each time through.  */
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
-  has_fixup = tinsn_to_insnbuf (t_insn, insnbuf);
-
-  noperands = xtensa_num_operands (isa, opcode);
-  assert (noperands == t_insn->ntok);
-
-  byte_count = xtensa_insn_length (isa, opcode);
-  oldfrag = frag_now;
-  /* This should NEVER cause us to jump into a new frag;
-     we've already reserved space.  */
-  old_size = frag_now_fix ();
-  f = frag_more (byte_count);
-  assert (oldfrag == frag_now);
-
-  /* This needs to generate a record that lists the parts that are
-     instructions.  */
-  if (!frag_now->tc_frag_data.is_insn)
+  /* Handle erroneous "@h" and "@l" expressions here before they propagate
+     into the symbol table where the generic portions of the assembler
+     won't know what to do with them.  */
+  if (expr->X_op == O_lo16 || expr->X_op == O_hi16)
     {
     {
-      /* If we are at the beginning of a fragment, switch this
-        fragment to an instruction fragment.  */
-      if (now_seg != absolute_section && old_size != 0)
-       as_warn (_("instruction fragment may contain data"));
-      frag_now->tc_frag_data.is_insn = TRUE;
+      as_bad (_("invalid expression for operand %i of '%s'"),
+             opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
+      return FALSE;
     }
 
     }
 
-  xtensa_insnbuf_to_chars (isa, insnbuf, f);
-
-  dwarf2_emit_insn (byte_count);
-
-  /* Now spit out the opcode fixup.... */
-  if (!has_fixup)
-    return !ok;
-
-  for (i = 0; i < noperands; ++i)
+  /* Next try the generic relocs.  */
+  if (reloc == BFD_RELOC_NONE)
+    reloc = encode_reloc (slot);
+  if (reloc == BFD_RELOC_NONE)
     {
     {
-      expressionS *expr = &t_insn->tok[i];
-      switch (expr->X_op)
-       {
-       case O_symbol:
-         if (get_relaxable_immed (opcode) == i)
-           {
-             if (record_fix)
-               {
-                 if (!xg_add_opcode_fix (opcode, i, expr, frag_now,
-                                         f - frag_now->fr_literal))
-                   ok = FALSE;
-               }
-             else
-               {
-                 /* Write it to the fr_offset, fr_symbol.  */
-                 frag_now->fr_symbol = expr->X_add_symbol;
-                 frag_now->fr_offset = expr->X_add_number;
-               }
-           }
-         else
-           {
-             as_bad (_("invalid operand %d on '%s'"),
-                     i, xtensa_opcode_name (isa, opcode));
-             ok = FALSE;
-           }
-         break;
-
-       case O_constant:
-       case O_register:
-         break;
+      as_bad (_("invalid relocation in instruction slot %i"), slot);
+      return FALSE;
+    }
 
 
-       default:
-         as_bad (_("invalid expression for operand %d on '%s'"),
-                 i, xtensa_opcode_name (isa, opcode));
-         ok = FALSE;
-         break;
-       }
+  howto = bfd_reloc_type_lookup (stdoutput, reloc);
+  if (!howto)
+    {
+      as_bad (_("undefined symbol for opcode \"%s\""),
+             xtensa_opcode_name (xtensa_default_isa, opcode));
+      return FALSE;
     }
 
     }
 
-  return !ok;
+  fmt_length = xtensa_format_length (xtensa_default_isa, fmt);
+  the_fix = fix_new_exp (fragP, offset, fmt_length, expr,
+                        howto->pc_relative, reloc);
+  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_number = expr->X_add_number;
+  the_fix->tc_fix_data.slot = slot;
+
+  return TRUE;
 }
 
 
 static bfd_boolean
 }
 
 
 static bfd_boolean
-xg_emit_insn_to_buf (t_insn, buf, fragP, offset, build_fix)
-     TInsn *t_insn;
-     char *buf;
-     fragS *fragP;
-     offsetT offset;
-     bfd_boolean build_fix;
+xg_emit_insn_to_buf (TInsn *tinsn,
+                    char *buf,
+                    fragS *fragP,
+                    offsetT offset,
+                    bfd_boolean build_fix)
 {
   static xtensa_insnbuf insnbuf = NULL;
   bfd_boolean has_symbolic_immed = FALSE;
   bfd_boolean ok = TRUE;
 {
   static xtensa_insnbuf insnbuf = NULL;
   bfd_boolean has_symbolic_immed = FALSE;
   bfd_boolean ok = TRUE;
+
   if (!insnbuf)
     insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
 
   if (!insnbuf)
     insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
 
-  has_symbolic_immed = tinsn_to_insnbuf (t_insn, insnbuf);
+  has_symbolic_immed = tinsn_to_insnbuf (tinsn, insnbuf);
   if (has_symbolic_immed && build_fix)
     {
       /* Add a fixup.  */
   if (has_symbolic_immed && build_fix)
     {
       /* Add a fixup.  */
-      int opnum = get_relaxable_immed (t_insn->opcode);
-      expressionS *exp = &t_insn->tok[opnum];
+      xtensa_format fmt = xg_get_single_format (tinsn->opcode);
+      int slot = xg_get_single_slot (tinsn->opcode);
+      int opnum = get_relaxable_immed (tinsn->opcode);
+      expressionS *exp = &tinsn->tok[opnum];
 
 
-      if (!xg_add_opcode_fix (t_insn->opcode, 
-                             opnum, exp, fragP, offset))
+      if (!xg_add_opcode_fix (tinsn, opnum, fmt, slot, exp, fragP, offset))
        ok = FALSE;
     }
   fragP->tc_frag_data.is_insn = TRUE;
        ok = FALSE;
     }
   fragP->tc_frag_data.is_insn = TRUE;
-  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
+  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
+                          (unsigned char *) buf, 0);
   return ok;
 }
 
 
   return ok;
 }
 
 
-/* Put in a fixup record based on the opcode.
-   Return true on success.  */
-
-bfd_boolean
-xg_add_opcode_fix (opcode, opnum, expr, fragP, offset)
-     xtensa_opcode opcode;
-     int opnum;
-     expressionS *expr;
-     fragS *fragP;
-     offsetT offset;
-{ 
-  bfd_reloc_code_real_type reloc; 
-  reloc_howto_type *howto; 
-  int insn_length;
-  fixS *the_fix;
-
-  reloc = opnum_to_reloc (opnum);
-  if (reloc == BFD_RELOC_NONE)
-    {
-      as_bad (_("invalid relocation operand %i on '%s'"),
-             opnum, xtensa_opcode_name (xtensa_default_isa, opcode));
-      return FALSE;
-    }
-
-  howto = bfd_reloc_type_lookup (stdoutput, reloc);
-
-  if (!howto)
-    {
-      as_bad (_("undefined symbol for opcode \"%s\"."),
-             xtensa_opcode_name (xtensa_default_isa, opcode));
-      return FALSE;
-    }
-
-  insn_length = xtensa_insn_length (xtensa_default_isa, opcode);
-  the_fix = fix_new_exp (fragP, offset, insn_length, expr,
-                        howto->pc_relative, reloc);
-
-  if (expr->X_add_symbol && 
-      (S_IS_EXTERNAL (expr->X_add_symbol) || S_IS_WEAK (expr->X_add_symbol)))
-    the_fix->fx_plt = TRUE;
-  
-  return TRUE;
-}
-
-
-void
-xg_resolve_literals (insn, lit_sym)
-     TInsn *insn;
-     symbolS *lit_sym;
+static void
+xg_resolve_literals (TInsn *insn, symbolS *lit_sym)
 {
   symbolS *sym = get_special_literal_symbol ();
   int i;
 {
   symbolS *sym = get_special_literal_symbol ();
   int i;
@@ -4076,14 +4219,11 @@ xg_resolve_literals (insn, lit_sym)
 }
 
 
 }
 
 
-void
-xg_resolve_labels (insn, label_sym)
-     TInsn *insn;
-     symbolS *label_sym;
+static void
+xg_resolve_labels (TInsn *insn, symbolS *label_sym)
 {
   symbolS *sym = get_special_label_symbol ();
   int i;
 {
   symbolS *sym = get_special_label_symbol ();
   int i;
-  /* assert(!insn->is_literal); */
   for (i = 0; i < insn->ntok; i++)
     if (insn->tok[i].X_add_symbol == sym)
       insn->tok[i].X_add_symbol = label_sym;
   for (i = 0; i < insn->ntok; i++)
     if (insn->tok[i].X_add_symbol == sym)
       insn->tok[i].X_add_symbol = label_sym;
@@ -4091,179 +4231,28 @@ xg_resolve_labels (insn, label_sym)
 }
 
 
 }
 
 
-static void
-xg_assemble_tokens (insn)
-     /*const */ TInsn *insn;
-{
-  /* By the time we get here, there's not too much left to do. 
-     1) Check our assumptions. 
-     2) Check if the current instruction is "narrow". 
-        If so, then finish the frag, create another one.
-        We could also go back to change some previous
-        "narrow" frags into no-change ones if we have more than
-        MAX_NARROW_ALIGNMENT of them without alignment restrictions
-        between them.
-
-     Cases:
-        1) It has constant operands and doesn't fit.
-           Go ahead and assemble it so it will fail.
-        2) It has constant operands that fit.
-           If narrow and !is_specific_opcode,
-              assemble it and put in a relocation
-           else
-              assemble it.
-        3) It has a symbolic immediate operand
-           a) Find the worst-case relaxation required
-           b) Find the worst-case literal pool space required.
-              Insert appropriate alignment & space in the literal.
-              Assemble it.
-              Add the relocation.  */
+/* Return TRUE if the instruction can write to the specified
+   integer register.  */
 
 
-  assert (insn->insn_type == ITYPE_INSN);
+static bfd_boolean
+is_register_writer (const TInsn *insn, const char *regset, int regnum)
+{
+  int i;
+  int num_ops;
+  xtensa_isa isa = xtensa_default_isa;
 
 
-  if (!tinsn_has_symbolic_operands (insn))
+  num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
+
+  for (i = 0; i < num_ops; i++)
     {
     {
-      if (xg_is_narrow_insn (insn) && !insn->is_specific_opcode)
+      char inout;
+      inout = xtensa_operand_inout (isa, insn->opcode, i);
+      if ((inout == 'o' || inout == 'm')
+         && xtensa_operand_is_register (isa, insn->opcode, i) == 1)
        {
        {
-         /* assemble it but add max required space */
-         int max_size = xg_get_max_narrow_insn_size (insn->opcode);
-         int min_size = xg_get_insn_size (insn);
-         char *last_insn;
-         assert (max_size == 3);
-         /* make sure we have enough space to widen it */
-         xg_force_frag_space (max_size);
-         /* Output the instruction.  It may cause an error if some 
-            operands do not fit.  */
-         last_insn = frag_more (0);
-         if (xg_emit_insn (insn, TRUE))
-           as_warn (_("instruction with constant operands does not fit"));
-         xg_finish_frag (last_insn, RELAX_NARROW, max_size - min_size, TRUE);
-       }
-      else
-       {
-         /* Assemble it.  No relocation needed.  */
-         int max_size = xg_get_insn_size (insn);
-         xg_force_frag_space (max_size);
-         if (xg_emit_insn (insn, FALSE))
-           as_warn (_("instruction with constant operands does not "
-                      "fit without widening"));
-         /* frag_more (max_size); */
-
-         /* Special case for jx.  If the jx is the next to last
-            instruction in a loop, we will add a NOP after it.  This
-            avoids a hardware issue that could occur if the jx jumped
-            to the next instruction.  */
-         if (software_avoid_b_j_loop_end
-             && is_jx_opcode (insn->opcode))
-           {
-             maybe_has_b_j_loop_end = TRUE;
-             /* add 2 of these */
-             frag_now->tc_frag_data.is_insn = TRUE;
-             frag_var (rs_machine_dependent, 4, 4,
-                       RELAX_ADD_NOP_IF_PRE_LOOP_END,
-                       frag_now->fr_symbol, frag_now->fr_offset, NULL);
-           }
-       }
-    }
-  else
-    {
-      /* Need to assemble it with space for the relocation.  */
-      if (!insn->is_specific_opcode)
-       {
-         /* Assemble it but add max required space.  */
-         char *last_insn;
-         int min_size = xg_get_insn_size (insn);
-         int max_size = xg_get_max_insn_widen_size (insn->opcode);
-         int max_literal_size =
-           xg_get_max_insn_widen_literal_size (insn->opcode);
-
-#if 0
-         symbolS *immed_sym = xg_get_insn_immed_symbol (insn);
-         set_frag_segment (frag_now, now_seg);
-#endif /* 0 */
-
-         /* Make sure we have enough space to widen the instruction. 
-            This may open a new fragment.  */
-         xg_force_frag_space (max_size);
-         if (max_literal_size != 0)
-           xg_assemble_literal_space (max_literal_size);
-
-         /* Output the instruction.  It may cause an error if some 
-            operands do not fit.  Emit the incomplete instruction.  */
-         last_insn = frag_more (0);
-         xg_emit_insn (insn, FALSE);
-
-         xg_finish_frag (last_insn, RELAX_IMMED, max_size - min_size, TRUE);
-
-         /* Special cases for loops:
-            close_loop_end should be inserted AFTER short_loop.
-            Make sure that CLOSE loops are processed BEFORE short_loops
-            when converting them.  */
-
-         /* "short_loop": add a NOP if the loop is < 4 bytes.  */
-         if (software_avoid_short_loop
-             && is_loop_opcode (insn->opcode))
-           {
-             maybe_has_short_loop = TRUE;
-             frag_now->tc_frag_data.is_insn = TRUE;
-             frag_var (rs_machine_dependent, 4, 4,
-                       RELAX_ADD_NOP_IF_SHORT_LOOP,
-                       frag_now->fr_symbol, frag_now->fr_offset, NULL);
-             frag_now->tc_frag_data.is_insn = TRUE;
-             frag_var (rs_machine_dependent, 4, 4,
-                       RELAX_ADD_NOP_IF_SHORT_LOOP,
-                       frag_now->fr_symbol, frag_now->fr_offset, NULL);
-           }
-
-         /* "close_loop_end": Add up to 12 bytes of NOPs to keep a
-            loop at least 12 bytes away from another loop's loop
-            end.  */
-         if (software_avoid_close_loop_end
-             && is_loop_opcode (insn->opcode))
-           {
-             maybe_has_close_loop_end = TRUE;
-             frag_now->tc_frag_data.is_insn = TRUE;
-             frag_var (rs_machine_dependent, 12, 12,
-                       RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
-                       frag_now->fr_symbol, frag_now->fr_offset, NULL);
-           }
-       }
-      else
-       {
-         /* Assemble it in place.  No expansion will be required, 
-            but we'll still need a relocation record.  */
-         int max_size = xg_get_insn_size (insn);
-         xg_force_frag_space (max_size);
-         if (xg_emit_insn (insn, TRUE))
-           as_warn (_("instruction's constant operands do not fit"));
-       }
-    }
-}
-
-
-/* Return true if the instruction can write to the specified
-   integer register.  */
-
-static bfd_boolean
-is_register_writer (insn, regset, regnum)
-     const TInsn *insn;
-     const char *regset;
-     int regnum;
-{
-  int i;
-  int num_ops;
-  xtensa_isa isa = xtensa_default_isa;
-
-  num_ops = xtensa_num_operands (isa, insn->opcode);
-
-  for (i = 0; i < num_ops; i++)
-    {
-      xtensa_operand operand = xtensa_get_operand (isa, insn->opcode, i);
-      char inout = xtensa_operand_inout (operand);
-
-      if (inout == '>' || inout == '=')
-       {
-         if (strcmp (xtensa_operand_kind (operand), regset) == 0)
+         xtensa_regfile opnd_rf =
+           xtensa_operand_regfile (isa, insn->opcode, i);
+         if (!strcmp (xtensa_regfile_shortname (isa, opnd_rf), regset))
            {
              if ((insn->tok[i].X_op == O_register)
                  && (insn->tok[i].X_add_number == regnum))
            {
              if ((insn->tok[i].X_op == O_register)
                  && (insn->tok[i].X_add_number == regnum))
@@ -4276,8 +4265,7 @@ is_register_writer (insn, regset, regnum)
 
 
 static bfd_boolean
 
 
 static bfd_boolean
-is_bad_loopend_opcode (tinsn)
-     const TInsn * tinsn;
+is_bad_loopend_opcode (const TInsn *tinsn)
 {
   xtensa_opcode opcode = tinsn->opcode;
 
 {
   xtensa_opcode opcode = tinsn->opcode;
 
@@ -4297,14 +4285,8 @@ is_bad_loopend_opcode (tinsn)
       || opcode == xtensa_ret_n_opcode
       || opcode == xtensa_retw_opcode
       || opcode == xtensa_retw_n_opcode
       || opcode == xtensa_ret_n_opcode
       || opcode == xtensa_retw_opcode
       || opcode == xtensa_retw_n_opcode
-      || opcode == xtensa_waiti_opcode)
-    return TRUE;
-  
-  /* An RSR of LCOUNT is illegal as the last opcode in a loop.  */
-  if (opcode == xtensa_rsr_opcode
-      && tinsn->ntok >= 2
-      && tinsn->tok[1].X_op == O_constant
-      && tinsn->tok[1].X_add_number == 2)
+      || opcode == xtensa_waiti_opcode
+      || opcode == xtensa_rsr_lcount_opcode)
     return TRUE;
 
   return FALSE;
     return TRUE;
 
   return FALSE;
@@ -4316,9 +4298,8 @@ is_bad_loopend_opcode (tinsn)
    Also, the assembler generates stabs labels that need
    not be aligned:  FAKE_LABEL_NAME . {"F", "L", "endfunc"}.  */
 
    Also, the assembler generates stabs labels that need
    not be aligned:  FAKE_LABEL_NAME . {"F", "L", "endfunc"}.  */
 
-bfd_boolean
-is_unaligned_label (sym) 
-     symbolS *sym; 
+static bfd_boolean
+is_unaligned_label (symbolS *sym)
 {
   const char *name = S_GET_NAME (sym);
   static size_t fake_size = 0;
 {
   const char *name = S_GET_NAME (sym);
   static size_t fake_size = 0;
@@ -4332,7 +4313,7 @@ is_unaligned_label (sym)
   if (fake_size == 0)
     fake_size = strlen (FAKE_LABEL_NAME);
 
   if (fake_size == 0)
     fake_size = strlen (FAKE_LABEL_NAME);
 
-  if (name 
+  if (name
       && strncmp (FAKE_LABEL_NAME, name, fake_size) == 0
       && (name[fake_size] == 'F'
          || name[fake_size] == 'L'
       && strncmp (FAKE_LABEL_NAME, name, fake_size) == 0
       && (name[fake_size] == 'F'
          || name[fake_size] == 'L'
@@ -4344,13 +4325,12 @@ is_unaligned_label (sym)
 }
 
 
 }
 
 
-fragS *
-next_non_empty_frag (fragP)
-     const fragS *fragP;
+static fragS *
+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.  */
   while (next_fragP && next_fragP->fr_fix == 0)
     next_fragP = next_fragP->fr_next;
      So we have to skip until we find something legit.  */
   while (next_fragP && next_fragP->fr_fix == 0)
     next_fragP = next_fragP->fr_next;
@@ -4362,50 +4342,107 @@ next_non_empty_frag (fragP)
 }
 
 
 }
 
 
-xtensa_opcode
-next_frag_opcode (fragP)
-     const fragS * fragP;
+static bfd_boolean
+next_frag_opcode_is_loop (const fragS *fragP, xtensa_opcode *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)
+    return FALSE;
+
+  out_opcode = get_opcode_from_buf (next_fragP->fr_literal, 0);
+  if (xtensa_opcode_is_loop (xtensa_default_isa, out_opcode) == 1)
+    {
+      *opcode = out_opcode;
+      return TRUE;
+    }
+  return FALSE;
+}
+
+
+static int
+frag_format_size (const fragS *fragP)
+{
   static xtensa_insnbuf insnbuf = NULL;
   xtensa_isa isa = xtensa_default_isa;
   static xtensa_insnbuf insnbuf = NULL;
   xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt;
+  int fmt_size;
 
   if (!insnbuf)
     insnbuf = xtensa_insnbuf_alloc (isa);
 
 
   if (!insnbuf)
     insnbuf = xtensa_insnbuf_alloc (isa);
 
-  if (next_fragP == NULL)
+  if (fragP == NULL)
+    return XTENSA_UNDEFINED;
+
+  xtensa_insnbuf_from_chars (isa, insnbuf,
+                            (unsigned char *) fragP->fr_literal, 0);
+
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (fmt == XTENSA_UNDEFINED)
     return XTENSA_UNDEFINED;
     return XTENSA_UNDEFINED;
+  fmt_size = xtensa_format_length (isa, fmt);
+
+  /* If the next format won't be changing due to relaxation, just
+     return the length of the first format.  */
+  if (fragP->fr_opcode != fragP->fr_literal)
+    return fmt_size;
+
+  /* If during relaxation we have to pull an instruction out of a
+     multi-slot instruction, we will return the more conservative
+     number.  This works because alignment on bigger instructions
+     is more restrictive than alignment on smaller instructions.
+     This is more conservative than we would like, but it happens
+     infrequently.  */
+
+  if (xtensa_format_num_slots (xtensa_default_isa, fmt) > 1)
+    return fmt_size;
+
+  /* If we aren't doing one of our own relaxations or it isn't
+     slot-based, then the insn size won't change.  */
+  if (fragP->fr_type != rs_machine_dependent)
+    return fmt_size;
+  if (fragP->fr_subtype != RELAX_SLOTS)
+    return fmt_size;
+
+  /* If an instruction is about to grow, return the longer size.  */
+  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_STEP3)
+    return 3;
+
+  if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+    return 2 + fragP->tc_frag_data.text_expansion[0];
 
 
-  xtensa_insnbuf_from_chars (isa, insnbuf, next_fragP->fr_literal);
-  return xtensa_decode_insn (isa, insnbuf);
+  return fmt_size;
+}
+
+
+static int
+next_frag_format_size (const fragS *fragP)
+{
+  const fragS *next_fragP = next_non_empty_frag (fragP);
+  return frag_format_size (next_fragP);
 }
 
 
 }
 
 
-/* Return true if the target frag is one of the next non-empty frags.  */
+/* In early Xtensa Processors, for reasons that are unclear, the ISA
+   required two-byte instructions to be treated as three-byte instructions
+   for loop instruction alignment.  This restriction was removed beginning
+   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
+   does not cross a fetch boundary.  */
 
 
-bfd_boolean
-is_next_frag_target (fragP, target)
-     const fragS *fragP;
-     const fragS *target;
+static int
+get_loop_align_size (int insn_size)
 {
 {
-  if (fragP == NULL)
-    return FALSE;
+  if (insn_size == XTENSA_UNDEFINED)
+    return xtensa_fetch_width;
 
 
-  for (; fragP; fragP = fragP->fr_next)
-    {
-      if (fragP == target)
-       return TRUE;
-      if (fragP->fr_fix != 0)
-       return FALSE;
-      if (fragP->fr_type == rs_fill && fragP->fr_offset != 0)
-       return FALSE;
-      if ((fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
-         && ((fragP->fr_address % (1 << fragP->fr_offset)) != 0))
-       return FALSE;
-      if (fragP->fr_type == rs_space)
-       return FALSE;
-    }
-  return FALSE;
+  if (enforce_three_byte_loop_align && insn_size == 2)
+    return 3;
+
+  return insn_size;
 }
 
 
 }
 
 
@@ -4413,10 +4450,34 @@ is_next_frag_target (fragP, target)
    switch its state so it will instantiate a NOP.  */
 
 static void
    switch its state so it will instantiate a NOP.  */
 
 static void
-update_next_frag_nop_state (fragP)
-     fragS *fragP;
+update_next_frag_state (fragS *fragP)
 {
   fragS *next_fragP = fragP->fr_next;
 {
   fragS *next_fragP = fragP->fr_next;
+  fragS *new_target = NULL;
+
+  if (align_targets)
+    {
+      /* We are guaranteed there will be one of these...   */
+      while (!(next_fragP->fr_type == rs_machine_dependent
+              && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
+                  || next_fragP->fr_subtype == RELAX_UNREACHABLE)))
+       next_fragP = next_fragP->fr_next;
+
+      assert (next_fragP->fr_type == rs_machine_dependent
+             && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
+                 || next_fragP->fr_subtype == RELAX_UNREACHABLE));
+
+      /* ...and one of these.  */
+      new_target = next_fragP->fr_next;
+      while (!(new_target->fr_type == rs_machine_dependent
+              && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
+                  || new_target->fr_subtype == RELAX_DESIRE_ALIGN)))
+       new_target = new_target->fr_next;
+
+      assert (new_target->fr_type == rs_machine_dependent
+             && (new_target->fr_subtype == RELAX_MAYBE_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)
     {
@@ -4426,16 +4487,16 @@ update_next_frag_nop_state (fragP)
          next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP;
          return;
        }
          next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP;
          return;
        }
+
       next_fragP = next_fragP->fr_next;
     }
 }
 
 
 static bfd_boolean
       next_fragP = next_fragP->fr_next;
     }
 }
 
 
 static bfd_boolean
-next_frag_is_branch_target (fragP)
-     const fragS *fragP;
+next_frag_is_branch_target (const fragS *fragP)
 {
 {
-  /* Sometimes an empty will end up here due 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.  */
   for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
     {
      so we have to skip until we find something legit.  */
   for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
     {
@@ -4449,10 +4510,9 @@ next_frag_is_branch_target (fragP)
 
 
 static bfd_boolean
 
 
 static bfd_boolean
-next_frag_is_loop_target (fragP)
-     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. */
   for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
     {
      So we have to skip until we find something legit. */
   for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
     {
@@ -4466,17 +4526,16 @@ next_frag_is_loop_target (fragP)
 
 
 static addressT
 
 
 static addressT
-next_frag_pre_opcode_bytes (fragp)
-     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 = next_frag_opcode (fragp);
-  if (!is_loop_opcode (next_opcode))
+  if (!next_frag_opcode_is_loop (fragp, &next_opcode))
     return 0;
 
     return 0;
 
-  /* Sometimes an empty will end up here due storage allocation issues.
-     So we have to skip until we find something legit.  */
+  /* Sometimes an empty will end up here due to storage allocation issues,
+     so we have to skip until we find something legit.  */
   while (next_fragp->fr_fix == 0)
     next_fragp = next_fragp->fr_next;
 
   while (next_fragp->fr_fix == 0)
     next_fragp = next_fragp->fr_next;
 
@@ -4485,8 +4544,9 @@ next_frag_pre_opcode_bytes (fragp)
 
   /* There is some implicit knowledge encoded in here.
      The LOOP instructions that are NOT RELAX_IMMED have
 
   /* There is some implicit knowledge encoded in here.
      The LOOP instructions that are NOT RELAX_IMMED have
-     been relaxed.  */
-  if (next_fragp->fr_subtype > RELAX_IMMED)
+     been relaxed.  Note that we can assume that the LOOP
+     instruction is in slot 0 because loops aren't bundleable.  */
+  if (next_fragp->tc_frag_data.slot_subtypes[0] > RELAX_IMMED)
       return get_expanded_loop_offset (next_opcode);
 
   return 0;
       return get_expanded_loop_offset (next_opcode);
 
   return 0;
@@ -4498,77 +4558,73 @@ next_frag_pre_opcode_bytes (fragp)
    placed nearest to their use.  */
 
 static void
    placed nearest to their use.  */
 
 static void
-xtensa_mark_literal_pool_location ()
+xtensa_mark_literal_pool_location (void)
 {
   /* Any labels pointing to the current location need
      to be adjusted to after the literal pool.  */
   emit_state s;
   fragS *pool_location;
 
 {
   /* Any labels pointing to the current location need
      to be adjusted to after the literal pool.  */
   emit_state s;
   fragS *pool_location;
 
-  frag_align (2, 0, 0);
+  if (use_literal_section)
+    return;
 
 
-  /* We stash info in the fr_var of these frags
-     so we can later move the literal's fixes into this 
-     frchain's fix list.  We can use fr_var because fr_var's
-     interpretation depends solely on the fr_type and subtype.  */
+  /* We stash info in these frags so we can later move the literal's
+     fixes into this frchain's fix list.  */
   pool_location = frag_now;
   pool_location = frag_now;
-  frag_variant (rs_machine_dependent, 0, (int) frchain_now, 
+  frag_now->tc_frag_data.lit_frchain = frchain_now;
+  frag_now->tc_frag_data.literal_frag = frag_now;
+  frag_variant (rs_machine_dependent, 0, 0,
                RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
                RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
-  frag_variant (rs_machine_dependent, 0, (int) now_seg, 
+  xtensa_set_frag_assembly_state (frag_now);
+  frag_now->tc_frag_data.lit_seg = now_seg;
+  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);
 
   /* Now put a frag into the literal pool that points to this location.  */
   set_literal_pool_location (now_seg, pool_location);
 
   /* Now put a frag into the literal pool that points to this location.  */
   set_literal_pool_location (now_seg, pool_location);
-  xtensa_switch_to_literal_fragment (&s);
+  xtensa_switch_to_non_abs_literal_fragment (&s);
+  frag_align (2, 0, 0);
+  record_alignment (now_seg, 2);
 
   /* Close whatever frag is there.  */
   frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
 
   /* Close whatever frag is there.  */
   frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+  xtensa_set_frag_assembly_state (frag_now);
   frag_now->tc_frag_data.literal_frag = pool_location;
   frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
   xtensa_restore_emit_state (&s);
   frag_now->tc_frag_data.literal_frag = pool_location;
   frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
   xtensa_restore_emit_state (&s);
+  xtensa_set_frag_assembly_state (frag_now);
 }
 
 
 }
 
 
-/* The "loops_ok" argument is provided to allow ignoring labels that 
-   define loop ends.  This fixes a bug where the NOPs to align a 
-   loop opcode were included in a previous zero-cost loop:
-
-   loop a0, loopend
-     <loop1 body>
-   loopend:
-
-   loop a2, loopend2
-     <loop2 body>
-
-   would become:
-
-   loop a0, loopend
-     <loop1 body>
-     nop.n <===== bad!
-   loopend:
-
-   loop a2, loopend2
-     <loop2 body>
-
-   This argument is used to prevent moving the NOP to before the
-   loop-end label, which is what you want in this special case.  */
+/* Build a nop of the correct size into tinsn.  */
 
 static void
 
 static void
-xtensa_move_labels (new_frag, new_offset, loops_ok)
-     fragS *new_frag;
-     valueT new_offset;
-     bfd_boolean loops_ok;
+build_nop (TInsn *tinsn, int size)
 {
 {
-  sym_list *lit;
-
-  for (lit = insn_labels; lit; lit = lit->next)
+  tinsn_init (tinsn);
+  switch (size)
     {
     {
-      symbolS *lit_sym = lit->sym;
-      if (loops_ok || symbol_get_tc (lit_sym)->is_loop_target == 0)
+    case 2:
+      tinsn->opcode = xtensa_nop_n_opcode;
+      tinsn->ntok = 0;
+      if (tinsn->opcode == XTENSA_UNDEFINED)
+       as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
+      break;
+
+    case 3:
+      if (xtensa_nop_opcode == XTENSA_UNDEFINED)
        {
        {
-         S_SET_VALUE (lit_sym, new_offset);
-         symbol_set_frag (lit_sym, new_frag);
+         tinsn->opcode = xtensa_or_opcode;
+         set_expr_const (&tinsn->tok[0], 1);
+         set_expr_const (&tinsn->tok[1], 1);
+         set_expr_const (&tinsn->tok[2], 1);
+         tinsn->ntok = 3;
        }
        }
+      else
+       tinsn->opcode = xtensa_nop_opcode;
+
+      assert (tinsn->opcode != XTENSA_UNDEFINED);
     }
 }
 
     }
 }
 
@@ -4576,44 +4632,20 @@ xtensa_move_labels (new_frag, new_offset, loops_ok)
 /* Assemble a NOP of the requested size in the buffer.  User must have
    allocated "buf" with at least "size" bytes.  */
 
 /* Assemble a NOP of the requested size in the buffer.  User must have
    allocated "buf" with at least "size" bytes.  */
 
-void
-assemble_nop (size, buf)
-     size_t size;
-     char *buf;
+static void
+assemble_nop (int size, char *buf)
 {
   static xtensa_insnbuf insnbuf = NULL;
 {
   static xtensa_insnbuf insnbuf = NULL;
-  TInsn t_insn;
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+  TInsn tinsn;
 
 
-  tinsn_init (&t_insn);
-  switch (size)
-    {
-    case 2:
-      t_insn.opcode = xtensa_nop_n_opcode;
-      t_insn.ntok = 0;
-      if (t_insn.opcode == XTENSA_UNDEFINED)
-       as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
-      tinsn_to_insnbuf (&t_insn, insnbuf);
-      xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
-      break;
+  build_nop (&tinsn, size);
 
 
-    case 3:
-      t_insn.opcode = xtensa_or_opcode;
-      assert (t_insn.opcode != XTENSA_UNDEFINED);
-      if (t_insn.opcode == XTENSA_UNDEFINED)
-       as_fatal (_("opcode 'OR' unavailable in this configuration"));
-      set_expr_const (&t_insn.tok[0], 1);
-      set_expr_const (&t_insn.tok[1], 1);
-      set_expr_const (&t_insn.tok[2], 1);
-      t_insn.ntok = 3;
-      tinsn_to_insnbuf (&t_insn, insnbuf);
-      xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
-      break;
+  if (!insnbuf)
+    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
 
 
-    default:
-      as_fatal (_("invalid %d-byte NOP requested"), size);
-    }
+  tinsn_to_insnbuf (&tinsn, insnbuf);
+  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
+                          (unsigned char *) buf, 0);
 }
 
 
 }
 
 
@@ -4625,14 +4657,12 @@ assemble_nop (size, buf)
    opcode.  */
 
 static addressT
    opcode.  */
 
 static addressT
-get_expanded_loop_offset (opcode)
-     xtensa_opcode opcode;
+get_expanded_loop_offset (xtensa_opcode opcode)
 {
   /* This is the OFFSET of the loop instruction in the expanded loop.
      This MUST correspond directly to the specification of the loop
      expansion.  It will be validated on fragment conversion.  */
 {
   /* This is the OFFSET of the loop instruction in the expanded loop.
      This MUST correspond directly to the specification of the loop
      expansion.  It will be validated on fragment conversion.  */
-  if (opcode == XTENSA_UNDEFINED)
-    as_fatal (_("get_expanded_loop_offset: undefined opcode"));
+  assert (opcode != XTENSA_UNDEFINED);
   if (opcode == xtensa_loop_opcode)
     return 0;
   if (opcode == xtensa_loopnez_opcode)
   if (opcode == xtensa_loop_opcode)
     return 0;
   if (opcode == xtensa_loopnez_opcode)
@@ -4644,45 +4674,301 @@ get_expanded_loop_offset (opcode)
 }
 
 
 }
 
 
-fragS *
-get_literal_pool_location (seg)
-     segT seg;
+static fragS *
+get_literal_pool_location (segT seg)
 {
   return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
 }
 
 
 static void
 {
   return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
 }
 
 
 static void
-set_literal_pool_location (seg, literal_pool_loc)
-     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
+   opened and after a frag has been closed.  */
+
+static void
+xtensa_set_frag_assembly_state (fragS *fragP)
+{
+  if (!density_supported)
+    fragP->tc_frag_data.is_no_density = TRUE;
+
+  /* This function is called from subsegs_finish, which is called
+     after xtensa_end, so we can't use "use_transform" or
+     "use_schedule" here.  */
+  if (!directive_state[directive_transform])
+    fragP->tc_frag_data.is_no_transform = TRUE;
+  if (directive_state[directive_longcalls])
+    fragP->tc_frag_data.use_longcalls = TRUE;
+  fragP->tc_frag_data.use_absolute_literals =
+    directive_state[directive_absolute_literals];
+  fragP->tc_frag_data.is_assembly_state_set = TRUE;
+}
+
+
+static bfd_boolean
+relaxable_section (asection *sec)
+{
+  return (sec->flags & SEC_DEBUGGING) == 0;
+}
+
+
+static void
+xtensa_mark_frags_for_org (void)
+{
+  segT *seclist;
+
+  /* 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
+     "no transform", which will prevent linker optimizations from messing
+     up the .org distance.  This should be done after
+     xtensa_find_unmarked_state_frags, because we don't want to worry here
+     about that function trashing the data we save here.  */
+
+  for (seclist = &stdoutput->sections;
+       seclist && *seclist;
+       seclist = &(*seclist)->next)
+    {
+      segT sec = *seclist;
+      segment_info_type *seginfo;
+      fragS *fragP;
+      flagword flags;
+      flags = bfd_get_section_flags (stdoutput, sec);
+      if (flags & SEC_DEBUGGING)
+       continue;
+      if (!(flags & SEC_ALLOC))
+       continue;
+
+      seginfo = seg_info (sec);
+      if (seginfo && seginfo->frchainP)
+       {
+         fragS *last_fragP = seginfo->frchainP->frch_root;
+         for (fragP = seginfo->frchainP->frch_root; fragP;
+              fragP = fragP->fr_next)
+           {
+             /* 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.  */
+             if (fragP->fr_subtype == RELAX_ORG)
+               {
+                 while (last_fragP != fragP->fr_next)
+                   {
+                     last_fragP->tc_frag_data.is_no_transform = TRUE;
+                     last_fragP = last_fragP->fr_next;
+                   }
+               }
+           }
+       }
+    }
+}
+
+
+static void
+xtensa_find_unmarked_state_frags (void)
+{
+  segT *seclist;
+
+  /* Walk over each fragment of all of the current segments.  For each
+     unmarked fragment, mark it with the same info as the previous
+     fragment.  */
+  for (seclist = &stdoutput->sections;
+       seclist && *seclist;
+       seclist = &(*seclist)->next)
+    {
+      segT sec = *seclist;
+      segment_info_type *seginfo;
+      fragS *fragP;
+      flagword flags;
+      flags = bfd_get_section_flags (stdoutput, sec);
+      if (flags & SEC_DEBUGGING)
+       continue;
+      if (!(flags & SEC_ALLOC))
+       continue;
+
+      seginfo = seg_info (sec);
+      if (seginfo && seginfo->frchainP)
+       {
+         fragS *last_fragP = 0;
+         for (fragP = seginfo->frchainP->frch_root; fragP;
+              fragP = fragP->fr_next)
+           {
+             if (fragP->fr_fix != 0
+                 && !fragP->tc_frag_data.is_assembly_state_set)
+               {
+                 if (last_fragP == 0)
+                   {
+                     as_warn_where (fragP->fr_file, fragP->fr_line,
+                                    _("assembly state not set for first frag in section %s"),
+                                    sec->name);
+                   }
+                 else
+                   {
+                     fragP->tc_frag_data.is_assembly_state_set = TRUE;
+                     fragP->tc_frag_data.is_no_density =
+                       last_fragP->tc_frag_data.is_no_density;
+                     fragP->tc_frag_data.is_no_transform =
+                       last_fragP->tc_frag_data.is_no_transform;
+                     fragP->tc_frag_data.use_longcalls =
+                       last_fragP->tc_frag_data.use_longcalls;
+                     fragP->tc_frag_data.use_absolute_literals =
+                       last_fragP->tc_frag_data.use_absolute_literals;
+                   }
+               }
+             if (fragP->tc_frag_data.is_assembly_state_set)
+               last_fragP = fragP;
+           }
+       }
+    }
+}
+
+
+static void
+xtensa_find_unaligned_branch_targets (bfd *abfd ATTRIBUTE_UNUSED,
+                                     asection *sec,
+                                     void *unused ATTRIBUTE_UNUSED)
+{
+  flagword flags = bfd_get_section_flags (abfd, sec);
+  segment_info_type *seginfo = seg_info (sec);
+  fragS *frag = seginfo->frchainP->frch_root;
+
+  if (flags & SEC_CODE)
+    {
+      xtensa_isa isa = xtensa_default_isa;
+      xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
+      while (frag != NULL)
+       {
+         if (frag->tc_frag_data.is_branch_target)
+           {
+             int op_size;
+             addressT branch_align, frag_addr;
+             xtensa_format fmt;
+
+             xtensa_insnbuf_from_chars
+               (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
+             fmt = xtensa_format_decode (isa, insnbuf);
+             op_size = xtensa_format_length (isa, fmt);
+             branch_align = 1 << branch_align_power (sec);
+             frag_addr = frag->fr_address % branch_align;
+             if (frag_addr + op_size > branch_align)
+               as_warn_where (frag->fr_file, frag->fr_line,
+                              _("unaligned branch target: %d bytes at 0x%lx"),
+                              op_size, (long) frag->fr_address);
+           }
+         frag = frag->fr_next;
+       }
+      xtensa_insnbuf_free (isa, insnbuf);
+    }
+}
+
+
+static void
+xtensa_find_unaligned_loops (bfd *abfd ATTRIBUTE_UNUSED,
+                            asection *sec,
+                            void *unused ATTRIBUTE_UNUSED)
+{
+  flagword flags = bfd_get_section_flags (abfd, sec);
+  segment_info_type *seginfo = seg_info (sec);
+  fragS *frag = seginfo->frchainP->frch_root;
+  xtensa_isa isa = xtensa_default_isa;
+
+  if (flags & SEC_CODE)
+    {
+      xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
+      while (frag != NULL)
+       {
+         if (frag->tc_frag_data.is_first_loop_insn)
+           {
+             int op_size;
+             addressT frag_addr;
+             xtensa_format fmt;
+
+             xtensa_insnbuf_from_chars
+               (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
+             fmt = xtensa_format_decode (isa, insnbuf);
+             op_size = xtensa_format_length (isa, fmt);
+             frag_addr = frag->fr_address % xtensa_fetch_width;
+
+             if (frag_addr + op_size > xtensa_fetch_width)
+               as_warn_where (frag->fr_file, frag->fr_line,
+                              _("unaligned loop: %d bytes at 0x%lx"),
+                              op_size, (long) frag->fr_address);
+           }
+         frag = frag->fr_next;
+       }
+      xtensa_insnbuf_free (isa, insnbuf);
+    }
+}
+
+
+static int
+xg_apply_fix_value (fixS *fixP, valueT val)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
+  xtensa_format fmt;
+  int slot;
+  bfd_boolean alt_reloc;
+  xtensa_opcode opcode;
+  char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+  (void) decode_reloc (fixP->fx_r_type, &slot, &alt_reloc);
+  if (alt_reloc)
+    as_fatal (_("unexpected fix"));
+
+  if (!insnbuf)
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
+
+  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (fmt == XTENSA_UNDEFINED)
+    as_fatal (_("undecodable fix"));
+  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+  opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+  if (opcode == XTENSA_UNDEFINED)
+    as_fatal (_("undecodable fix"));
+
+  /* CONST16 immediates are not PC-relative, despite the fact that we
+     reuse the normal PC-relative operand relocations for the low part
+     of a CONST16 operand.  */
+  if (opcode == xtensa_const16_opcode)
+    return 0;
+
+  xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode,
+                             get_relaxable_immed (opcode), val,
+                             fixP->fx_file, fixP->fx_line);
+
+  xtensa_format_set_slot (isa, fmt, slot, insnbuf, slotbuf);
+  xtensa_insnbuf_to_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
+
+  return 1;
+}
+
 \f
 /* External Functions and Other GAS Hooks.  */
 
 const char *
 \f
 /* External Functions and Other GAS Hooks.  */
 
 const char *
-xtensa_target_format ()
+xtensa_target_format (void)
 {
   return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
 }
 
 
 void
 {
   return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
 }
 
 
 void
-xtensa_file_arch_init (abfd)
-     bfd *abfd;
+xtensa_file_arch_init (bfd *abfd)
 {
   bfd_set_private_flags (abfd, 0x100 | 0x200);
 }
 
 
 void
 {
   bfd_set_private_flags (abfd, 0x100 | 0x200);
 }
 
 
 void
-md_number_to_chars (buf, val, n)
-     char *buf;
-     valueT val;
-     int n;
+md_number_to_chars (char *buf, valueT val, int n)
 {
   if (target_big_endian)
     number_to_chars_bigendian (buf, val, n);
 {
   if (target_big_endian)
     number_to_chars_bigendian (buf, val, n);
@@ -4696,27 +4982,24 @@ md_number_to_chars (buf, val, n)
    need.  */
 
 void
    need.  */
 
 void
-md_begin ()
+md_begin (void)
 {
   segT current_section = now_seg;
   int current_subsec = now_subseg;
   xtensa_isa isa;
 
 {
   segT current_section = now_seg;
   int current_subsec = now_subseg;
   xtensa_isa isa;
 
-#if STATIC_LIBISA
-  isa = xtensa_isa_init ();
-#else
-  /* ISA was already initialized by xtensa_init().  */
+  xtensa_default_isa = xtensa_isa_init (0, 0);
   isa = xtensa_default_isa;
   isa = xtensa_default_isa;
-#endif
 
 
-  /* Set  up the .literal, .fini.literal and .init.literal sections.  */
+  linkrelax = 1;
+
+  /* Set up the literal sections.  */
   memset (&default_lit_sections, 0, sizeof (default_lit_sections));
   memset (&default_lit_sections, 0, sizeof (default_lit_sections));
-  default_lit_sections.init_lit_seg_name = INIT_LITERAL_SECTION_NAME;
-  default_lit_sections.fini_lit_seg_name = FINI_LITERAL_SECTION_NAME;
-  default_lit_sections.lit_seg_name = LITERAL_SECTION_NAME;
 
   subseg_set (current_section, current_subsec);
 
 
   subseg_set (current_section, current_subsec);
 
+  xg_init_vinsn (&cur_vinsn);
+
   xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
   xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
   xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
   xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
   xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
   xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
@@ -4727,75 +5010,184 @@ md_begin ()
   xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4");
   xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8");
   xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
   xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4");
   xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8");
   xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
+  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_movi_opcode = xtensa_opcode_lookup (isa, "movi");
+  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_jx_opcode = xtensa_opcode_lookup (isa, "jx");
   xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx");
+  xtensa_l32r_opcode = xtensa_opcode_lookup (isa, "l32r");
   xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop");
   xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez");
   xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz");
   xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop");
   xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez");
   xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz");
+  xtensa_nop_opcode = xtensa_opcode_lookup (isa, "nop");
   xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n");
   xtensa_or_opcode = xtensa_opcode_lookup (isa, "or");
   xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret");
   xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n");
   xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw");
   xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n");
   xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n");
   xtensa_or_opcode = xtensa_opcode_lookup (isa, "or");
   xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret");
   xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n");
   xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw");
   xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n");
-  xtensa_rsr_opcode = xtensa_opcode_lookup (isa, "rsr");
+  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");
+
+  init_op_placement_info_table ();
+
+  /* Set up the assembly state.  */
+  if (!frag_now->tc_frag_data.is_assembly_state_set)
+    xtensa_set_frag_assembly_state (frag_now);
+}
+
+
+/* TC_INIT_FIX_DATA hook */
+
+void
+xtensa_init_fix_data (fixS *x)
+{
+  x->tc_fix_data.slot = 0;
+  x->tc_fix_data.X_add_symbol = NULL;
+  x->tc_fix_data.X_add_number = 0;
 }
 
 
 /* tc_frob_label hook */
 
 void
 }
 
 
 /* tc_frob_label hook */
 
 void
-xtensa_frob_label (sym)
-     symbolS *sym;
+xtensa_frob_label (symbolS *sym)
 {
 {
+  float freq;
+
+  if (cur_vinsn.inside_bundle)
+    {
+      as_bad (_("labels are not valid inside bundles"));
+      return;
+    }
+
+  freq = get_subseg_target_freq (now_seg, now_subseg);
+
+  /* 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.  */
+  symbol_set_frag (sym, frag_now);
+  S_SET_VALUE (sym, (valueT) frag_now_fix ());
+
   if (generating_literals)
     xtensa_add_literal_sym (sym);
   else
     xtensa_add_insn_label (sym);
 
   if (generating_literals)
     xtensa_add_literal_sym (sym);
   else
     xtensa_add_insn_label (sym);
 
-  if (symbol_get_tc (sym)->is_loop_target
-      && (get_last_insn_flags (now_seg, now_subseg)
+  if (symbol_get_tc (sym)->is_loop_target)
+    {
+      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);
+      frag_var (rs_machine_dependent, 4, 4, RELAX_LOOP_END,
+               frag_now->fr_symbol, frag_now->fr_offset, NULL);
+
+      xtensa_set_frag_assembly_state (frag_now);
+      xtensa_move_labels (frag_now, 0);
+    }
 
   /* No target aligning in the absolute section.  */
   if (now_seg != absolute_section
 
   /* No target aligning in the absolute section.  */
   if (now_seg != absolute_section
-      && align_targets
+      && do_align_targets ()
       && !is_unaligned_label (sym)
       && !is_unaligned_label (sym)
-      && !frag_now->tc_frag_data.is_literal)
+      && !generating_literals)
     {
     {
-      /* frag_now->tc_frag_data.is_insn = TRUE; */
-      frag_var (rs_machine_dependent, 4, 4,
+      xtensa_set_frag_assembly_state (frag_now);
+
+      frag_var (rs_machine_dependent,
+               0, (int) freq,
                RELAX_DESIRE_ALIGN_IF_TARGET,
                frag_now->fr_symbol, frag_now->fr_offset, NULL);
                RELAX_DESIRE_ALIGN_IF_TARGET,
                frag_now->fr_symbol, frag_now->fr_offset, NULL);
-      xtensa_move_labels (frag_now, 0, TRUE);
+      xtensa_set_frag_assembly_state (frag_now);
+      xtensa_move_labels (frag_now, 0);
+    }
+
+  /* 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
+     forward branch, mark the frag accordingly.  Backward branches
+     are handled by xg_add_branch_and_loop_targets.  */
+  if (symbol_get_tc (sym)->is_branch_target)
+    symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
+
+  /* Loops only go forward, so they can be identified here.  */
+  if (symbol_get_tc (sym)->is_loop_target)
+    symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
+
+  dwarf2_emit_label (sym);
+}
+
 
 
-      /* If the label is already known to be a branch target, i.e., a
-        forward branch, mark the frag accordingly.  Backward branches
-        are handled by xg_add_branch_and_loop_targets.  */
-      if (symbol_get_tc (sym)->is_branch_target)
-       symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
+/* tc_unrecognized_line hook */
 
 
-      /* Loops only go forward, so they can be identified here.  */
-      if (symbol_get_tc (sym)->is_loop_target)
-       symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
+int
+xtensa_unrecognized_line (int ch)
+{
+  switch (ch)
+    {
+    case '{' :
+      if (cur_vinsn.inside_bundle == 0)
+       {
+         /* PR8110: Cannot emit line number info inside a FLIX bundle
+            when using --gstabs.  Temporarily disable debug info.  */
+         generate_lineno_debug ();
+         if (debug_type == DEBUG_STABS)
+           {
+             xt_saved_debug_type = debug_type;
+             debug_type = DEBUG_NONE;
+           }
+
+         cur_vinsn.inside_bundle = 1;
+       }
+      else
+       {
+         as_bad (_("extra opening brace"));
+         return 0;
+       }
+      break;
+
+    case '}' :
+      if (cur_vinsn.inside_bundle)
+       finish_vinsn (&cur_vinsn);
+      else
+       {
+         as_bad (_("extra closing brace"));
+         return 0;
+       }
+      break;
+    default:
+      as_bad (_("syntax error"));
+      return 0;
     }
     }
+  return 1;
 }
 
 
 /* md_flush_pending_output hook */
 
 void
 }
 
 
 /* md_flush_pending_output hook */
 
 void
-xtensa_flush_pending_output ()
-{
+xtensa_flush_pending_output (void)
+{
+  /* This line fixes a bug where automatically generated gstabs info
+     separates a function label from its entry instruction, ending up
+     with the literal position between the function label and the entry
+     instruction and crashing code.  It only happens with --gstabs and
+     --text-section-literals, and when several other obscure relaxation
+     conditions are met.  */
+  if (outputting_stabs_line_debug)
+    return;
+
+  if (cur_vinsn.inside_bundle)
+    as_bad (_("missing closing brace"));
+
   /* If there is a non-zero instruction fragment, close it.  */
   if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn)
     {
       frag_wane (frag_now);
       frag_new (0);
   /* If there is a non-zero instruction fragment, close it.  */
   if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn)
     {
       frag_wane (frag_now);
       frag_new (0);
+      xtensa_set_frag_assembly_state (frag_now);
     }
   frag_now->tc_frag_data.is_insn = FALSE;
 
     }
   frag_now->tc_frag_data.is_insn = FALSE;
 
@@ -4803,33 +5195,36 @@ xtensa_flush_pending_output ()
 }
 
 
 }
 
 
+/* 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
+   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 bad things happen.  */
+
+static void
+error_reset_cur_vinsn (void)
+{
+  if (cur_vinsn.inside_bundle)
+    {
+      if (*input_line_pointer == '}'
+         || *(input_line_pointer - 1) == '}'
+         || *(input_line_pointer - 2) == '}')
+       xg_clear_vinsn (&cur_vinsn);
+    }
+}
+
+
 void
 void
-md_assemble (str)
-     char *str;
+md_assemble (char *str)
 {
   xtensa_isa isa = xtensa_default_isa;
 {
   xtensa_isa isa = xtensa_default_isa;
-  char *opname;
+  char *opname, *file_name;
   unsigned opnamelen;
   bfd_boolean has_underbar = FALSE;
   unsigned opnamelen;
   bfd_boolean has_underbar = FALSE;
-  char *arg_strings[MAX_INSN_ARGS]; 
+  char *arg_strings[MAX_INSN_ARGS];
   int num_args;
   int num_args;
-  IStack istack;               /* Put instructions into here.  */
   TInsn orig_insn;             /* Original instruction from the input.  */
   TInsn orig_insn;             /* Original instruction from the input.  */
-  int i;
-  symbolS *lit_sym = NULL;
-
-  if (frag_now->tc_frag_data.is_literal)
-    {
-      static bfd_boolean reported = 0;
-      if (reported < 4)
-       as_bad (_("cannot assemble '%s' into a literal fragment"), str);
-      if (reported == 3)
-       as_bad (_("..."));
-      reported++;
-      return;
-    }
 
 
-  istack_init (&istack);
   tinsn_init (&orig_insn);
 
   /* Split off the opcode.  */
   tinsn_init (&orig_insn);
 
   /* Split off the opcode.  */
@@ -4857,52 +5252,38 @@ md_assemble (str)
 
   orig_insn.insn_type = ITYPE_INSN;
   orig_insn.ntok = 0;
 
   orig_insn.insn_type = ITYPE_INSN;
   orig_insn.ntok = 0;
-  orig_insn.is_specific_opcode = (has_underbar || !use_generics ());
-  specific_opcode = orig_insn.is_specific_opcode;
+  orig_insn.is_specific_opcode = (has_underbar || !use_transform ());
 
   orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
   if (orig_insn.opcode == XTENSA_UNDEFINED)
     {
 
   orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
   if (orig_insn.opcode == XTENSA_UNDEFINED)
     {
-      as_bad (_("unknown opcode %s"), opname);
-      return;
-    }
-
-  if (frag_now_fix () != 0 && !frag_now->tc_frag_data.is_insn)
-    {
-      frag_wane (frag_now);
-      frag_new (0);
-    }
-
-  if (software_a0_b_retw_interlock)
-    {
-      if ((get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
-         && is_conditional_branch_opcode (orig_insn.opcode))
+      xtensa_format fmt = xtensa_format_lookup (isa, opname);
+      if (fmt == XTENSA_UNDEFINED)
        {
        {
-         has_a0_b_retw = TRUE;
-
-         /* 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
-            add a nop if needed.  */
-         frag_now->tc_frag_data.is_insn = TRUE;
-         frag_var (rs_machine_dependent, 4, 4,
-                   RELAX_ADD_NOP_IF_A0_B_RETW,
-                   frag_now->fr_symbol, frag_now->fr_offset, NULL);
-         frag_now->tc_frag_data.is_insn = TRUE;
-         frag_var (rs_machine_dependent, 4, 4,
-                   RELAX_ADD_NOP_IF_A0_B_RETW,
-                   frag_now->fr_symbol, frag_now->fr_offset, NULL);
+         as_bad (_("unknown opcode or format name '%s'"), opname);
+         error_reset_cur_vinsn ();
+         return;
        }
        }
+      if (!cur_vinsn.inside_bundle)
+       {
+         as_bad (_("format names only valid inside bundles"));
+         error_reset_cur_vinsn ();
+         return;
+       }
+      if (cur_vinsn.format != XTENSA_UNDEFINED)
+       as_warn (_("multiple formats specified for one bundle; using '%s'"),
+                opname);
+      cur_vinsn.format = fmt;
+      free (has_underbar ? opname - 1 : opname);
+      error_reset_cur_vinsn ();
+      return;
     }
 
     }
 
-  /* Special case: The call instructions should be marked "specific opcode"
-     to keep them from expanding.  */
-  if (!use_longcalls () && is_direct_call_opcode (orig_insn.opcode))
-    orig_insn.is_specific_opcode = TRUE;
-
   /* Parse the arguments.  */
   if (parse_arguments (&orig_insn, num_args, arg_strings))
     {
       as_bad (_("syntax error"));
   /* Parse the arguments.  */
   if (parse_arguments (&orig_insn, num_args, arg_strings))
     {
       as_bad (_("syntax error"));
+      error_reset_cur_vinsn ();
       return;
     }
 
       return;
     }
 
@@ -4912,176 +5293,105 @@ md_assemble (str)
   while (num_args-- > 0)
     free (arg_strings[num_args]);
 
   while (num_args-- > 0)
     free (arg_strings[num_args]);
 
-  /* Check for the right number and type of arguments.  */
-  if (tinsn_check_arguments (&orig_insn))
-    return;
-
-  /* See if the instruction implies an aligned section.  */
-  if (is_entry_opcode (orig_insn.opcode) || is_loop_opcode (orig_insn.opcode))
-    record_alignment (now_seg, 2);
-
-  xg_add_branch_and_loop_targets (&orig_insn);
-
-  /* Special cases for instructions that force an alignment... */
-  if (!orig_insn.is_specific_opcode && is_loop_opcode (orig_insn.opcode))
+  /* Get expressions for invisible operands.  */
+  if (get_invisible_operands (&orig_insn))
     {
     {
-      size_t max_fill;
-
-      frag_now->tc_frag_data.is_insn = TRUE;
-      frag_now->tc_frag_data.is_no_density = !code_density_available ();
-      max_fill = get_text_align_max_fill_size
-       (get_text_align_power (XTENSA_FETCH_WIDTH),
-        TRUE, frag_now->tc_frag_data.is_no_density);
-      frag_var (rs_machine_dependent, max_fill, max_fill,
-               RELAX_ALIGN_NEXT_OPCODE, frag_now->fr_symbol,
-               frag_now->fr_offset, NULL);
-
-      xtensa_move_labels (frag_now, 0, FALSE);
+      error_reset_cur_vinsn ();
+      return;
     }
 
     }
 
-  /* Special-case for "entry" instruction.  */
-  if (is_entry_opcode (orig_insn.opcode))
+  /* Check for the right number and type of arguments.  */
+  if (tinsn_check_arguments (&orig_insn))
     {
     {
-      /* Check that the second opcode (#1) is >= 16.  */
-      if (orig_insn.ntok >= 2)
-       {
-         expressionS *exp = &orig_insn.tok[1];
-         switch (exp->X_op)
-           {
-           case O_constant:
-             if (exp->X_add_number < 16)
-               as_warn (_("entry instruction with stack decrement < 16"));
-             break;
-
-           default:
-             as_warn (_("entry instruction with non-constant decrement"));
-           }
-       }
-
-      if (!orig_insn.is_specific_opcode)
-       {
-         xtensa_mark_literal_pool_location ();
-
-         /* Automatically align ENTRY instructions.  */
-         xtensa_move_labels (frag_now, 0, TRUE);
-         frag_align (2, 0, 0);
-       }
+      error_reset_cur_vinsn ();
+      return;
     }
 
     }
 
-  /* Any extra alignment frags have been inserted now, and we're about to
-     emit a new instruction so clear the list of labels.  */
-  xtensa_clear_insn_labels ();
+  /* A FLIX bundle may be spread across multiple input lines.  We want to
+     report the first such line in the debug information.  Record the line
+     number for each TInsn (assume the file name doesn't change), so the
+     first line can be found later.  */
+  as_where (&file_name, &orig_insn.linenum);
 
 
-  if (software_a0_b_retw_interlock)
-    set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
-                        is_register_writer (&orig_insn, "a", 0));
+  xg_add_branch_and_loop_targets (&orig_insn);
 
 
-  set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
-                      is_bad_loopend_opcode (&orig_insn));
+  /* Check that immediate value for ENTRY is >= 16.  */
+  if (orig_insn.opcode == xtensa_entry_opcode && orig_insn.ntok >= 3)
+    {
+      expressionS *exp = &orig_insn.tok[2];
+      if (exp->X_op == O_constant && exp->X_add_number < 16)
+       as_warn (_("entry instruction with stack decrement < 16"));
+    }
 
   /* Finish it off:
 
   /* Finish it off:
-     assemble_tokens (opcode, tok, ntok); 
-     expand the tokens from the orig_insn into the 
-     stack of instructions that will not expand 
+     assemble_tokens (opcode, tok, ntok);
+     expand the tokens from the orig_insn into the
+     stack of instructions that will not expand
      unless required at relaxation time.  */
      unless required at relaxation time.  */
-  if (xg_expand_assembly_insn (&istack, &orig_insn))
-    return;
 
 
-  for (i = 0; i < istack.ninsn; i++)
+  if (!cur_vinsn.inside_bundle)
+    emit_single_op (&orig_insn);
+  else /* We are inside a bundle.  */
     {
     {
-      TInsn *insn = &istack.insn[i];
-      if (insn->insn_type == ITYPE_LITERAL)
-       {
-         assert (lit_sym == NULL);
-         lit_sym = xg_assemble_literal (insn);
-       }
-      else
-       {
-         if (lit_sym)
-           xg_resolve_literals (insn, lit_sym);
-         xg_assemble_tokens (insn);
-       }
+      cur_vinsn.slots[cur_vinsn.num_slots] = orig_insn;
+      cur_vinsn.num_slots++;
+      if (*input_line_pointer == '}'
+         || *(input_line_pointer - 1) == '}'
+         || *(input_line_pointer - 2) == '}')
+       finish_vinsn (&cur_vinsn);
     }
 
     }
 
-  /* Now, if the original opcode was a call... */
-  if (align_targets && is_call_opcode (orig_insn.opcode))
-    {
-      frag_now->tc_frag_data.is_insn = TRUE;
-      frag_var (rs_machine_dependent, 4, 4,
-               RELAX_DESIRE_ALIGN,
-               frag_now->fr_symbol,
-               frag_now->fr_offset,
-               NULL);
-    }
+  /* We've just emitted a new instruction so clear the list of labels.  */
+  xtensa_clear_insn_labels ();
 }
 
 
 }
 
 
-/* TC_CONS_FIX_NEW hook: Check for "@PLT" suffix on symbol references.
-   If found, use an XTENSA_PLT reloc for 4-byte values.  Otherwise, this
-   is the same as the standard code in read.c.  */
+/* HANDLE_ALIGN hook */
 
 
-void 
-xtensa_cons_fix_new (frag, where, size, exp)
-     fragS *frag;
-     int where; 
-     int size;
-     expressionS *exp;
-{
-  bfd_reloc_code_real_type r;
-  bfd_boolean plt = FALSE;
+/* For a .align directive, we mark the previous block with the alignment
+   information.  This will be placed in the object file in the
+   property section corresponding to this section.  */
 
 
-  if (*input_line_pointer == '@') 
+void
+xtensa_handle_align (fragS *fragP)
+{
+  if (linkrelax
+      && ! fragP->tc_frag_data.is_literal
+      && (fragP->fr_type == rs_align
+         || fragP->fr_type == rs_align_code)
+      && fragP->fr_address + fragP->fr_fix > 0
+      && fragP->fr_offset > 0
+      && now_seg != bss_section)
     {
     {
-      if (!strncmp (input_line_pointer, PLT_SUFFIX, strlen (PLT_SUFFIX) - 1)
-         && !strncmp (input_line_pointer, plt_suffix,
-                      strlen (plt_suffix) - 1))
-       {
-         as_bad (_("undefined @ suffix '%s', expected '%s'"), 
-                 input_line_pointer, plt_suffix);
-         ignore_rest_of_line ();
-         return;
-       }
-
-      input_line_pointer += strlen (plt_suffix);
-      plt = TRUE;
+      fragP->tc_frag_data.is_align = TRUE;
+      fragP->tc_frag_data.alignment = fragP->fr_offset;
     }
 
     }
 
-  switch (size)
+  if (fragP->fr_type == rs_align_test)
     {
     {
-    case 1:
-      r = BFD_RELOC_8;
-      break;
-    case 2:
-      r = BFD_RELOC_16;
-      break;
-    case 4:
-      r = plt ? BFD_RELOC_XTENSA_PLT : BFD_RELOC_32;
-      break;
-    case 8:
-      r = BFD_RELOC_64;
-      break;
-    default:
-      as_bad (_("unsupported BFD relocation size %u"), size);
-      r = BFD_RELOC_32;
-      break;
+      int count;
+      count = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+      if (count != 0)
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("unaligned entry instruction"));
     }
     }
-  fix_new_exp (frag, where, size, exp, 0, r);
+
+  if (linkrelax && fragP->fr_type == rs_org)
+    fragP->fr_subtype = RELAX_ORG;
 }
 }
-  
+
 
 /* TC_FRAG_INIT hook */
 
 void
 
 /* TC_FRAG_INIT hook */
 
 void
-xtensa_frag_init (frag)
-     fragS *frag;
+xtensa_frag_init (fragS *frag)
 {
 {
-  frag->tc_frag_data.is_no_density = !code_density_available ();
+  xtensa_set_frag_assembly_state (frag);
 }
 
 
 symbolS *
 }
 
 
 symbolS *
-md_undefined_symbol (name)
-     char *name ATTRIBUTE_UNUSED;
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
 {
   return NULL;
 }
 {
   return NULL;
 }
@@ -5090,95 +5400,180 @@ md_undefined_symbol (name)
 /* Round up a section size to the appropriate boundary.  */
 
 valueT
 /* Round up a section size to the appropriate boundary.  */
 
 valueT
-md_section_align (segment, size)
-     segT segment ATTRIBUTE_UNUSED;
-     valueT size;
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
 {
   return size;                 /* Byte alignment is fine.  */
 }
 
 
 long
 {
   return size;                 /* Byte alignment is fine.  */
 }
 
 
 long
-md_pcrel_from (fixP)
-     fixS *fixP;
+md_pcrel_from (fixS *fixP)
 {
   char *insn_p;
   static xtensa_insnbuf insnbuf = NULL;
 {
   char *insn_p;
   static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
   int opnum;
   int opnum;
-  xtensa_operand operand;
+  uint32 opnd_value;
   xtensa_opcode opcode;
   xtensa_opcode opcode;
+  xtensa_format fmt;
+  int slot;
   xtensa_isa isa = xtensa_default_isa;
   valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
   xtensa_isa isa = xtensa_default_isa;
   valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
-
-  if (fixP->fx_done)
-    return addr;
+  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 addr;
+    return 0;
 
   if (!insnbuf)
 
   if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (isa);
+    {
+      insnbuf = 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, insn_p);
-  opcode = xtensa_decode_insn (isa, insnbuf);
+  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) insn_p, 0);
+  fmt = xtensa_format_decode (isa, insnbuf);
+
+  if (fmt == XTENSA_UNDEFINED)
+    as_fatal (_("bad instruction format"));
 
 
-  opnum = reloc_to_opnum (fixP->fx_r_type);
+  if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc) != 0)
+    as_fatal (_("invalid relocation"));
 
 
-  if (opnum < 0)
-    as_fatal (_("invalid operand relocation for '%s' instruction"),
-             xtensa_opcode_name (isa, opcode));
-  if (opnum >= xtensa_num_operands (isa, opcode))
-    as_fatal (_("invalid relocation for operand %d in '%s' instruction"),
-             opnum, xtensa_opcode_name (isa, opcode));
-  operand = xtensa_get_operand (isa, opcode, opnum);
-  if (!operand)
+  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+  opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+
+  /* Check for "alternate" relocations (operand not specified).  None
+     of the current uses for these are really PC-relative.  */
+  if (alt_reloc || opcode == xtensa_const16_opcode)
     {
     {
-      as_warn_where (fixP->fx_file,
-                    fixP->fx_line,
-                    _("invalid relocation type %d for %s instruction"),
-                    fixP->fx_r_type, xtensa_opcode_name (isa, opcode));
-      return addr;
+      if (opcode != xtensa_l32r_opcode
+         && opcode != xtensa_const16_opcode)
+       as_fatal (_("invalid relocation for '%s' instruction"),
+                 xtensa_opcode_name (isa, opcode));
+      return 0;
     }
 
     }
 
-  if (!operand_is_pcrel_label (operand))
+  opnum = get_relaxable_immed (opcode);
+  opnd_value = 0;
+  if (xtensa_operand_is_PCrelative (isa, opcode, opnum) != 1
+      || xtensa_operand_do_reloc (isa, opcode, opnum, &opnd_value, addr))
     {
       as_bad_where (fixP->fx_file,
                    fixP->fx_line,
                    _("invalid relocation for operand %d of '%s'"),
                    opnum, xtensa_opcode_name (isa, opcode));
     {
       as_bad_where (fixP->fx_file,
                    fixP->fx_line,
                    _("invalid relocation for operand %d of '%s'"),
                    opnum, xtensa_opcode_name (isa, opcode));
-      return addr;
+      return 0;
     }
     }
-  if (!xtensa_operand_isPCRelative (operand))
+  return 0 - opnd_value;
+}
+
+
+/* TC_FORCE_RELOCATION hook */
+
+int
+xtensa_force_relocation (fixS *fix)
+{
+  switch (fix->fx_r_type)
     {
     {
-      as_warn_where (fixP->fx_file,
-                    fixP->fx_line,
-                    _("non-PCREL relocation operand %d for '%s': %s"),
-                    opnum, xtensa_opcode_name (isa, opcode),
-                    bfd_get_reloc_code_name (fixP->fx_r_type));
-      return addr;
+    case BFD_RELOC_XTENSA_ASM_EXPAND:
+    case BFD_RELOC_XTENSA_SLOT0_ALT:
+    case BFD_RELOC_XTENSA_SLOT1_ALT:
+    case BFD_RELOC_XTENSA_SLOT2_ALT:
+    case BFD_RELOC_XTENSA_SLOT3_ALT:
+    case BFD_RELOC_XTENSA_SLOT4_ALT:
+    case BFD_RELOC_XTENSA_SLOT5_ALT:
+    case BFD_RELOC_XTENSA_SLOT6_ALT:
+    case BFD_RELOC_XTENSA_SLOT7_ALT:
+    case BFD_RELOC_XTENSA_SLOT8_ALT:
+    case BFD_RELOC_XTENSA_SLOT9_ALT:
+    case BFD_RELOC_XTENSA_SLOT10_ALT:
+    case BFD_RELOC_XTENSA_SLOT11_ALT:
+    case BFD_RELOC_XTENSA_SLOT12_ALT:
+    case BFD_RELOC_XTENSA_SLOT13_ALT:
+    case BFD_RELOC_XTENSA_SLOT14_ALT:
+      return 1;
+    default:
+      break;
     }
 
     }
 
-  return 0 - xtensa_operand_do_reloc (operand, 0, addr);
+  if (linkrelax && fix->fx_addsy
+      && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+    return 1;
+
+  return generic_force_reloc (fix);
+}
+
+
+/* TC_VALIDATE_FIX_SUB hook */
+
+int
+xtensa_validate_fix_sub (fixS *fix)
+{
+  segT add_symbol_segment, sub_symbol_segment;
+
+  /* The difference of two symbols should be resolved by the assembler when
+     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 linker knows to adjust the difference value.  */
+  if (!linkrelax || fix->fx_addsy == NULL)
+    return 0;
+
+  /* Make sure both symbols are in the same segment, and that segment is
+     "normal" and relaxable.  If the segment is not "normal", then the
+     fix is not valid.  If the segment is not "relaxable", then the fix
+     should have been handled earlier.  */
+  add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+  if (! SEG_NORMAL (add_symbol_segment) ||
+      ! relaxable_section (add_symbol_segment))
+    return 0;
+  sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+  return (sub_symbol_segment == add_symbol_segment);
+}
+
+
+/* NO_PSEUDO_DOT hook */
+
+/* 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
+   seems wrong.  */
+
+bfd_boolean
+xtensa_check_inside_bundle (void)
+{
+  if (cur_vinsn.inside_bundle && input_line_pointer[-1] == '.')
+    as_bad (_("directives are not valid inside bundles"));
+
+  /* This function must always return FALSE because it is called via a
+     macro that has nothing to do with bundling.  */
+  return FALSE;
 }
 
 
 }
 
 
-/* tc_symbol_new_hook */
+/* md_elf_section_change_hook */
 
 void
 
 void
-xtensa_symbol_new_hook (symbolP)
-     symbolS *symbolP;
+xtensa_elf_section_change_hook (void)
 {
 {
-  symbol_get_tc (symbolP)->plt = 0;
+  /* Set up the assembly state.  */
+  if (!frag_now->tc_frag_data.is_assembly_state_set)
+    xtensa_set_frag_assembly_state (frag_now);
 }
 
 
 /* tc_fix_adjustable hook */
 
 bfd_boolean
 }
 
 
 /* tc_fix_adjustable hook */
 
 bfd_boolean
-xtensa_fix_adjustable (fixP)
-     fixS *fixP;
-{
+xtensa_fix_adjustable (fixS *fixP)
+{
+  /* An offset is not allowed in combination with the difference of two
+     symbols, but that cannot be easily detected after a local symbol
+     has been adjusted to a (section+offset) form.  Return 0 so that such
+     an fix will not be adjusted.  */
+  if (fixP->fx_subsy && fixP->fx_addsy && fixP->fx_offset
+      && relaxable_section (S_GET_SEGMENT (fixP->fx_subsy)))
+    return 0;
+
   /* We need the symbol name for the VTABLE entries.  */
   if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
   /* We need the symbol name for the VTABLE entries.  */
   if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
@@ -5188,389 +5583,1757 @@ xtensa_fix_adjustable (fixP)
 }
 
 
 }
 
 
-void
-md_apply_fix3 (fixP, valP, seg)
-     fixS *fixP;
-     valueT *valP;
-     segT seg ATTRIBUTE_UNUSED;
-{
-  if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+  char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+  valueT val = 0;
+
+  /* Subtracted symbols are only allowed for a few relocation types, and
+     unless linkrelax is enabled, they should not make it to this point.  */
+  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_8)))
+    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_32:
+    case BFD_RELOC_16:
+    case BFD_RELOC_8:
+      if (fixP->fx_subsy)
+       {
+         switch (fixP->fx_r_type)
+           {
+           case BFD_RELOC_8:
+             fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
+             break;
+           case BFD_RELOC_16:
+             fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
+             break;
+           case BFD_RELOC_32:
+             fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
+             break;
+           default:
+             break;
+           }
+
+         /* An offset is only allowed when it results from adjusting a
+            local symbol into a section-relative offset.  If the offset
+            came from the original expression, tc_fix_adjustable will have
+            prevented the fix from being converted to a section-relative
+            form so that we can flag the error here.  */
+         if (fixP->fx_offset != 0 && !symbol_section_p (fixP->fx_addsy))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("cannot represent subtraction with an offset"));
+
+         val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+                - S_GET_VALUE (fixP->fx_subsy));
+
+         /* The difference value gets written out, and the DIFF reloc
+            identifies the address of the subtracted symbol (i.e., the one
+            with the lowest address).  */
+         *valP = val;
+         fixP->fx_offset -= val;
+         fixP->fx_subsy = NULL;
+       }
+      else if (! fixP->fx_addsy)
+       {
+         val = *valP;
+         fixP->fx_done = 1;
+       }
+      /* fall through */
+
+    case BFD_RELOC_XTENSA_PLT:
+      md_number_to_chars (fixpos, val, fixP->fx_size);
+      fixP->fx_no_overflow = 0; /* Use the standard overflow check.  */
+      break;
+
+    case BFD_RELOC_XTENSA_SLOT0_OP:
+    case BFD_RELOC_XTENSA_SLOT1_OP:
+    case BFD_RELOC_XTENSA_SLOT2_OP:
+    case BFD_RELOC_XTENSA_SLOT3_OP:
+    case BFD_RELOC_XTENSA_SLOT4_OP:
+    case BFD_RELOC_XTENSA_SLOT5_OP:
+    case BFD_RELOC_XTENSA_SLOT6_OP:
+    case BFD_RELOC_XTENSA_SLOT7_OP:
+    case BFD_RELOC_XTENSA_SLOT8_OP:
+    case BFD_RELOC_XTENSA_SLOT9_OP:
+    case BFD_RELOC_XTENSA_SLOT10_OP:
+    case BFD_RELOC_XTENSA_SLOT11_OP:
+    case BFD_RELOC_XTENSA_SLOT12_OP:
+    case BFD_RELOC_XTENSA_SLOT13_OP:
+    case BFD_RELOC_XTENSA_SLOT14_OP:
+      if (linkrelax)
+       {
+         /* Write the tentative value of a PC-relative relocation to a
+            local symbol into the instruction.  The value will be ignored
+            by the linker, and it makes the object file disassembly
+            readable when all branch targets are encoded in relocations.  */
+
+         assert (fixP->fx_addsy);
+         if (S_GET_SEGMENT (fixP->fx_addsy) == seg
+             && !S_FORCE_RELOC (fixP->fx_addsy, 1))
+           {
+             val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+                    - md_pcrel_from (fixP));
+             (void) xg_apply_fix_value (fixP, val);
+           }
+       }
+      else if (! fixP->fx_addsy)
+       {
+         val = *valP;
+         if (xg_apply_fix_value (fixP, val))
+           fixP->fx_done = 1;
+       }
+      break;
+
+    case BFD_RELOC_XTENSA_ASM_EXPAND:
+    case BFD_RELOC_XTENSA_SLOT0_ALT:
+    case BFD_RELOC_XTENSA_SLOT1_ALT:
+    case BFD_RELOC_XTENSA_SLOT2_ALT:
+    case BFD_RELOC_XTENSA_SLOT3_ALT:
+    case BFD_RELOC_XTENSA_SLOT4_ALT:
+    case BFD_RELOC_XTENSA_SLOT5_ALT:
+    case BFD_RELOC_XTENSA_SLOT6_ALT:
+    case BFD_RELOC_XTENSA_SLOT7_ALT:
+    case BFD_RELOC_XTENSA_SLOT8_ALT:
+    case BFD_RELOC_XTENSA_SLOT9_ALT:
+    case BFD_RELOC_XTENSA_SLOT10_ALT:
+    case BFD_RELOC_XTENSA_SLOT11_ALT:
+    case BFD_RELOC_XTENSA_SLOT12_ALT:
+    case BFD_RELOC_XTENSA_SLOT13_ALT:
+    case BFD_RELOC_XTENSA_SLOT14_ALT:
+      /* These all need to be resolved at link-time.  Do nothing now.  */
+      break;
+
+    case BFD_RELOC_VTABLE_INHERIT:
+    case BFD_RELOC_VTABLE_ENTRY:
+      fixP->fx_done = 0;
+      break;
+
+    default:
+      as_bad (_("unhandled local relocation fix %s"),
+             bfd_get_reloc_code_name (fixP->fx_r_type));
+    }
+}
+
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+  int prec;
+  LITTLENUM_TYPE words[4];
+  char *t;
+  int i;
+
+  switch (type)
+    {
+    case 'f':
+      prec = 2;
+      break;
+
+    case 'd':
+      prec = 4;
+      break;
+
+    default:
+      *sizeP = 0;
+      return "bad call to md_atof";
+    }
+
+  t = atof_ieee (input_line_pointer, type, words);
+  if (t)
+    input_line_pointer = t;
+
+  *sizeP = prec * 2;
+
+  for (i = prec - 1; i >= 0; i--)
+    {
+      int idx = i;
+      if (target_big_endian)
+       idx = (prec - 1 - i);
+
+      md_number_to_chars (litP, (valueT) words[idx], 2);
+      litP += 2;
+    }
+
+  return NULL;
+}
+
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT seg ATTRIBUTE_UNUSED)
+{
+  return total_frag_text_expansion (fragP);
+}
+
+
+/* Translate internal representation of relocation info to BFD target
+   format.  */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+  arelent *reloc;
+
+  reloc = (arelent *) xmalloc (sizeof (arelent));
+  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+  /* Make sure none of our internal relocations make it this far.
+     They'd better have been fully resolved by this point.  */
+  assert ((int) fixp->fx_r_type > 0);
+
+  reloc->addend = fixp->fx_offset;
+
+  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+  if (reloc->howto == NULL)
+    {
+      as_bad_where (fixp->fx_file, fixp->fx_line,
+                   _("cannot represent `%s' relocation in object file"),
+                   bfd_get_reloc_code_name (fixp->fx_r_type));
+      free (reloc->sym_ptr_ptr);
+      free (reloc);
+      return NULL;
+    }
+
+  if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+    as_fatal (_("internal error? cannot generate `%s' relocation"),
+             bfd_get_reloc_code_name (fixp->fx_r_type));
+
+  return reloc;
+}
+
+\f
+/* Checks for resource conflicts between instructions.  */
+
+/* The func unit stuff could be implemented as bit-vectors rather
+   than the iterative approach here.  If it ends up being too
+   slow, we will switch it.  */
+
+resource_table *
+new_resource_table (void *data,
+                   int cycles,
+                   int nu,
+                   unit_num_copies_func uncf,
+                   opcode_num_units_func onuf,
+                   opcode_funcUnit_use_unit_func ouuf,
+                   opcode_funcUnit_use_stage_func ousf)
+{
+  int i;
+  resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
+  rt->data = data;
+  rt->cycles = cycles;
+  rt->allocated_cycles = cycles;
+  rt->num_units = nu;
+  rt->unit_num_copies = uncf;
+  rt->opcode_num_units = onuf;
+  rt->opcode_unit_use = ouuf;
+  rt->opcode_unit_stage = ousf;
+
+  rt->units = (unsigned char **) xcalloc (cycles, sizeof (unsigned char *));
+  for (i = 0; i < cycles; i++)
+    rt->units[i] = (unsigned char *) xcalloc (nu, sizeof (unsigned char));
+
+  return rt;
+}
+
+
+void
+clear_resource_table (resource_table *rt)
+{
+  int i, j;
+  for (i = 0; i < rt->allocated_cycles; i++)
+    for (j = 0; j < rt->num_units; j++)
+      rt->units[i][j] = 0;
+}
+
+
+/* We never shrink it, just fake it into thinking so.  */
+
+void
+resize_resource_table (resource_table *rt, int cycles)
+{
+  int i, old_cycles;
+
+  rt->cycles = cycles;
+  if (cycles <= rt->allocated_cycles)
+    return;
+
+  old_cycles = rt->allocated_cycles;
+  rt->allocated_cycles = cycles;
+
+  rt->units = xrealloc (rt->units,
+                       rt->allocated_cycles * sizeof (unsigned char *));
+  for (i = 0; i < old_cycles; i++)
+    rt->units[i] = xrealloc (rt->units[i],
+                            rt->num_units * sizeof (unsigned char));
+  for (i = old_cycles; i < cycles; i++)
+    rt->units[i] = xcalloc (rt->num_units, sizeof (unsigned char));
+}
+
+
+bfd_boolean
+resources_available (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+  int i;
+  int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+  for (i = 0; i < uses; i++)
+    {
+      xtensa_funcUnit unit = (rt->opcode_unit_use) (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 = (rt->unit_num_copies) (rt->data, unit);
+      if (copies_in_use >= copies)
+       return FALSE;
+    }
+  return TRUE;
+}
+
+
+void
+reserve_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+  int i;
+  int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+  for (i = 0; i < uses; i++)
+    {
+      xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+      int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+      /* Note that this allows resources to be oversubscribed.  That's
+        essential to the way the optional scheduler works.
+        resources_available reports when a resource is over-subscribed,
+        so it's easy to tell.  */
+      rt->units[stage + cycle][unit]++;
+    }
+}
+
+
+void
+release_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+  int i;
+  int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+  for (i = 0; i < uses; i++)
+    {
+      xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+      int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+      assert (rt->units[stage + cycle][unit] > 0);
+      rt->units[stage + cycle][unit]--;
+    }
+}
+
+
+/* Wrapper functions make parameterized resource reservation
+   more convenient.  */
+
+int
+opcode_funcUnit_use_unit (void *data, xtensa_opcode opcode, int idx)
+{
+  xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+  return use->unit;
+}
+
+
+int
+opcode_funcUnit_use_stage (void *data, xtensa_opcode opcode, int idx)
+{
+  xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+  return use->stage;
+}
+
+
+/* Note that this function does not check issue constraints, but
+   solely whether the hardware is available to execute the given
+   instructions together.  It also doesn't check if the tinsns
+   write the same state, or access the same tieports.  That is
+   checked by check_t1_t2_reads_and_writes.  */
+
+static bfd_boolean
+resources_conflict (vliw_insn *vinsn)
+{
+  int i;
+  static resource_table *rt = NULL;
+
+  /* This is the most common case by far.  Optimize it.  */
+  if (vinsn->num_slots == 1)
+    return FALSE;
+
+  if (rt == NULL)
+    {
+      xtensa_isa isa = xtensa_default_isa;
+      rt = new_resource_table
+       (isa, xtensa_isa_num_pipe_stages (isa),
+        xtensa_isa_num_funcUnits (isa),
+        (unit_num_copies_func) xtensa_funcUnit_num_copies,
+        (opcode_num_units_func) xtensa_opcode_num_funcUnit_uses,
+        opcode_funcUnit_use_unit,
+        opcode_funcUnit_use_stage);
+    }
+
+  clear_resource_table (rt);
+
+  for (i = 0; i < vinsn->num_slots; i++)
+    {
+      if (!resources_available (rt, vinsn->slots[i].opcode, 0))
+       return TRUE;
+      reserve_resources (rt, vinsn->slots[i].opcode, 0);
+    }
+
+  return FALSE;
+}
+
+\f
+/* finish_vinsn, emit_single_op and helper functions.  */
+
+static bfd_boolean find_vinsn_conflicts (vliw_insn *);
+static xtensa_format xg_find_narrowest_format (vliw_insn *);
+static void xg_assemble_vliw_tokens (vliw_insn *);
+
+
+/* We have reached the end of a bundle; emit into the frag.  */
+
+static void
+finish_vinsn (vliw_insn *vinsn)
+{
+  IStack slotstack;
+  int i;
+  char *file_name;
+  unsigned line;
+
+  if (find_vinsn_conflicts (vinsn))
+    {
+      xg_clear_vinsn (vinsn);
+      return;
+    }
+
+  /* First, find a format that works.  */
+  if (vinsn->format == XTENSA_UNDEFINED)
+    vinsn->format = xg_find_narrowest_format (vinsn);
+
+  if (vinsn->format == XTENSA_UNDEFINED)
+    {
+      as_where (&file_name, &line);
+      as_bad_where (file_name, line,
+                   _("couldn't find a valid instruction format"));
+      fprintf (stderr, _("    ops were: "));
+      for (i = 0; i < vinsn->num_slots; i++)
+       fprintf (stderr, _(" %s;"),
+                xtensa_opcode_name (xtensa_default_isa,
+                                    vinsn->slots[i].opcode));
+      fprintf (stderr, _("\n"));
+      xg_clear_vinsn (vinsn);
+      return;
+    }
+
+  if (vinsn->num_slots
+      != xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
+    {
+      as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
+             xtensa_format_name (xtensa_default_isa, vinsn->format),
+             xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
+             vinsn->num_slots);
+      xg_clear_vinsn (vinsn);
+      return;
+    }
+
+  if (resources_conflict (vinsn))
+    {
+      as_where (&file_name, &line);
+      as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+      fprintf (stderr, "    ops were: ");
+      for (i = 0; i < vinsn->num_slots; i++)
+       fprintf (stderr, " %s;",
+                xtensa_opcode_name (xtensa_default_isa,
+                                    vinsn->slots[i].opcode));
+      fprintf (stderr, "\n");
+      xg_clear_vinsn (vinsn);
+      return;
+    }
+
+  for (i = 0; i < vinsn->num_slots; i++)
+    {
+      if (vinsn->slots[i].opcode != XTENSA_UNDEFINED)
+       {
+         symbolS *lit_sym = NULL;
+         int j;
+         bfd_boolean e = FALSE;
+         bfd_boolean saved_density = density_supported;
+
+         /* We don't want to narrow ops inside multi-slot bundles.  */
+         if (vinsn->num_slots > 1)
+           density_supported = FALSE;
+
+         istack_init (&slotstack);
+         if (vinsn->slots[i].opcode == xtensa_nop_opcode)
+           {
+             vinsn->slots[i].opcode =
+               xtensa_format_slot_nop_opcode (xtensa_default_isa,
+                                              vinsn->format, i);
+             vinsn->slots[i].ntok = 0;
+           }
+
+         if (xg_expand_assembly_insn (&slotstack, &vinsn->slots[i]))
+           {
+             e = TRUE;
+             continue;
+           }
+
+         density_supported = saved_density;
+
+         if (e)
+           {
+             xg_clear_vinsn (vinsn);
+             return;
+           }
+
+         for (j = 0; j < slotstack.ninsn; j++)
+           {
+             TInsn *insn = &slotstack.insn[j];
+             if (insn->insn_type == ITYPE_LITERAL)
+               {
+                 assert (lit_sym == NULL);
+                 lit_sym = xg_assemble_literal (insn);
+               }
+             else
+               {
+                 assert (insn->insn_type == ITYPE_INSN);
+                 if (lit_sym)
+                   xg_resolve_literals (insn, lit_sym);
+                 if (j != slotstack.ninsn - 1)
+                   emit_single_op (insn);
+               }
+           }
+
+         if (vinsn->num_slots > 1)
+           {
+             if (opcode_fits_format_slot
+                 (slotstack.insn[slotstack.ninsn - 1].opcode,
+                  vinsn->format, i))
+               {
+                 vinsn->slots[i] = slotstack.insn[slotstack.ninsn - 1];
+               }
+             else
+               {
+                 emit_single_op (&slotstack.insn[slotstack.ninsn - 1]);
+                 if (vinsn->format == XTENSA_UNDEFINED)
+                   vinsn->slots[i].opcode = xtensa_nop_opcode;
+                 else
+                   vinsn->slots[i].opcode
+                     = xtensa_format_slot_nop_opcode (xtensa_default_isa,
+                                                      vinsn->format, i);
+
+                 vinsn->slots[i].ntok = 0;
+               }
+           }
+         else
+           {
+             vinsn->slots[0] = slotstack.insn[slotstack.ninsn - 1];
+             vinsn->format = XTENSA_UNDEFINED;
+           }
+       }
+    }
+
+  /* Now check resource conflicts on the modified bundle.  */
+  if (resources_conflict (vinsn))
+    {
+      as_where (&file_name, &line);
+      as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+      fprintf (stderr, "    ops were: ");
+      for (i = 0; i < vinsn->num_slots; i++)
+       fprintf (stderr, " %s;",
+                xtensa_opcode_name (xtensa_default_isa,
+                                    vinsn->slots[i].opcode));
+      fprintf (stderr, "\n");
+      xg_clear_vinsn (vinsn);
+      return;
+    }
+
+  /* First, find a format that works.  */
+  if (vinsn->format == XTENSA_UNDEFINED)
+      vinsn->format = xg_find_narrowest_format (vinsn);
+
+  xg_assemble_vliw_tokens (vinsn);
+
+  xg_clear_vinsn (vinsn);
+}
+
+
+/* Given an vliw instruction, what conflicts are there in register
+   usage and in writes to states and queues?
+
+   This function does two things:
+   1. Reports an error when a vinsn contains illegal combinations
+      of writes to registers states or queues.
+   2. Marks individual tinsns as not relaxable if the combination
+      contains antidependencies.
+
+   Job 2 handles things like swap semantics in instructions that need
+   to be relaxed.  For example,
+
+       addi a0, a1, 100000
+
+   normally would be relaxed to
+
+       l32r a0, some_label
+       add a0, a1, a0
+
+   _but_, if the above instruction is bundled with an a0 reader, e.g.,
+
+       { addi a0, a1, 10000 ; add a2, a0, a4 ; }
+
+   then we can't relax it into
+
+       l32r a0, some_label
+       { add a0, a1, a0 ; add a2, a0, a4 ; }
+
+   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 bfd_boolean
+find_vinsn_conflicts (vliw_insn *vinsn)
+{
+  int i, j;
+  int branches = 0;
+  xtensa_isa isa = xtensa_default_isa;
+
+  assert (!past_xtensa_end);
+
+  for (i = 0 ; i < vinsn->num_slots; i++)
+    {
+      TInsn *op1 = &vinsn->slots[i];
+      if (op1->is_specific_opcode)
+       op1->keep_wide = TRUE;
+      else
+       op1->keep_wide = FALSE;
+    }
+
+  for (i = 0 ; i < vinsn->num_slots; i++)
+    {
+      TInsn *op1 = &vinsn->slots[i];
+
+      if (xtensa_opcode_is_branch (isa, op1->opcode) == 1)
+       branches++;
+
+      for (j = 0; j < vinsn->num_slots; j++)
+       {
+         if (i != j)
+           {
+             TInsn *op2 = &vinsn->slots[j];
+             char conflict_type = check_t1_t2_reads_and_writes (op1, op2);
+             switch (conflict_type)
+               {
+               case 'c':
+                 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, op2->opcode), j);
+                 return TRUE;
+               case 'd':
+                 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, op2->opcode), j);
+                 return TRUE;
+               case 'e':
+                 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, op2->opcode), j);
+                 return TRUE;
+               case 'f':
+                 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, op2->opcode), j);
+                 return TRUE;
+               default:
+                 /* Everything is OK.  */
+                 break;
+               }
+             op2->is_specific_opcode = (op2->is_specific_opcode
+                                        || conflict_type == 'a');
+           }
+       }
+    }
+
+  if (branches > 1)
+    {
+      as_bad (_("multiple branches or jumps in the same bundle"));
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+/* Check how the state used by t1 and t2 relate.
+   Cases found are:
+
+   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
+           read the same reg though)
+   case C: t1 writes a register t2 writes (a register conflict within a
+           bundle)
+   case D: t1 writes a state that t2 also writes
+   case E: t1 writes a tie queue that t2 also writes
+   case F: two volatile queue accesses
+*/
+
+static char
+check_t1_t2_reads_and_writes (TInsn *t1, TInsn *t2)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_regfile t1_regfile, t2_regfile;
+  int t1_reg, t2_reg;
+  int t1_base_reg, t1_last_reg;
+  int t2_base_reg, t2_last_reg;
+  char t1_inout, t2_inout;
+  int i, j;
+  char conflict = 'b';
+  int t1_states;
+  int t2_states;
+  int t1_interfaces;
+  int t2_interfaces;
+  bfd_boolean t1_volatile = FALSE;
+  bfd_boolean t2_volatile = FALSE;
+
+  /* Check registers.  */
+  for (j = 0; j < t2->ntok; j++)
+    {
+      if (xtensa_operand_is_register (isa, t2->opcode, j) != 1)
+       continue;
+
+      t2_regfile = xtensa_operand_regfile (isa, t2->opcode, j);
+      t2_base_reg = t2->tok[j].X_add_number;
+      t2_last_reg = t2_base_reg + xtensa_operand_num_regs (isa, t2->opcode, j);
+
+      for (i = 0; i < t1->ntok; i++)
+       {
+         if (xtensa_operand_is_register (isa, t1->opcode, i) != 1)
+           continue;
+
+         t1_regfile = xtensa_operand_regfile (isa, t1->opcode, i);
+
+         if (t1_regfile != t2_regfile)
+           continue;
+
+         t1_inout = xtensa_operand_inout (isa, t1->opcode, i);
+         t2_inout = xtensa_operand_inout (isa, t2->opcode, j);
+
+         if (xtensa_operand_is_known_reg (isa, t1->opcode, i) == 0
+             || xtensa_operand_is_known_reg (isa, t2->opcode, j) == 0)
+           {
+             if (t1_inout == 'm' || t1_inout == 'o'
+                 || t2_inout == 'm' || t2_inout == 'o')
+               {
+                 conflict = 'a';
+                 continue;
+               }
+           }
+
+         t1_base_reg = t1->tok[i].X_add_number;
+         t1_last_reg = (t1_base_reg
+                        + xtensa_operand_num_regs (isa, t1->opcode, i));
+
+         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++)
+               {
+                 if (t1_reg != t2_reg)
+                   continue;
+
+                 if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+                   {
+                     conflict = 'a';
+                     continue;
+                   }
+
+                 if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+                   {
+                     conflict = 'a';
+                     continue;
+                   }
+
+                 if (t1_inout != 'i' && t2_inout != 'i')
+                   return 'c';
+               }
+           }
+       }
+    }
+
+  /* Check states.  */
+  t1_states = xtensa_opcode_num_stateOperands (isa, t1->opcode);
+  t2_states = xtensa_opcode_num_stateOperands (isa, t2->opcode);
+  for (j = 0; j < t2_states; j++)
+    {
+      xtensa_state t2_so = xtensa_stateOperand_state (isa, t2->opcode, j);
+      t2_inout = xtensa_stateOperand_inout (isa, t2->opcode, j);
+      for (i = 0; i < t1_states; i++)
+       {
+         xtensa_state t1_so = xtensa_stateOperand_state (isa, t1->opcode, i);
+         t1_inout = xtensa_stateOperand_inout (isa, t1->opcode, i);
+         if (t1_so != t2_so)
+           continue;
+
+         if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+           {
+             conflict = 'a';
+             continue;
+           }
+
+         if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+           {
+             conflict = 'a';
+             continue;
+           }
+
+         if (t1_inout != 'i' && t2_inout != 'i')
+           return 'd';
+       }
+    }
+
+  /* Check tieports.  */
+  t1_interfaces = xtensa_opcode_num_interfaceOperands (isa, t1->opcode);
+  t2_interfaces = xtensa_opcode_num_interfaceOperands (isa, t2->opcode);
+  for (j = 0; j < t2_interfaces; j++)
+    {
+      xtensa_interface t2_int
+       = xtensa_interfaceOperand_interface (isa, t2->opcode, j);
+      int t2_class = xtensa_interface_class_id (isa, t2_int);
+
+      t2_inout = xtensa_interface_inout (isa, t2_int);
+      if (xtensa_interface_has_side_effect (isa, t2_int) == 1)
+       t2_volatile = TRUE;
+
+      for (i = 0; i < t1_interfaces; i++)
+       {
+         xtensa_interface t1_int
+           = xtensa_interfaceOperand_interface (isa, t1->opcode, j);
+         int t1_class = xtensa_interface_class_id (isa, t1_int);
+
+         t1_inout = xtensa_interface_inout (isa, t1_int);
+         if (xtensa_interface_has_side_effect (isa, t1_int) == 1)
+           t1_volatile = TRUE;
+
+         if (t1_volatile && t2_volatile && (t1_class == t2_class))
+           return 'f';
+
+         if (t1_int != t2_int)
+           continue;
+
+         if (t2_inout == 'i' && t1_inout == 'o')
+           {
+             conflict = 'a';
+             continue;
+           }
+
+         if (t1_inout == 'i' && t2_inout == 'o')
+           {
+             conflict = 'a';
+             continue;
+           }
+
+         if (t1_inout != 'i' && t2_inout != 'i')
+           return 'e';
+       }
+    }
+
+  return conflict;
+}
+
+
+static xtensa_format
+xg_find_narrowest_format (vliw_insn *vinsn)
+{
+  /* Right now we assume that the ops within the vinsn are properly
+     ordered for the slots that the programmer wanted them in.  In
+     other words, we don't rearrange the ops in hopes of finding a
+     better format.  The scheduler handles that.  */
+
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format format;
+  vliw_insn v_copy = *vinsn;
+  xtensa_opcode nop_opcode = xtensa_nop_opcode;
+
+  if (vinsn->num_slots == 1)
+    return xg_get_single_format (vinsn->slots[0].opcode);
+
+  for (format = 0; format < xtensa_isa_num_formats (isa); format++)
+    {
+      v_copy = *vinsn;
+      if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
+       {
+         int slot;
+         int fit = 0;
+         for (slot = 0; slot < v_copy.num_slots; slot++)
+           {
+             if (v_copy.slots[slot].opcode == nop_opcode)
+               {
+                 v_copy.slots[slot].opcode =
+                   xtensa_format_slot_nop_opcode (isa, format, slot);
+                 v_copy.slots[slot].ntok = 0;
+               }
+
+             if (opcode_fits_format_slot (v_copy.slots[slot].opcode,
+                                          format, slot))
+               fit++;
+             else if (v_copy.num_slots > 1)
+               {
+                 TInsn widened;
+                 /* Try the widened version.  */
+                 if (!v_copy.slots[slot].keep_wide
+                     && !v_copy.slots[slot].is_specific_opcode
+                     && xg_is_single_relaxable_insn (&v_copy.slots[slot],
+                                                     &widened, TRUE)
+                     && opcode_fits_format_slot (widened.opcode,
+                                                 format, slot))
+                   {
+                     v_copy.slots[slot] = widened;
+                     fit++;
+                   }
+               }
+           }
+         if (fit == v_copy.num_slots)
+           {
+             *vinsn = v_copy;
+             xtensa_format_encode (isa, format, vinsn->insnbuf);
+             vinsn->format = format;
+             break;
+           }
+       }
+    }
+
+  if (format == xtensa_isa_num_formats (isa))
+    return XTENSA_UNDEFINED;
+
+  return format;
+}
+
+
+/* Return the additional space needed in a frag
+   for possible relaxations of any ops in a VLIW insn.
+   Also fill out the relaxations that might be required of
+   each tinsn in the vinsn.  */
+
+static int
+relaxation_requirements (vliw_insn *vinsn, bfd_boolean *pfinish_frag)
+{
+  bfd_boolean finish_frag = FALSE;
+  int extra_space = 0;
+  int slot;
+
+  for (slot = 0; slot < vinsn->num_slots; slot++)
+    {
+      TInsn *tinsn = &vinsn->slots[slot];
+      if (!tinsn_has_symbolic_operands (tinsn))
+       {
+         /* A narrow instruction could be widened later to help
+            alignment issues.  */
+         if (xg_is_single_relaxable_insn (tinsn, 0, TRUE)
+             && !tinsn->is_specific_opcode
+             && vinsn->num_slots == 1)
+           {
+             /* Difference in bytes between narrow and wide insns...  */
+             extra_space += 1;
+             tinsn->subtype = RELAX_NARROW;
+           }
+       }
+      else
+       {
+         if (workaround_b_j_loop_end
+             && tinsn->opcode == xtensa_jx_opcode
+             && use_transform ())
+           {
+             /* Add 2 of these.  */
+             extra_space += 3; /* for the nop size */
+             tinsn->subtype = RELAX_ADD_NOP_IF_PRE_LOOP_END;
+           }
+
+         /* Need to assemble it with space for the relocation.  */
+         if (xg_is_relaxable_insn (tinsn, 0)
+             && !tinsn->is_specific_opcode)
+           {
+             int max_size = xg_get_max_insn_widen_size (tinsn->opcode);
+             int max_literal_size =
+               xg_get_max_insn_widen_literal_size (tinsn->opcode);
+
+             tinsn->literal_space = max_literal_size;
+
+             tinsn->subtype = RELAX_IMMED;
+             extra_space += max_size;
+           }
+         else
+           {
+             /* A fix record will be added for this instruction prior
+                to relaxation, so make it end the frag.  */
+             finish_frag = TRUE;
+           }
+       }
+    }
+  *pfinish_frag = finish_frag;
+  return extra_space;
+}
+
+
+static void
+bundle_tinsn (TInsn *tinsn, vliw_insn *vinsn)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  int slot, chosen_slot;
+
+  vinsn->format = xg_get_single_format (tinsn->opcode);
+  assert (vinsn->format != XTENSA_UNDEFINED);
+  vinsn->num_slots = xtensa_format_num_slots (isa, vinsn->format);
+
+  chosen_slot = xg_get_single_slot (tinsn->opcode);
+  for (slot = 0; slot < vinsn->num_slots; slot++)
+    {
+      if (slot == chosen_slot)
+       vinsn->slots[slot] = *tinsn;
+      else
+       {
+         vinsn->slots[slot].opcode =
+           xtensa_format_slot_nop_opcode (isa, vinsn->format, slot);
+         vinsn->slots[slot].ntok = 0;
+         vinsn->slots[slot].insn_type = ITYPE_INSN;
+       }
+    }
+}
+
+
+static bfd_boolean
+emit_single_op (TInsn *orig_insn)
+{
+  int i;
+  IStack istack;               /* put instructions into here */
+  symbolS *lit_sym = NULL;
+  symbolS *label_sym = NULL;
+
+  istack_init (&istack);
+
+  /* Special-case for "movi aX, foo" which is guaranteed to need relaxing.
+     Because the scheduling and bundling characteristics of movi and
+     l32r or const16 are so different, we can do much better if we relax
+     it prior to scheduling and bundling, rather than after.  */
+  if ((orig_insn->opcode == xtensa_movi_opcode
+       || orig_insn->opcode == xtensa_movi_n_opcode)
+      && !cur_vinsn.inside_bundle
+      && (orig_insn->tok[1].X_op == O_symbol
+         || orig_insn->tok[1].X_op == O_pltrel)
+      && !orig_insn->is_specific_opcode && use_transform ())
+    xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
+  else
+    if (xg_expand_assembly_insn (&istack, orig_insn))
+      return TRUE;
+
+  for (i = 0; i < istack.ninsn; i++)
+    {
+      TInsn *insn = &istack.insn[i];
+      switch (insn->insn_type)
+       {
+       case ITYPE_LITERAL:
+         assert (lit_sym == NULL);
+         lit_sym = xg_assemble_literal (insn);
+         break;
+       case ITYPE_LABEL:
+         {
+           static int relaxed_sym_idx = 0;
+           char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
+           sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
+           colon (label);
+           assert (label_sym == NULL);
+           label_sym = symbol_find_or_make (label);
+           assert (label_sym);
+           free (label);
+         }
+         break;
+       case ITYPE_INSN:
+         {
+           vliw_insn v;
+           if (lit_sym)
+             xg_resolve_literals (insn, lit_sym);
+           if (label_sym)
+             xg_resolve_labels (insn, label_sym);
+           xg_init_vinsn (&v);
+           bundle_tinsn (insn, &v);
+           finish_vinsn (&v);
+           xg_free_vinsn (&v);
+         }
+         break;
+       default:
+         assert (0);
+         break;
+       }
+    }
+  return FALSE;
+}
+
+
+static int
+total_frag_text_expansion (fragS *fragP)
+{
+  int slot;
+  int total_expansion = 0;
+
+  for (slot = 0; slot < MAX_SLOTS; slot++)
+    total_expansion += fragP->tc_frag_data.text_expansion[slot];
+
+  return total_expansion;
+}
+
+
+/* Emit a vliw instruction to the current fragment.  */
+
+static void
+xg_assemble_vliw_tokens (vliw_insn *vinsn)
+{
+  bfd_boolean finish_frag;
+  bfd_boolean is_jump = FALSE;
+  bfd_boolean is_branch = FALSE;
+  xtensa_isa isa = xtensa_default_isa;
+  int i;
+  int insn_size;
+  int extra_space;
+  char *f = NULL;
+  int slot;
+  unsigned current_line, best_linenum;
+  char *current_file;
+
+  best_linenum = UINT_MAX;
+
+  if (generating_literals)
+    {
+      static int reported = 0;
+      if (reported < 4)
+       as_bad_where (frag_now->fr_file, frag_now->fr_line,
+                     _("cannot assemble into a literal fragment"));
+      if (reported == 3)
+       as_bad (_("..."));
+      reported++;
+      return;
+    }
+
+  if (frag_now_fix () != 0
+      && (! frag_now->tc_frag_data.is_insn
+         || (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+         || !use_transform () != frag_now->tc_frag_data.is_no_transform
+         || (directive_state[directive_longcalls]
+             != frag_now->tc_frag_data.use_longcalls)
+         || (directive_state[directive_absolute_literals]
+             != frag_now->tc_frag_data.use_absolute_literals)))
+    {
+      frag_wane (frag_now);
+      frag_new (0);
+      xtensa_set_frag_assembly_state (frag_now);
+    }
+
+  if (workaround_a0_b_retw
+      && vinsn->num_slots == 1
+      && (get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
+      && xtensa_opcode_is_branch (isa, vinsn->slots[0].opcode) == 1
+      && use_transform ())
+    {
+      has_a0_b_retw = TRUE;
+
+      /* 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
+        add a nop if needed.  */
+      frag_now->tc_frag_data.is_insn = TRUE;
+      frag_var (rs_machine_dependent, 4, 4,
+               RELAX_ADD_NOP_IF_A0_B_RETW,
+               frag_now->fr_symbol,
+               frag_now->fr_offset,
+               NULL);
+      xtensa_set_frag_assembly_state (frag_now);
+      frag_now->tc_frag_data.is_insn = TRUE;
+      frag_var (rs_machine_dependent, 4, 4,
+               RELAX_ADD_NOP_IF_A0_B_RETW,
+               frag_now->fr_symbol,
+               frag_now->fr_offset,
+               NULL);
+      xtensa_set_frag_assembly_state (frag_now);
+    }
+
+  for (i = 0; i < vinsn->num_slots; i++)
     {
     {
-      /* This happens when the relocation is within the current section. 
-         It seems this implies a PCREL operation.  We'll catch it and error 
-         if not.  */
-
-      char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
-      static xtensa_insnbuf insnbuf = NULL;
-      xtensa_opcode opcode;
-      xtensa_isa isa;
-
-      switch (fixP->fx_r_type)
-       {
-       case BFD_RELOC_XTENSA_ASM_EXPAND:
-         fixP->fx_done = 1;
-         break;
+      /* See if the instruction implies an aligned section.  */
+      if (xtensa_opcode_is_loop (isa, vinsn->slots[i].opcode) == 1)
+       record_alignment (now_seg, 2);
 
 
-       case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
-         as_bad (_("unhandled local relocation fix %s"),
-                 bfd_get_reloc_code_name (fixP->fx_r_type));
-         break;
-
-       case BFD_RELOC_32:
-       case BFD_RELOC_16:
-       case BFD_RELOC_8:
-         /* The only one we support that isn't an instruction field.  */
-         md_number_to_chars (fixpos, *valP, fixP->fx_size);
-         fixP->fx_done = 1;
-         break;
-
-       case BFD_RELOC_XTENSA_OP0:
-       case BFD_RELOC_XTENSA_OP1:
-       case BFD_RELOC_XTENSA_OP2:
-         isa = xtensa_default_isa;
-         if (!insnbuf)
-           insnbuf = xtensa_insnbuf_alloc (isa);
+      /* Also determine the best line number for debug info.  */
+      best_linenum = vinsn->slots[i].linenum < best_linenum
+       ? vinsn->slots[i].linenum : best_linenum;
+    }
 
 
-         xtensa_insnbuf_from_chars (isa, insnbuf, fixpos);
-         opcode = xtensa_decode_insn (isa, insnbuf);
-         if (opcode == XTENSA_UNDEFINED)
-           as_fatal (_("undecodable FIX"));
+  /* Special cases for instructions that force an alignment... */
+  /* None of these opcodes are bundle-able.  */
+  if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1)
+    {
+      int max_fill;
 
 
-         xtensa_insnbuf_set_immediate_field (opcode, insnbuf, *valP,
-                                             fixP->fx_file, fixP->fx_line);
+      /* 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
+        the end of the loop at the beginning, without adding special code
+        to mark the loop instructions themselves.  */
+      symbolS *target_sym = NULL;
+      if (vinsn->slots[0].tok[1].X_op == O_symbol)
+       target_sym = vinsn->slots[0].tok[1].X_add_symbol;
 
 
-         fixP->fx_frag->tc_frag_data.is_insn = TRUE;
-         xtensa_insnbuf_to_chars (isa, insnbuf, fixpos);
-         fixP->fx_done = 1;
-         break;
+      xtensa_set_frag_assembly_state (frag_now);
+      frag_now->tc_frag_data.is_insn = TRUE;
 
 
-       case BFD_RELOC_VTABLE_INHERIT:
-       case BFD_RELOC_VTABLE_ENTRY:
-         fixP->fx_done = 0;
-         break;
+      max_fill = get_text_align_max_fill_size
+       (get_text_align_power (xtensa_fetch_width),
+        TRUE, frag_now->tc_frag_data.is_no_density);
 
 
-       default:
-         as_bad (_("unhandled local relocation fix %s"),
-                 bfd_get_reloc_code_name (fixP->fx_r_type));
-       }
+      if (use_transform ())
+       frag_var (rs_machine_dependent, max_fill, max_fill,
+                 RELAX_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
+      else
+       frag_var (rs_machine_dependent, 0, 0,
+                 RELAX_CHECK_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
+      xtensa_set_frag_assembly_state (frag_now);
     }
     }
-}
-
-
-char *
-md_atof (type, litP, sizeP)
-     int type;
-     char *litP;
-     int *sizeP;
-{
-  int prec;
-  LITTLENUM_TYPE words[4];
-  char *t;
-  int i;
 
 
-  switch (type)
+  if (vinsn->slots[0].opcode == xtensa_entry_opcode
+      && !vinsn->slots[0].is_specific_opcode)
     {
     {
-    case 'f':
-      prec = 2;
-      break;
+      xtensa_mark_literal_pool_location ();
+      xtensa_move_labels (frag_now, 0);
+      frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
+    }
 
 
-    case 'd':
-      prec = 4;
-      break;
+  if (vinsn->num_slots == 1)
+    {
+      if (workaround_a0_b_retw && use_transform ())
+       set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
+                            is_register_writer (&vinsn->slots[0], "a", 0));
 
 
-    default:
-      *sizeP = 0;
-      return "bad call to md_atof";
+      set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
+                          is_bad_loopend_opcode (&vinsn->slots[0]));
     }
     }
+  else
+    set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND, FALSE);
 
 
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
+  insn_size = xtensa_format_length (isa, vinsn->format);
 
 
-  *sizeP = prec * 2;
+  extra_space = relaxation_requirements (vinsn, &finish_frag);
 
 
-  for (i = prec - 1; i >= 0; i--)
+  /* vinsn_to_insnbuf will produce the error.  */
+  if (vinsn->format != XTENSA_UNDEFINED)
     {
     {
-      int idx = i;
-      if (target_big_endian)
-       idx = (prec - 1 - i);
-
-      md_number_to_chars (litP, (valueT) words[idx], 2);
-      litP += 2;
+      f = frag_more (insn_size + extra_space);
+      xtensa_set_frag_assembly_state (frag_now);
+      frag_now->tc_frag_data.is_insn = TRUE;
     }
 
     }
 
-  return NULL;
-}
-
+  vinsn_to_insnbuf (vinsn, f, frag_now, FALSE);
+  if (vinsn->format == XTENSA_UNDEFINED)
+    return;
 
 
-int
-md_estimate_size_before_relax (fragP, seg)
-     fragS *fragP;
-     segT seg ATTRIBUTE_UNUSED;
-{
-  return fragP->tc_frag_data.text_expansion;
-}
+  xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, (unsigned char *) f, 0);
 
 
+  /* Temporarily set the logical line number to the one we want to appear
+     in the debug information.  */
+  as_where (&current_file, &current_line);
+  new_logical_line (current_file, best_linenum);
+  dwarf2_emit_insn (insn_size + extra_space);
+  new_logical_line (current_file, current_line);
 
 
-/* Translate internal representation of relocation info to BFD target
-   format.  */
+  for (slot = 0; slot < vinsn->num_slots; slot++)
+    {
+      TInsn *tinsn = &vinsn->slots[slot];
+      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_offsets[slot] = tinsn->offset;
+      frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
+      if (tinsn->literal_space != 0)
+       xg_assemble_literal_space (tinsn->literal_space, slot);
 
 
-arelent *
-tc_gen_reloc (section, fixp)
-     asection *section ATTRIBUTE_UNUSED;
-     fixS *fixp;
-{
-  arelent *reloc;
+      if (tinsn->subtype == RELAX_NARROW)
+       assert (vinsn->num_slots == 1);
+      if (xtensa_opcode_is_jump (isa, tinsn->opcode) == 1)
+       is_jump = TRUE;
+      if (xtensa_opcode_is_branch (isa, tinsn->opcode) == 1)
+       is_branch = TRUE;
 
 
-  reloc = (arelent *) xmalloc (sizeof (arelent));
-  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
-  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
-  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+      if (tinsn->subtype || tinsn->symbol || tinsn->offset
+         || tinsn->literal_frag || is_jump || is_branch)
+       finish_frag = TRUE;
+    }
 
 
-  /* Make sure none of our internal relocations make it this far.
-     They'd better have been fully resolved by this point.  */
-  assert ((int) fixp->fx_r_type > 0);
+  if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+    frag_now->tc_frag_data.is_specific_opcode = TRUE;
 
 
-  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
-  if (reloc->howto == NULL)
+  if (finish_frag)
     {
     {
-      as_bad_where (fixp->fx_file, fixp->fx_line,
-                   _("cannot represent `%s' relocation in object file"),
-                   bfd_get_reloc_code_name (fixp->fx_r_type));
-      return NULL;
+      frag_variant (rs_machine_dependent,
+                   extra_space, extra_space, RELAX_SLOTS,
+                   frag_now->fr_symbol, frag_now->fr_offset, f);
+      xtensa_set_frag_assembly_state (frag_now);
     }
 
     }
 
-  if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+  /* Special cases for loops:
+     close_loop_end should be inserted AFTER short_loop.
+     Make sure that CLOSE loops are processed BEFORE short_loops
+     when converting them.  */
+
+  /* "short_loop": Add a NOP if the loop is < 4 bytes.  */
+  if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1
+      && !vinsn->slots[0].is_specific_opcode)
     {
     {
-      as_fatal (_("internal error? cannot generate `%s' relocation"),
-               bfd_get_reloc_code_name (fixp->fx_r_type));
-    }
-  assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
+      if (workaround_short_loop && use_transform ())
+       {
+         maybe_has_short_loop = TRUE;
+         frag_now->tc_frag_data.is_insn = TRUE;
+         frag_var (rs_machine_dependent, 4, 4,
+                   RELAX_ADD_NOP_IF_SHORT_LOOP,
+                   frag_now->fr_symbol, frag_now->fr_offset, NULL);
+         frag_now->tc_frag_data.is_insn = TRUE;
+         frag_var (rs_machine_dependent, 4, 4,
+                   RELAX_ADD_NOP_IF_SHORT_LOOP,
+                   frag_now->fr_symbol, frag_now->fr_offset, NULL);
+       }
 
 
-  reloc->addend = fixp->fx_offset;
+      /* "close_loop_end": Add up to 12 bytes of NOPs to keep a
+        loop at least 12 bytes away from another loop's end.  */
+      if (workaround_close_loop_end && use_transform ())
+       {
+         maybe_has_close_loop_end = TRUE;
+         frag_now->tc_frag_data.is_insn = TRUE;
+         frag_var (rs_machine_dependent, 12, 12,
+                   RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
+                   frag_now->fr_symbol, frag_now->fr_offset, NULL);
+       }
+    }
 
 
-  switch (fixp->fx_r_type)
+  if (use_transform ())
     {
     {
-    case BFD_RELOC_XTENSA_OP0:
-    case BFD_RELOC_XTENSA_OP1:
-    case BFD_RELOC_XTENSA_OP2:
-    case BFD_RELOC_XTENSA_ASM_EXPAND:
-    case BFD_RELOC_32: 
-    case BFD_RELOC_XTENSA_PLT: 
-    case BFD_RELOC_VTABLE_INHERIT:
-    case BFD_RELOC_VTABLE_ENTRY:
-      break;
-
-    case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
-      as_warn (_("emitting simplification relocation"));
-      break;
+      if (is_jump)
+       {
+         assert (finish_frag);
+         frag_var (rs_machine_dependent,
+                   UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
+                   RELAX_UNREACHABLE,
+                   frag_now->fr_symbol, frag_now->fr_offset, NULL);
+         xtensa_set_frag_assembly_state (frag_now);
+       }
+      else if (is_branch && do_align_targets ())
+       {
+         assert (finish_frag);
+         frag_var (rs_machine_dependent,
+                   UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
+                   RELAX_MAYBE_UNREACHABLE,
+                   frag_now->fr_symbol, frag_now->fr_offset, NULL);
+         xtensa_set_frag_assembly_state (frag_now);
+         frag_var (rs_machine_dependent,
+                   0, 0,
+                   RELAX_MAYBE_DESIRE_ALIGN,
+                   frag_now->fr_symbol, frag_now->fr_offset, NULL);
+         xtensa_set_frag_assembly_state (frag_now);
+       }
+    }
 
 
-    default:
-      as_warn (_("emitting unknown relocation"));
+  /* Now, if the original opcode was a call...  */
+  if (do_align_targets ()
+      && xtensa_opcode_is_call (isa, vinsn->slots[0].opcode) == 1)
+    {
+      float freq = get_subseg_total_freq (now_seg, now_subseg);
+      frag_now->tc_frag_data.is_insn = TRUE;
+      frag_var (rs_machine_dependent, 4, (int) freq, RELAX_DESIRE_ALIGN,
+               frag_now->fr_symbol, frag_now->fr_offset, NULL);
+      xtensa_set_frag_assembly_state (frag_now);
     }
 
     }
 
-  return reloc;
+  if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+    {
+      frag_wane (frag_now);
+      frag_new (0);
+      xtensa_set_frag_assembly_state (frag_now);
+    }
 }
 
 \f
 }
 
 \f
+/* xtensa_end and helper functions.  */
+
+static void xtensa_cleanup_align_frags (void);
+static void xtensa_fix_target_frags (void);
+static void xtensa_mark_narrow_branches (void);
+static void xtensa_mark_zcl_first_insns (void);
+static void xtensa_fix_a0_b_retw_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_short_loop_frags (void);
+static void xtensa_sanity_check (void);
+static void xtensa_add_config_info (void);
+
 void
 void
-xtensa_end ()
+xtensa_end (void)
 {
   directive_balance ();
 {
   directive_balance ();
+  xtensa_flush_pending_output ();
+
+  past_xtensa_end = TRUE;
+
   xtensa_move_literals ();
 
   xtensa_reorder_segments ();
   xtensa_cleanup_align_frags ();
   xtensa_fix_target_frags ();
   xtensa_move_literals ();
 
   xtensa_reorder_segments ();
   xtensa_cleanup_align_frags ();
   xtensa_fix_target_frags ();
-  if (software_a0_b_retw_interlock && 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 (software_avoid_b_j_loop_end && maybe_has_b_j_loop_end)
+  if (workaround_b_j_loop_end)
     xtensa_fix_b_j_loop_end_frags ();
 
   /* "close_loop_end" should be processed BEFORE "short_loop".  */
     xtensa_fix_b_j_loop_end_frags ();
 
   /* "close_loop_end" should be processed BEFORE "short_loop".  */
-  if (software_avoid_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 (software_avoid_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)
+    xtensa_mark_narrow_branches ();
+  xtensa_mark_zcl_first_insns ();
 
   xtensa_sanity_check ();
 
   xtensa_sanity_check ();
+
+  xtensa_add_config_info ();
 }
 
 
 static void
 }
 
 
 static void
-xtensa_cleanup_align_frags ()
+xtensa_cleanup_align_frags (void)
 {
   frchainS *frchP;
 {
   frchainS *frchP;
+  asection *s;
 
 
-  for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
-    {
-      fragS *fragP;
-
-      /* Walk over all of the fragments in a subsection.  */
-      for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
-       {
-         if ((fragP->fr_type == rs_align
-              || fragP->fr_type == rs_align_code
-              || (fragP->fr_type == rs_machine_dependent
-                  && (fragP->fr_subtype == RELAX_DESIRE_ALIGN
-                      || fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
-             && fragP->fr_fix == 0)
-           {
-             fragS * next = fragP->fr_next;
-
-             while (next
-                    && next->fr_type == rs_machine_dependent 
-                    && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET) 
-               {
-                 frag_wane (next);
-                 next = next->fr_next;
-               }
-           }
-       }
-    }
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if ((fragP->fr_type == rs_align
+                || fragP->fr_type == rs_align_code
+                || (fragP->fr_type == rs_machine_dependent
+                    && (fragP->fr_subtype == RELAX_DESIRE_ALIGN
+                        || fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
+               && fragP->fr_fix == 0)
+             {
+               fragS *next = fragP->fr_next;
+
+               while (next
+                      && next->fr_fix == 0
+                      && next->fr_type == rs_machine_dependent
+                      && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
+                 {
+                   frag_wane (next);
+                   next = next->fr_next;
+                 }
+             }
+           /* If we don't widen branch targets, then they
+              will be easier to align.  */
+           if (fragP->tc_frag_data.is_branch_target
+               && fragP->fr_opcode == fragP->fr_literal
+               && fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_SLOTS
+               && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+             frag_wane (fragP);
+           if (fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_UNREACHABLE)
+             fragP->tc_frag_data.is_unreachable = TRUE;
+         }
+      }
 }
 
 
 /* Re-process all of the fragments looking to convert all of the
    RELAX_DESIRE_ALIGN_IF_TARGET fragments.  If there is a branch
    target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
 }
 
 
 /* Re-process all of the fragments looking to convert all of the
    RELAX_DESIRE_ALIGN_IF_TARGET fragments.  If there is a branch
    target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
-   If the next fragment starts with a loop target, AND the previous
-   fragment can be expanded to negate the branch, convert this to a
-   RELAX_LOOP_END.  Otherwise, convert to a .fill 0.  */
+   Otherwise, convert to a .fill 0.  */
 
 static void
 
 static void
-xtensa_fix_target_frags ()
+xtensa_fix_target_frags (void)
 {
   frchainS *frchP;
 {
   frchainS *frchP;
+  asection *s;
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
-  for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
-    {
-      bfd_boolean prev_frag_can_negate_branch = FALSE;
-      fragS *fragP;
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
 
 
-      /* Walk over all of the fragments in a subsection.  */
-      for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
-       {
-         if (fragP->fr_type == rs_machine_dependent
-             && fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
-           {
-             if (next_frag_is_loop_target (fragP))
-               {
-                 if (prev_frag_can_negate_branch)
-                   fragP->fr_subtype = RELAX_LOOP_END;
-                 else
-                   {
-                     if (!align_only_targets ||
-                         next_frag_is_branch_target (fragP))
-                       fragP->fr_subtype = RELAX_DESIRE_ALIGN;
-                     else
-                       frag_wane (fragP);
-                   }
-               }
-             else if (!align_only_targets
-                      || next_frag_is_branch_target (fragP))
-               fragP->fr_subtype = RELAX_DESIRE_ALIGN;
-             else
-               frag_wane (fragP);
-           }
-         if (fragP->fr_fix != 0)
-           prev_frag_can_negate_branch = FALSE;
-         if (frag_can_negate_branch (fragP))
-           prev_frag_can_negate_branch = TRUE;
-       }
-    }
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if (fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
+             {
+               if (next_frag_is_branch_target (fragP))
+                 fragP->fr_subtype = RELAX_DESIRE_ALIGN;
+               else
+                 frag_wane (fragP);
+             }
+         }
+      }
+}
+
+
+static bfd_boolean is_narrow_branch_guaranteed_in_range (fragS *, TInsn *);
+
+static void
+xtensa_mark_narrow_branches (void)
+{
+  frchainS *frchP;
+  asection *s;
+
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if (fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_SLOTS
+               && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
+             {
+               vliw_insn vinsn;
+
+               vinsn_from_chars (&vinsn, fragP->fr_opcode);
+               tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
+
+               if (vinsn.num_slots == 1
+                   && xtensa_opcode_is_branch (xtensa_default_isa,
+                                               vinsn.slots[0].opcode) == 1
+                   && xg_get_single_size (vinsn.slots[0].opcode) == 2
+                   && is_narrow_branch_guaranteed_in_range (fragP,
+                                                            &vinsn.slots[0]))
+                 {
+                   fragP->fr_subtype = RELAX_SLOTS;
+                   fragP->tc_frag_data.slot_subtypes[0] = RELAX_NARROW;
+                   fragP->tc_frag_data.is_aligning_branch = 1;
+                 }
+             }
+         }
+      }
 }
 
 
 }
 
 
+/* A branch is typically widened only when its target is out of
+   range.  However, we would like to widen them to align a subsequent
+   branch target when possible.
+
+   Because the branch relaxation code is so convoluted, the optimal solution
+   (combining the two cases) is difficult to get right in all circumstances.
+   We therefore go with an "almost as good" solution, where we only
+   use for alignment narrow branches that definitely will not expand to a
+   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
+   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
+   maximum offset is (4 - 2) + 63 = 65.  */
+#define MAX_IMMED6 65
+
+static offsetT unrelaxed_frag_max_size (fragS *);
+
 static bfd_boolean
 static bfd_boolean
-frag_can_negate_branch (fragP)
-     fragS *fragP;
+is_narrow_branch_guaranteed_in_range (fragS *fragP, TInsn *tinsn)
 {
 {
-  if (fragP->fr_type == rs_machine_dependent
-      && fragP->fr_subtype == RELAX_IMMED)
+  const expressionS *expr = &tinsn->tok[1];
+  symbolS *symbolP = expr->X_add_symbol;
+  offsetT max_distance = expr->X_add_number;
+  fragS *target_frag;
+
+  if (expr->X_op != O_symbol)
+    return FALSE;
+
+  target_frag = symbol_get_frag (symbolP);
+
+  max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
+  if (is_branch_jmp_to_next (tinsn, fragP))
+    return FALSE;
+
+  /* The branch doesn't branch over it's own frag,
+     but over the subsequent ones.  */
+  fragP = fragP->fr_next;
+  while (fragP != NULL && fragP != target_frag && max_distance <= MAX_IMMED6)
     {
     {
-      TInsn t_insn;
-      tinsn_from_chars (&t_insn, fragP->fr_opcode);
-      if (is_negatable_branch (&t_insn))
-       return TRUE;
+      max_distance += unrelaxed_frag_max_size (fragP);
+      fragP = fragP->fr_next;
     }
     }
+  if (max_distance <= MAX_IMMED6 && fragP == target_frag)
+    return TRUE;
   return FALSE;
 }
 
 
   return FALSE;
 }
 
 
+static void
+xtensa_mark_zcl_first_insns (void)
+{
+  frchainS *frchP;
+  asection *s;
+
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if (fragP->fr_type == rs_machine_dependent
+               && (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
+                   || fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE))
+             {
+               /* Find the loop frag.  */
+               fragS *targ_frag = next_non_empty_frag (fragP);
+               /* Find the first insn frag.  */
+               targ_frag = next_non_empty_frag (targ_frag);
+
+               /* Of course, sometimes (mostly for toy test cases) a
+                  zero-cost loop instruction is the last in a section.  */
+               if (targ_frag)
+                 {
+                   targ_frag->tc_frag_data.is_first_loop_insn = TRUE;
+                   /* Do not widen a frag that is the first instruction of a
+                      zero-cost loop.  It makes that loop harder to align.  */
+                   if (targ_frag->fr_type == rs_machine_dependent
+                       && targ_frag->fr_subtype == RELAX_SLOTS
+                       && (targ_frag->tc_frag_data.slot_subtypes[0]
+                           == RELAX_NARROW))
+                     {
+                       if (targ_frag->tc_frag_data.is_aligning_branch)
+                         targ_frag->tc_frag_data.slot_subtypes[0] = RELAX_IMMED;
+                       else
+                         {
+                           frag_wane (targ_frag);
+                           targ_frag->tc_frag_data.slot_subtypes[0] = 0;
+                         }
+                     }
+                 }
+               if (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)
+                 frag_wane (fragP);
+             }
+         }
+      }
+}
+
+
 /* 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
    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.  */
 
 /* 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
    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.  */
 
+static bfd_boolean next_instrs_are_b_retw (fragS *);
+
 static void
 static void
-xtensa_fix_a0_b_retw_frags ()
+xtensa_fix_a0_b_retw_frags (void)
 {
   frchainS *frchP;
 {
   frchainS *frchP;
+  asection *s;
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
-  for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
-    {
-      fragS *fragP;
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
 
 
-      /* Walk over all of the fragments in a subsection.  */
-      for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
-       {
-         if (fragP->fr_type == rs_machine_dependent
-             && fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW)
-           {
-             if (next_instrs_are_b_retw (fragP))
-               relax_frag_add_nop (fragP);
-             else
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if (fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW)
+             {
+               if (next_instrs_are_b_retw (fragP))
+                 {
+                   if (fragP->tc_frag_data.is_no_transform)
+                     as_bad (_("instruction sequence (write a0, branch, retw) may trigger hardware errata"));
+                   else
+                     relax_frag_add_nop (fragP);
+                 }
                frag_wane (fragP);
                frag_wane (fragP);
-           }
-       }
-    }
+             }
+         }
+      }
 }
 
 
 }
 
 
-bfd_boolean
-next_instrs_are_b_retw (fragP)
-     fragS * fragP;
+static bfd_boolean
+next_instrs_are_b_retw (fragS *fragP)
 {
   xtensa_opcode opcode;
 {
   xtensa_opcode opcode;
+  xtensa_format fmt;
   const fragS *next_fragP = next_non_empty_frag (fragP);
   static xtensa_insnbuf insnbuf = NULL;
   const fragS *next_fragP = next_non_empty_frag (fragP);
   static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
   xtensa_isa isa = xtensa_default_isa;
   int offset = 0;
   xtensa_isa isa = xtensa_default_isa;
   int offset = 0;
+  int slot;
+  bfd_boolean branch_seen = FALSE;
 
   if (!insnbuf)
 
   if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (isa);
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
 
   if (next_fragP == NULL)
     return FALSE;
 
   /* Check for the conditional branch.  */
 
   if (next_fragP == NULL)
     return FALSE;
 
   /* Check for the conditional branch.  */
-  xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]);
-  opcode = xtensa_decode_insn (isa, insnbuf);
+  xtensa_insnbuf_from_chars
+    (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (fmt == XTENSA_UNDEFINED)
+    return FALSE;
+
+  for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+    {
+      xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+      opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+
+      branch_seen = (branch_seen
+                    || xtensa_opcode_is_branch (isa, opcode) == 1);
+    }
 
 
-  if (!is_conditional_branch_opcode (opcode))
+  if (!branch_seen)
     return FALSE;
 
     return FALSE;
 
-  offset += xtensa_insn_length (isa, opcode);
+  offset += xtensa_format_length (isa, fmt);
   if (offset == next_fragP->fr_fix)
     {
       next_fragP = next_non_empty_frag (next_fragP);
       offset = 0;
     }
   if (offset == next_fragP->fr_fix)
     {
       next_fragP = next_non_empty_frag (next_fragP);
       offset = 0;
     }
+
   if (next_fragP == NULL)
     return FALSE;
 
   /* Check for the retw/retw.n.  */
   if (next_fragP == NULL)
     return FALSE;
 
   /* Check for the retw/retw.n.  */
-  xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]);
-  opcode = xtensa_decode_insn (isa, insnbuf);
+  xtensa_insnbuf_from_chars
+    (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
+  fmt = xtensa_format_decode (isa, insnbuf);
+
+  /* Because RETW[.N] is not bundleable, a VLIW bundle here means that we
+     have no problems.  */
+  if (fmt == XTENSA_UNDEFINED
+      || xtensa_format_num_slots (isa, fmt) != 1)
+    return FALSE;
+
+  xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf);
+  opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
 
 
-  if (is_windowed_return_opcode (opcode))
+  if (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode)
     return TRUE;
     return TRUE;
+
   return FALSE;
 }
 
   return FALSE;
 }
 
@@ -5580,36 +7343,43 @@ next_instrs_are_b_retw (fragP)
    loop end label, convert this frag to one that will generate a NOP.
    In any case close it off with a .fill 0.  */
 
    loop end label, convert this frag to one that will generate a NOP.
    In any case close it off with a .fill 0.  */
 
+static bfd_boolean next_instr_is_loop_end (fragS *);
+
 static void
 static void
-xtensa_fix_b_j_loop_end_frags ()
+xtensa_fix_b_j_loop_end_frags (void)
 {
   frchainS *frchP;
 {
   frchainS *frchP;
+  asection *s;
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
-  for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
-    {
-      fragS *fragP;
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
 
 
-      /* Walk over all of the fragments in a subsection.  */
-      for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
-       {
-         if (fragP->fr_type == rs_machine_dependent
-             && fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END)
-           {
-             if (next_instr_is_loop_end (fragP))
-               relax_frag_add_nop (fragP);
-             else
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if (fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END)
+             {
+               if (next_instr_is_loop_end (fragP))
+                 {
+                   if (fragP->tc_frag_data.is_no_transform)
+                     as_bad (_("branching or jumping to a loop end may trigger hardware errata"));
+                   else
+                     relax_frag_add_nop (fragP);
+                 }
                frag_wane (fragP);
                frag_wane (fragP);
-           }
-       }
-    }
+             }
+         }
+      }
 }
 
 
 }
 
 
-bfd_boolean
-next_instr_is_loop_end (fragP)
-     fragS * fragP;
+static bfd_boolean
+next_instr_is_loop_end (fragS *fragP)
 {
   const fragS *next_fragP;
 
 {
   const fragS *next_fragP;
 
@@ -5638,92 +7408,86 @@ next_instr_is_loop_end (fragP)
    make it at least 12 bytes away.  In any case close it off with a
    .fill 0.  */
 
    make it at least 12 bytes away.  In any case close it off with a
    .fill 0.  */
 
+static offsetT min_bytes_to_other_loop_end
+  (fragS *, fragS *, offsetT);
+
 static void
 static void
-xtensa_fix_close_loop_end_frags ()
+xtensa_fix_close_loop_end_frags (void)
 {
   frchainS *frchP;
 {
   frchainS *frchP;
+  asection *s;
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
-  for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
-    {
-      fragS *fragP;
-
-      fragS *current_target = NULL;
-      offsetT current_offset = 0;
-
-      /* Walk over all of the fragments in a subsection.  */
-      for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
-       {
-         if (fragP->fr_type == rs_machine_dependent
-             && fragP->fr_subtype == RELAX_IMMED)
-           {
-             /* Read it.  If the instruction is a loop, get the target.  */
-             xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_opcode);
-             if (is_loop_opcode (opcode))
-               {
-                 TInsn t_insn;
-
-                 tinsn_from_chars (&t_insn, fragP->fr_opcode);
-                 tinsn_immed_from_frag (&t_insn, fragP);
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
 
 
-                 /* Get the current fragment target.  */
-                 if (fragP->fr_symbol)
-                   {
-                     current_target = symbol_get_frag (fragP->fr_symbol);
-                     current_offset = fragP->fr_offset;
-                   }
-               }
-           }
+       fragS *current_target = NULL;
 
 
-         if (current_target
-             && fragP->fr_type == rs_machine_dependent
-             && fragP->fr_subtype == RELAX_ADD_NOP_IF_CLOSE_LOOP_END)
-           {
-             size_t min_bytes;
-             size_t bytes_added = 0;
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if (fragP->fr_type == rs_machine_dependent
+               && ((fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
+                   || (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)))
+             current_target = symbol_get_frag (fragP->fr_symbol);
+
+           if (current_target
+               && fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_ADD_NOP_IF_CLOSE_LOOP_END)
+             {
+               offsetT min_bytes;
+               int bytes_added = 0;
 
 #define REQUIRED_LOOP_DIVIDING_BYTES 12
 
 #define REQUIRED_LOOP_DIVIDING_BYTES 12
-             /* Max out at 12.  */
-             min_bytes = min_bytes_to_other_loop_end
-               (fragP->fr_next, current_target, current_offset,
-                REQUIRED_LOOP_DIVIDING_BYTES);
-
-             if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
-               {
-                 while (min_bytes + bytes_added
-                        < REQUIRED_LOOP_DIVIDING_BYTES)
-                   {
-                     int length = 3;
-
-                     if (fragP->fr_var < length)
-                       as_warn (_("fr_var %lu < length %d; ignoring"),
-                                fragP->fr_var, length);
-                     else
-                       {
-                         assemble_nop (length,
-                                       fragP->fr_literal + fragP->fr_fix);
-                         fragP->fr_fix += length;
-                         fragP->fr_var -= length;
-                       }
-                     bytes_added += length;
-                   }
-               }
-             frag_wane (fragP);
-           }
-       }
-    }
+               /* Max out at 12.  */
+               min_bytes = min_bytes_to_other_loop_end
+                 (fragP->fr_next, current_target, REQUIRED_LOOP_DIVIDING_BYTES);
+
+               if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
+                 {
+                   if (fragP->tc_frag_data.is_no_transform)
+                     as_bad (_("loop end too close to another loop end may trigger hardware errata"));
+                   else
+                     {
+                       while (min_bytes + bytes_added
+                              < REQUIRED_LOOP_DIVIDING_BYTES)
+                         {
+                           int length = 3;
+
+                           if (fragP->fr_var < length)
+                             as_fatal (_("fr_var %lu < length %d"),
+                                       (long) fragP->fr_var, length);
+                           else
+                             {
+                               assemble_nop (length,
+                                             fragP->fr_literal + fragP->fr_fix);
+                               fragP->fr_fix += length;
+                               fragP->fr_var -= length;
+                             }
+                           bytes_added += length;
+                         }
+                     }
+                 }
+               frag_wane (fragP);
+             }
+           assert (fragP->fr_type != rs_machine_dependent
+                   || fragP->fr_subtype != RELAX_ADD_NOP_IF_CLOSE_LOOP_END);
+         }
+      }
 }
 
 
 }
 
 
-size_t
-min_bytes_to_other_loop_end (fragP, current_target, current_offset, max_size)
-     fragS *fragP;
-     fragS *current_target;
-     offsetT current_offset;
-     size_t max_size;
+static offsetT unrelaxed_frag_min_size (fragS *);
+
+static offsetT
+min_bytes_to_other_loop_end (fragS *fragP,
+                            fragS *current_target,
+                            offsetT max_size)
 {
 {
-  size_t offset = 0;
+  offsetT offset = 0;
   fragS *current_fragP;
 
   for (current_fragP = fragP;
   fragS *current_fragP;
 
   for (current_fragP = fragP;
@@ -5732,24 +7496,23 @@ min_bytes_to_other_loop_end (fragP, current_target, current_offset, max_size)
     {
       if (current_fragP->tc_frag_data.is_loop_target
          && current_fragP != current_target)
     {
       if (current_fragP->tc_frag_data.is_loop_target
          && current_fragP != current_target)
-       return offset + current_offset;
+       return offset;
 
       offset += unrelaxed_frag_min_size (current_fragP);
 
 
       offset += unrelaxed_frag_min_size (current_fragP);
 
-      if (offset + current_offset >= max_size)
+      if (offset >= max_size)
        return max_size;
     }
   return max_size;
 }
 
 
        return max_size;
     }
   return max_size;
 }
 
 
-size_t
-unrelaxed_frag_min_size (fragP)
-     fragS * fragP;
+static offsetT
+unrelaxed_frag_min_size (fragS *fragP)
 {
 {
-  size_t size = fragP->fr_fix;
+  offsetT size = fragP->fr_fix;
 
 
-  /* add fill size */
+  /* Add fill size.  */
   if (fragP->fr_type == rs_fill)
     size += fragP->fr_offset;
 
   if (fragP->fr_type == rs_fill)
     size += fragP->fr_offset;
 
@@ -5757,6 +7520,43 @@ unrelaxed_frag_min_size (fragP)
 }
 
 
 }
 
 
+static offsetT
+unrelaxed_frag_max_size (fragS *fragP)
+{
+  offsetT size = fragP->fr_fix;
+  switch (fragP->fr_type)
+    {
+    case 0:
+      /* Empty frags created by the obstack allocation scheme
+        end up with type 0.  */
+      break;
+    case rs_fill:
+    case rs_org:
+    case rs_space:
+      size += fragP->fr_offset;
+      break;
+    case rs_align:
+    case rs_align_code:
+    case rs_align_test:
+    case rs_leb128:
+    case rs_cfa:
+    case rs_dwarf2dbg:
+      /* No further adjustments needed.  */
+      break;
+    case rs_machine_dependent:
+      if (fragP->fr_subtype != RELAX_DESIRE_ALIGN)
+       size += fragP->fr_var;
+      break;
+    default:
+      /* We had darn well better know how big it is.  */
+      assert (0);
+      break;
+    }
+
+  return size;
+}
+
+
 /* Re-process all of the fragments looking to convert all
    of the RELAX_ADD_NOP_IF_SHORT_LOOP.  If:
 
 /* Re-process all of the fragments looking to convert all
    of the RELAX_ADD_NOP_IF_SHORT_LOOP.  If:
 
@@ -5766,80 +7566,77 @@ unrelaxed_frag_min_size (fragP)
      2) loop has a jump or branch in it
 
    or B)
      2) loop has a jump or branch in it
 
    or B)
-     1) software_avoid_all_short_loops is true
+     1) workaround_all_short_loops is TRUE
      2) The generating loop was a  'loopgtz' or 'loopnez'
      3) the instruction size count to the loop end label is too short
         (<= 2 instructions)
    then convert this frag (and maybe the next one) to generate a NOP.
    In any case close it off with a .fill 0.  */
 
      2) The generating loop was a  'loopgtz' or 'loopnez'
      3) the instruction size count to the loop end label is too short
         (<= 2 instructions)
    then convert this frag (and maybe the next one) to generate a NOP.
    In any case close it off with a .fill 0.  */
 
+static int count_insns_to_loop_end (fragS *, bfd_boolean, int);
+static bfd_boolean branch_before_loop_end (fragS *);
+
 static void
 static void
-xtensa_fix_short_loop_frags ()
+xtensa_fix_short_loop_frags (void)
 {
   frchainS *frchP;
 {
   frchainS *frchP;
+  asection *s;
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
 
   /* When this routine is called, all of the subsections are still intact
      so we walk over subsections instead of sections.  */
-  for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
-    {
-      fragS *fragP;
-      fragS *current_target = NULL;
-      offsetT current_offset = 0;
-      xtensa_opcode current_opcode = XTENSA_UNDEFINED;
-
-      /* Walk over all of the fragments in a subsection.  */
-      for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
-       {
-         /* check on the current loop */
-         if (fragP->fr_type == rs_machine_dependent
-             && fragP->fr_subtype == RELAX_IMMED)
-           {
-             /* Read it.  If the instruction is a loop, get the target.  */
-             xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_opcode);
-             if (is_loop_opcode (opcode))
-               {
-                 TInsn t_insn;
-
-                 tinsn_from_chars (&t_insn, fragP->fr_opcode);
-                 tinsn_immed_from_frag (&t_insn, fragP);
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
+       fragS *current_target = NULL;
+       xtensa_opcode current_opcode = XTENSA_UNDEFINED;
 
 
-                 /* Get the current fragment target.  */
-                 if (fragP->fr_symbol)
-                   {
-                     current_target = symbol_get_frag (fragP->fr_symbol);
-                     current_offset = fragP->fr_offset;
-                     current_opcode = opcode;
-                   }
-               }
-           }
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if (fragP->fr_type == rs_machine_dependent
+               && ((fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
+                   || (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)))
+             {
+               TInsn t_insn;
+               fragS *loop_frag = next_non_empty_frag (fragP);
+               tinsn_from_chars (&t_insn, loop_frag->fr_opcode, 0);
+               current_target = symbol_get_frag (fragP->fr_symbol);
+               current_opcode = t_insn.opcode;
+               assert (xtensa_opcode_is_loop (xtensa_default_isa,
+                                              current_opcode) == 1);
+             }
 
 
-         if (fragP->fr_type == rs_machine_dependent
-             && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
-           {
-             size_t insn_count =
-               count_insns_to_loop_end (fragP->fr_next, TRUE, 3);
-             if (insn_count < 3
-                 && (branch_before_loop_end (fragP->fr_next)
-                     || (software_avoid_all_short_loops
-                         && current_opcode != XTENSA_UNDEFINED
-                         && !is_the_loop_opcode (current_opcode))))
-               relax_frag_add_nop (fragP);
-             else
+           if (fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
+             {
+               if (count_insns_to_loop_end (fragP->fr_next, TRUE, 3) < 3
+                   && (branch_before_loop_end (fragP->fr_next)
+                       || (workaround_all_short_loops
+                           && current_opcode != XTENSA_UNDEFINED
+                           && current_opcode != xtensa_loop_opcode)))
+                 {
+                   if (fragP->tc_frag_data.is_no_transform)
+                     as_bad (_("loop containing less than three instructions may trigger hardware errata"));
+                   else
+                     relax_frag_add_nop (fragP);
+                 }
                frag_wane (fragP);
                frag_wane (fragP);
-           }
-       }
-    }
+             }
+         }
+      }
 }
 
 
 }
 
 
-size_t
-count_insns_to_loop_end (base_fragP, count_relax_add, max_count)
-     fragS *base_fragP;
-     bfd_boolean count_relax_add;
-     size_t max_count;
+static int unrelaxed_frag_min_insn_count (fragS *);
+
+static int
+count_insns_to_loop_end (fragS *base_fragP,
+                        bfd_boolean count_relax_add,
+                        int max_count)
 {
   fragS *fragP = NULL;
 {
   fragS *fragP = NULL;
-  size_t insn_count = 0;
+  int insn_count = 0;
 
   fragP = base_fragP;
 
 
   fragP = base_fragP;
 
@@ -5867,26 +7664,35 @@ count_insns_to_loop_end (base_fragP, count_relax_add, max_count)
 }
 
 
 }
 
 
-size_t
-unrelaxed_frag_min_insn_count (fragP)
-     fragS *fragP;
+static int
+unrelaxed_frag_min_insn_count (fragS *fragP)
 {
 {
-  size_t insn_count = 0;
+  xtensa_isa isa = xtensa_default_isa;
+  static xtensa_insnbuf insnbuf = NULL;
+  int insn_count = 0;
   int offset = 0;
 
   if (!fragP->tc_frag_data.is_insn)
     return insn_count;
 
   int offset = 0;
 
   if (!fragP->tc_frag_data.is_insn)
     return insn_count;
 
+  if (!insnbuf)
+    insnbuf = xtensa_insnbuf_alloc (isa);
+
   /* Decode the fixed instructions.  */
   while (offset < fragP->fr_fix)
     {
   /* Decode the fixed instructions.  */
   while (offset < fragP->fr_fix)
     {
-      xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset);
-      if (opcode == XTENSA_UNDEFINED)
+      xtensa_format fmt;
+
+      xtensa_insnbuf_from_chars
+       (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
+      fmt = xtensa_format_decode (isa, insnbuf);
+
+      if (fmt == XTENSA_UNDEFINED)
        {
          as_fatal (_("undecodable instruction in instruction frag"));
          return insn_count;
        }
        {
          as_fatal (_("undecodable instruction in instruction frag"));
          return insn_count;
        }
-      offset += xtensa_insn_length (xtensa_default_isa, opcode);
+      offset += xtensa_format_length (isa, fmt);
       insn_count++;
     }
 
       insn_count++;
     }
 
@@ -5894,9 +7700,10 @@ unrelaxed_frag_min_insn_count (fragP)
 }
 
 
 }
 
 
-bfd_boolean
-branch_before_loop_end (base_fragP)
-     fragS *base_fragP;
+static bfd_boolean unrelaxed_frag_has_b_j (fragS *);
+
+static bfd_boolean
+branch_before_loop_end (fragS *base_fragP)
 {
   fragS *fragP;
 
 {
   fragS *fragP;
 
@@ -5911,28 +7718,40 @@ branch_before_loop_end (base_fragP)
 }
 
 
 }
 
 
-bfd_boolean
-unrelaxed_frag_has_b_j (fragP)
-     fragS *fragP;
+static bfd_boolean
+unrelaxed_frag_has_b_j (fragS *fragP)
 {
 {
-  size_t insn_count = 0;
+  static xtensa_insnbuf insnbuf = NULL;
+  xtensa_isa isa = xtensa_default_isa;
   int offset = 0;
 
   if (!fragP->tc_frag_data.is_insn)
     return FALSE;
 
   int offset = 0;
 
   if (!fragP->tc_frag_data.is_insn)
     return FALSE;
 
+  if (!insnbuf)
+    insnbuf = xtensa_insnbuf_alloc (isa);
+
   /* Decode the fixed instructions.  */
   while (offset < fragP->fr_fix)
     {
   /* Decode the fixed instructions.  */
   while (offset < fragP->fr_fix)
     {
-      xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset);
-      if (opcode == XTENSA_UNDEFINED)
+      xtensa_format fmt;
+      int slot;
+
+      xtensa_insnbuf_from_chars
+       (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
+      fmt = xtensa_format_decode (isa, insnbuf);
+      if (fmt == XTENSA_UNDEFINED)
+       return FALSE;
+
+      for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
        {
        {
-         as_fatal (_("undecodable instruction in instruction frag"));
-         return insn_count;
+         xtensa_opcode opcode =
+           get_opcode_from_buf (fragP->fr_literal + offset, slot);
+         if (xtensa_opcode_is_branch (isa, opcode) == 1
+             || xtensa_opcode_is_jump (isa, opcode) == 1)
+           return TRUE;
        }
        }
-      if (is_branch_or_jump_opcode (opcode))
-       return TRUE;
-      offset += xtensa_insn_length (xtensa_default_isa, opcode);
+      offset += xtensa_format_length (isa, fmt);
     }
   return FALSE;
 }
     }
   return FALSE;
 }
@@ -5940,66 +7759,69 @@ unrelaxed_frag_has_b_j (fragP)
 
 /* 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_local_forward_loop (const TInsn *, fragS *);
+
 static void
 static void
-xtensa_sanity_check ()
+xtensa_sanity_check (void)
 {
   char *file_name;
 {
   char *file_name;
-  int line;
-
+  unsigned line;
   frchainS *frchP;
   frchainS *frchP;
+  asection *s;
 
   as_where (&file_name, &line);
 
   as_where (&file_name, &line);
-  for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
-    {
-      fragS *fragP;
-
-      /* Walk over all of the fragments in a subsection.  */
-      for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
-       {
-         /* Currently we only check for empty loops here.  */
-         if (fragP->fr_type == rs_machine_dependent
-             && fragP->fr_subtype == RELAX_IMMED)
-           {
-             static xtensa_insnbuf insnbuf = NULL;
-             TInsn t_insn;
-
-             if (fragP->fr_opcode != NULL)
-               {
-                 if (!insnbuf)
-                   insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-                 tinsn_from_chars (&t_insn, fragP->fr_opcode);
-                 tinsn_immed_from_frag (&t_insn, fragP);
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
 
 
-                 if (is_loop_opcode (t_insn.opcode))
-                   {
-                     if (is_empty_loop (&t_insn, fragP))
-                       {
-                         new_logical_line (fragP->fr_file, fragP->fr_line);
-                         as_bad (_("invalid empty loop"));
-                       }
-                     if (!is_local_forward_loop (&t_insn, fragP))
-                       {
-                         new_logical_line (fragP->fr_file, fragP->fr_line);
-                         as_bad (_("loop target does not follow "
-                                   "loop instruction in section"));
-                       }
-                   }
-               }
-           }
-       }
-    }
+       /* Walk over all of the fragments in a subsection.  */
+       for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+         {
+           if (fragP->fr_type == rs_machine_dependent
+               && fragP->fr_subtype == RELAX_SLOTS 
+               && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
+             {
+               static xtensa_insnbuf insnbuf = NULL;
+               TInsn t_insn;
+
+               if (fragP->fr_opcode != NULL)
+                 {
+                   if (!insnbuf)
+                     insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+                   tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
+                   tinsn_immed_from_frag (&t_insn, fragP, 0);
+
+                   if (xtensa_opcode_is_loop (xtensa_default_isa,
+                                              t_insn.opcode) == 1)
+                     {
+                       if (is_empty_loop (&t_insn, fragP))
+                         {
+                           new_logical_line (fragP->fr_file, fragP->fr_line);
+                           as_bad (_("invalid empty loop"));
+                         }
+                       if (!is_local_forward_loop (&t_insn, fragP))
+                         {
+                           new_logical_line (fragP->fr_file, fragP->fr_line);
+                           as_bad (_("loop target does not follow "
+                                     "loop instruction in section"));
+                         }
+                     }
+                 }
+             }
+         }
+      }
   new_logical_line (file_name, line);
 }
 
 
 #define LOOP_IMMED_OPN 1
 
   new_logical_line (file_name, line);
 }
 
 
 #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.  */
 
 
-bfd_boolean
-is_empty_loop (insn, fragP)
-     const TInsn *insn;
-     fragS *fragP;
+static bfd_boolean
+is_empty_loop (const TInsn *insn, fragS *fragP)
 {
   const expressionS *expr;
   symbolS *symbolP;
 {
   const expressionS *expr;
   symbolS *symbolP;
@@ -6008,7 +7830,7 @@ is_empty_loop (insn, fragP)
   if (insn->insn_type != ITYPE_INSN)
     return FALSE;
 
   if (insn->insn_type != ITYPE_INSN)
     return FALSE;
 
-  if (!is_loop_opcode (insn->opcode))
+  if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
     return FALSE;
 
   if (insn->ntok <= LOOP_IMMED_OPN)
     return FALSE;
 
   if (insn->ntok <= LOOP_IMMED_OPN)
@@ -6031,6 +7853,7 @@ is_empty_loop (insn, fragP)
 
   /* Walk through the zero-size fragments from this one.  If we find
      the target fragment, then this is a zero-size loop.  */
 
   /* Walk through the zero-size fragments from this one.  If we find
      the target fragment, then this is a zero-size loop.  */
+
   for (next_fragP = fragP->fr_next;
        next_fragP != NULL;
        next_fragP = next_fragP->fr_next)
   for (next_fragP = fragP->fr_next;
        next_fragP != NULL;
        next_fragP = next_fragP->fr_next)
@@ -6044,10 +7867,8 @@ is_empty_loop (insn, fragP)
 }
 
 
 }
 
 
-bfd_boolean
-is_local_forward_loop (insn, fragP)
-     const TInsn *insn;
-     fragS *fragP;
+static bfd_boolean
+is_local_forward_loop (const TInsn *insn, fragS *fragP)
 {
   const expressionS *expr;
   symbolS *symbolP;
 {
   const expressionS *expr;
   symbolS *symbolP;
@@ -6056,7 +7877,7 @@ is_local_forward_loop (insn, fragP)
   if (insn->insn_type != ITYPE_INSN)
     return FALSE;
 
   if (insn->insn_type != ITYPE_INSN)
     return FALSE;
 
-  if (!is_loop_opcode (insn->opcode))
+  if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
     return FALSE;
 
   if (insn->ntok <= LOOP_IMMED_OPN)
     return FALSE;
 
   if (insn->ntok <= LOOP_IMMED_OPN)
@@ -6076,38 +7897,84 @@ is_local_forward_loop (insn, fragP)
 
   /* Walk through fragments until we find the target.
      If we do not find the target, then this is an invalid loop.  */
 
   /* Walk through fragments until we find the target.
      If we do not find the target, then this is an invalid loop.  */
+
   for (next_fragP = fragP->fr_next;
        next_fragP != NULL;
        next_fragP = next_fragP->fr_next)
   for (next_fragP = fragP->fr_next;
        next_fragP != NULL;
        next_fragP = next_fragP->fr_next)
-    if (next_fragP == symbol_get_frag (symbolP))
-      return TRUE;
+    {
+      if (next_fragP == symbol_get_frag (symbolP))
+       return TRUE;
+    }
 
   return FALSE;
 }
 
 
   return FALSE;
 }
 
+
+#define XTINFO_NAME "Xtensa_Info"
+#define XTINFO_NAMESZ 12
+#define XTINFO_TYPE 1
+
+static void
+xtensa_add_config_info (void)
+{
+  asection *info_sec;
+  char *data, *p;
+  int sz;
+
+  info_sec = subseg_new (".xtensa.info", 0);
+  bfd_set_section_flags (stdoutput, info_sec, SEC_HAS_CONTENTS | SEC_READONLY);
+
+  data = xmalloc (100);
+  sprintf (data, "USE_ABSOLUTE_LITERALS=%d\nABI=%d\n",
+          XSHAL_USE_ABSOLUTE_LITERALS, XSHAL_ABI);
+  sz = strlen (data) + 1;
+
+  /* Add enough null terminators to pad to a word boundary.  */
+  do
+    data[sz++] = 0;
+  while ((sz & 3) != 0);
+
+  /* Follow the standard note section layout:
+     First write the length of the name string.  */
+  p = frag_more (4);
+  md_number_to_chars (p, (valueT) XTINFO_NAMESZ, 4);
+
+  /* Next comes the length of the "descriptor", i.e., the actual data.  */
+  p = frag_more (4);
+  md_number_to_chars (p, (valueT) sz, 4);
+
+  /* Write the note type.  */
+  p = frag_more (4);
+  md_number_to_chars (p, (valueT) XTINFO_TYPE, 4);
+
+  /* Write the name field.  */
+  p = frag_more (XTINFO_NAMESZ);
+  memcpy (p, XTINFO_NAME, XTINFO_NAMESZ);
+
+  /* Finally, write the descriptor.  */
+  p = frag_more (sz);
+  memcpy (p, data, sz);
+
+  free (data);
+}
+
 \f
 /* Alignment Functions.  */
 
 \f
 /* Alignment Functions.  */
 
-size_t
-get_text_align_power (target_size)
-     int target_size;
+static int
+get_text_align_power (unsigned target_size)
 {
 {
-  size_t i = 0;
-  for (i = 0; i < sizeof (size_t); i++)
-    {
-      if (target_size <= (1 << i))
-       return i;
-    }
-  as_fatal (_("get_text_align_power: argument too large"));
-  return 0;
+  if (target_size <= 4)
+    return 2;
+  assert (target_size == 8);
+  return 3;
 }
 
 
 }
 
 
-addressT
-get_text_align_max_fill_size (align_pow, use_nops, use_no_density)
-     int align_pow;
-     bfd_boolean use_nops;
-     bfd_boolean use_no_density;
+static int
+get_text_align_max_fill_size (int align_pow,
+                             bfd_boolean use_nops,
+                             bfd_boolean use_no_density)
 {
   if (!use_nops)
     return (1 << align_pow);
 {
   if (!use_nops)
     return (1 << align_pow);
@@ -6118,105 +7985,86 @@ get_text_align_max_fill_size (align_pow, use_nops, use_no_density)
 }
 
 
 }
 
 
-/* get_text_align_fill_size ()
-  
-   Desired alignments:
-      give the address
-      target_size = size of next instruction
-      align_pow = get_text_align_power (target_size).
-      use_nops = 0
-      use_no_density = 0;
-   Loop alignments:
-      address = current address + loop instruction size;
-      target_size = 3 (for 2 or 3 byte target)
-                  = 8 (for 8 byte target)
-      align_pow = get_text_align_power (target_size);
-      use_nops = 1
-      use_no_density = set appropriately
-   Text alignments:
-      address = current address + loop instruction size;
-      target_size = 0
-      align_pow = get_text_align_power (target_size);
-      use_nops = 0
-      use_no_density = 0.  */
-
-addressT
-get_text_align_fill_size (address, align_pow, target_size,
-                         use_nops, use_no_density)
-     addressT address;
-     int align_pow;
-     int target_size;
-     bfd_boolean use_nops;
-     bfd_boolean use_no_density;
-{
-  /* Input arguments:
-
-     align_pow: log2 (required alignment).
-
-     target_size: alignment must allow the new_address and
-     new_address+target_size-1.
-
-     use_nops: if true, then we can only use 2 or 3 byte nops.
-
-     use_no_density: if use_nops and use_no_density, we can only use
-     3-byte nops.
-
-     Usually, for non-zero target_size, the align_pow is the power of 2
-     that is greater than or equal to the target_size.  This handles the
-     2-byte, 3-byte and 8-byte instructions.  */
-
-  size_t alignment = (1 << align_pow);
+/* 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
+   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
+   be filled by NOP instructions.  */
+
+static int
+get_text_align_fill_size (addressT address,
+                         int align_pow,
+                         int target_size,
+                         bfd_boolean use_nops,
+                         bfd_boolean use_no_density)
+{
+  addressT alignment, fill, fill_limit, fill_step;
+  bfd_boolean skip_one = FALSE;
+
+  alignment = (1 << align_pow);
+  assert (target_size > 0 && alignment >= (addressT) target_size);
+
   if (!use_nops)
     {
   if (!use_nops)
     {
-      /* This is the easy case.  */
-      size_t mod;
-      mod = address % alignment;
-      if (mod != 0)
-       mod = alignment - mod;
-      assert ((address + mod) % alignment == 0);
-      return mod;
+      fill_limit = alignment;
+      fill_step = 1;
     }
     }
-
-  /* This is the slightly harder case.  */
-  assert ((int) alignment >= target_size);
-  assert (target_size > 0);
-  if (!use_no_density)
+  else if (!use_no_density)
     {
     {
-      size_t i;
-      for (i = 0; i < alignment * 2; i++)
-       {
-         if (i == 1)
-           continue;
-         if ((address + i) >> align_pow ==
-             (address + i + target_size - 1) >> align_pow)
-           return i;
-       }
+      /* Combine 2- and 3-byte NOPs to fill anything larger than one.  */
+      fill_limit = alignment * 2;
+      fill_step = 1;
+      skip_one = TRUE;
     }
   else
     {
     }
   else
     {
-      size_t i;
+      /* Fill with 3-byte NOPs -- can only fill multiples of 3.  */
+      fill_limit = alignment * 3;
+      fill_step = 3;
+    }
 
 
-      /* Can only fill multiples of 3.  */
-      for (i = 0; i <= alignment * 3; i += 3)
-       {
-         if ((address + i) >> align_pow ==
-             (address + i + target_size - 1) >> align_pow)
-           return i;
-       }
+  /* Try all fill sizes until finding one that works.  */
+  for (fill = 0; fill < fill_limit; fill += fill_step)
+    {
+      if (skip_one && fill == 1)
+       continue;
+      if ((address + fill) >> align_pow
+         == (address + fill + target_size - 1) >> align_pow)
+       return fill;
     }
   assert (0);
   return 0;
 }
 
 
     }
   assert (0);
   return 0;
 }
 
 
+static int
+branch_align_power (segT sec)
+{
+  /* 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
+     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
+     (which may be insufficient, e.g., if the section has no alignment, but
+     it's good enough).  */
+  if (xtensa_fetch_width == 8)
+    {
+      if (get_recorded_alignment (sec) >= 3)
+       return 3;
+    }
+  else
+    assert (xtensa_fetch_width == 4);
+
+  return 2;
+}
+
+
 /* This will assert if it is not possible.  */
 
 /* This will assert if it is not possible.  */
 
-size_t
-get_text_align_nop_count (fill_size, use_no_density)
-     size_t fill_size;
-     bfd_boolean use_no_density;
+static int
+get_text_align_nop_count (offsetT fill_size, bfd_boolean use_no_density)
 {
 {
-  size_t count = 0;
+  int count = 0;
+
   if (use_no_density)
     {
       assert (fill_size % 3 == 0);
   if (use_no_density)
     {
       assert (fill_size % 3 == 0);
@@ -6227,7 +8075,7 @@ get_text_align_nop_count (fill_size, use_no_density)
 
   while (fill_size > 1)
     {
 
   while (fill_size > 1)
     {
-      size_t insn_size = 3;
+      int insn_size = 3;
       if (fill_size == 2 || fill_size == 4)
        insn_size = 2;
       fill_size -= insn_size;
       if (fill_size == 2 || fill_size == 4)
        insn_size = 2;
       fill_size -= insn_size;
@@ -6238,22 +8086,21 @@ get_text_align_nop_count (fill_size, use_no_density)
 }
 
 
 }
 
 
-size_t
-get_text_align_nth_nop_size (fill_size, n, use_no_density)
-     size_t fill_size;
-     size_t n;
-     bfd_boolean use_no_density;
+static int
+get_text_align_nth_nop_size (offsetT fill_size,
+                            int n,
+                            bfd_boolean use_no_density)
 {
 {
-  size_t count = 0;
-
-  assert (get_text_align_nop_count (fill_size, use_no_density) > n);
+  int count = 0;
 
   if (use_no_density)
     return 3;
 
 
   if (use_no_density)
     return 3;
 
+  assert (fill_size != 1);     /* Bad argument.  */
+
   while (fill_size > 1)
     {
   while (fill_size > 1)
     {
-      size_t insn_size = 3;
+      int insn_size = 3;
       if (fill_size == 2 || fill_size == 4)
        insn_size = 2;
       fill_size -= insn_size;
       if (fill_size == 2 || fill_size == 4)
        insn_size = 2;
       fill_size -= insn_size;
@@ -6270,234 +8117,173 @@ get_text_align_nth_nop_size (fill_size, n, use_no_density)
    for it to begin at if we are using NOPs to align it.  */
 
 static addressT
    for it to begin at if we are using NOPs to align it.  */
 
 static addressT
-get_noop_aligned_address (fragP, address)
-     fragS *fragP;
-     addressT address;
+get_noop_aligned_address (fragS *fragP, addressT address)
 {
 {
-  static xtensa_insnbuf insnbuf = NULL;
-  size_t fill_size = 0;
-
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
-  switch (fragP->fr_type)
-    {
-    case rs_machine_dependent:
-      if (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
-       {
-         /* The rule is: get next fragment's FIRST instruction.  Find
-            the smallest number of bytes that need to be added to
-            ensure that the next fragment's FIRST instruction will fit
-            in a single word.
-
-            E.G.,   2 bytes : 0, 1, 2 mod 4 
-                    3 bytes: 0, 1 mod 4 
+  /* The rule is: get next fragment's FIRST instruction.  Find
+     the smallest number of bytes that need to be added to
+     ensure that the next fragment's FIRST instruction will fit
+     in a single word.
 
 
-            If the FIRST instruction MIGHT be relaxed, 
-            assume that it will become a 3 byte instruction.  */
+     E.G.,   2 bytes : 0, 1, 2 mod 4
+            3 bytes: 0, 1 mod 4
 
 
-         int target_insn_size;
-         xtensa_opcode opcode = next_frag_opcode (fragP);
-         addressT pre_opcode_bytes;
+     If the FIRST instruction MIGHT be relaxed,
+     assume that it will become a 3-byte instruction.
 
 
-         if (opcode == XTENSA_UNDEFINED)
-           {
-             as_bad_where (fragP->fr_file, fragP->fr_line,
-                           _("invalid opcode for RELAX_ALIGN_NEXT_OPCODE"));
-             as_fatal (_("cannot continue"));
-           }
+     Note again here that LOOP instructions are not bundleable,
+     and this relaxation only applies to LOOP opcodes.  */
 
 
-         target_insn_size = xtensa_insn_length (xtensa_default_isa, opcode);
+  int fill_size = 0;
+  int first_insn_size;
+  int loop_insn_size;
+  addressT pre_opcode_bytes;
+  int align_power;
+  fragS *first_insn;
+  xtensa_opcode opcode;
+  bfd_boolean is_loop;
 
 
-         pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
+  assert (fragP->fr_type == rs_machine_dependent);
+  assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
 
 
-         if (is_loop_opcode (opcode))
-           {
-             /* next_fragP should be the loop.  */
-             const fragS *next_fragP = next_non_empty_frag (fragP);
-             xtensa_opcode next_opcode = next_frag_opcode (next_fragP);
-             size_t alignment;
+  /* Find the loop frag.  */
+  first_insn = next_non_empty_frag (fragP);
+  /* Now find the first insn frag.  */
+  first_insn = next_non_empty_frag (first_insn);
 
 
-             pre_opcode_bytes += target_insn_size;
+  is_loop = next_frag_opcode_is_loop (fragP, &opcode);
+  assert (is_loop);
+  loop_insn_size = xg_get_single_size (opcode);
 
 
-             /* For loops, the alignment depends on the size of the
-                instruction following the loop, not the loop instruction.  */
-             if (next_opcode == XTENSA_UNDEFINED)
-               target_insn_size = 3;
-             else
-               {
-                 target_insn_size =
-                   xtensa_insn_length (xtensa_default_isa, next_opcode);
+  pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
+  pre_opcode_bytes += loop_insn_size;
 
 
-                 if (target_insn_size == 2)
-                   target_insn_size = 3;       /* ISA specifies this.  */
-               }
+  /* For loops, the alignment depends on the size of the
+     instruction following the loop, not the LOOP instruction.  */
 
 
-             /* If it was 8, then we'll need a larger alignment
-                for the section.  */
-             alignment = get_text_align_power (target_insn_size);
+  if (first_insn == NULL)
+    first_insn_size = xtensa_fetch_width;
+  else
+    first_insn_size = get_loop_align_size (frag_format_size (first_insn));
 
 
-             /* Is Now_seg valid */
-             record_alignment (now_seg, alignment);
-           }
-         else
-           as_fatal (_("expected loop opcode in relax align next target"));
+  /* If it was 8, then we'll need a larger alignment for the section.  */
+  align_power = get_text_align_power (first_insn_size);
+  record_alignment (now_seg, align_power);
 
 
-         fill_size = get_text_align_fill_size
-           (address + pre_opcode_bytes,
-            get_text_align_power (target_insn_size),
-            target_insn_size, TRUE, fragP->tc_frag_data.is_no_density);
-       }
-      break;
-#if 0
-    case rs_align:
-    case rs_align_code:
-      fill_size = get_text_align_fill_size
-       (address, fragP->fr_offset, 1, TRUE,
-        fragP->tc_frag_data.is_no_density);
-      break;
-#endif
-    default:
-      as_fatal (_("expected align_code or RELAX_ALIGN_NEXT_OPCODE"));
-    }
+  fill_size = get_text_align_fill_size
+    (address + pre_opcode_bytes, align_power, first_insn_size, TRUE,
+     fragP->tc_frag_data.is_no_density);
 
   return address + fill_size;
 }
 
 
 
   return address + fill_size;
 }
 
 
-/* 3 mechanisms for relaxing an alignment: 
-   
-   Align to a power of 2. 
-   Align so the next fragment's instruction does not cross a word boundary. 
-   Align the current instruction so that if the next instruction 
-       were 3 bytes, it would not cross a word boundary. 
-   
-   We can align with:
-
-   zeros    - This is easy; always insert zeros. 
-   nops     - 3 and 2 byte instructions 
-              2 - 2 byte nop 
-              3 - 3 byte nop 
-              4 - 2, 2-byte nops 
-              >=5 : 3 byte instruction + fn(n-3) 
-   widening - widen previous instructions.  */
-
-static addressT
-get_widen_aligned_address (fragP, address)
-     fragS *fragP;
-     addressT address;
-{
-  addressT align_pow, new_address, loop_insn_offset;
-  fragS *next_frag;
-  int insn_size;
-  xtensa_opcode opcode, next_opcode;
-  static xtensa_insnbuf insnbuf = NULL;
-
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
-  if (fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
-    {
-      align_pow = fragP->fr_offset;
-      new_address = ((address + ((1 << align_pow) - 1))
-                    << align_pow) >> align_pow;
-      return new_address;
-    }
-
-  if (fragP->fr_type == rs_machine_dependent)
-    {
-      switch (fragP->fr_subtype)
-       {
-       case RELAX_DESIRE_ALIGN:
+/* 3 mechanisms for relaxing an alignment:
 
 
-         /* The rule is: get the next fragment's FIRST instruction. 
-            Find the smallest number of bytes needed to be added 
-            in order to ensure that the next fragment is FIRST 
-            instruction will fit in a single word. 
-            i.e.    2 bytes : 0, 1, 2.  mod 4 
-                    3 bytes: 0, 1 mod 4 
-            If the FIRST instruction MIGHT be relaxed, 
-            assume that it will become a 3-byte instruction.  */
+   Align to a power of 2.
+   Align so the next fragment's instruction does not cross a word boundary.
+   Align the current instruction so that if the next instruction
+       were 3 bytes, it would not cross a word boundary.
 
 
-         insn_size = 3;
-         /* Check to see if it might be 2 bytes.  */
-         next_opcode = next_frag_opcode (fragP);
-         if (next_opcode != XTENSA_UNDEFINED
-             && xtensa_insn_length (xtensa_default_isa, next_opcode) == 2)
-           insn_size = 2;
+   We can align with:
 
 
-         assert (insn_size <= 4);
-         for (new_address = address; new_address < address + 4; new_address++)
-           {
-             if (new_address >> 2 == (new_address + insn_size - 1) >> 2)
-               return new_address;
-           }
-         as_bad (_("internal error aligning"));
-         return address;
-
-       case RELAX_ALIGN_NEXT_OPCODE:
-         /* The rule is: get next fragment's FIRST instruction. 
-            Find the smallest number of bytes needed to be added 
-            in order to ensure that the next fragment's FIRST 
-            instruction will fit in a single word. 
-            i.e.    2 bytes : 0, 1, 2.  mod 4 
-                    3 bytes: 0, 1 mod 4 
-            If the FIRST instruction MIGHT be relaxed, 
-            assume that it will become a 3 byte instruction.  */
-
-         opcode = next_frag_opcode (fragP);
-         if (opcode == XTENSA_UNDEFINED)
-           {
-             as_bad_where (fragP->fr_file, fragP->fr_line,
-                           _("invalid opcode for RELAX_ALIGN_NEXT_OPCODE"));
-             as_fatal (_("cannot continue"));
-           }
-         insn_size = xtensa_insn_length (xtensa_default_isa, opcode);
-         assert (insn_size <= 4);
-         assert (is_loop_opcode (opcode));
+   zeros    - This is easy; always insert zeros.
+   nops     - 3-byte and 2-byte instructions
+              2 - 2-byte nop
+              3 - 3-byte nop
+              4 - 2 2-byte nops
+              >=5 : 3-byte instruction + fn (n-3)
+   widening - widen previous instructions.  */
 
 
-         loop_insn_offset = 0;
-         next_frag = next_non_empty_frag (fragP);
+static offsetT
+get_aligned_diff (fragS *fragP, addressT address, offsetT *max_diff)
+{
+  addressT target_address, loop_insn_offset;
+  int target_size;
+  xtensa_opcode loop_opcode;
+  bfd_boolean is_loop;
+  int align_power;
+  offsetT opt_diff;
+  offsetT branch_align;
 
 
-         /* If the loop has been expanded then the loop
-            instruction could be at an offset from this fragment.  */
-         if (next_frag->fr_subtype != RELAX_IMMED)
-           loop_insn_offset = get_expanded_loop_offset (opcode);
+  assert (fragP->fr_type == rs_machine_dependent);
+  switch (fragP->fr_subtype)
+    {
+    case RELAX_DESIRE_ALIGN:
+      target_size = next_frag_format_size (fragP);
+      if (target_size == XTENSA_UNDEFINED)
+       target_size = 3;
+      align_power = branch_align_power (now_seg);
+      branch_align = 1 << align_power;
+      /* Don't count on the section alignment being as large as the target.  */
+      if (target_size > branch_align)
+       target_size = branch_align;
+      opt_diff = get_text_align_fill_size (address, align_power,
+                                          target_size, FALSE, FALSE);
+
+      *max_diff = (opt_diff + branch_align
+                  - (target_size + ((address + opt_diff) % branch_align)));
+      assert (*max_diff >= opt_diff);
+      return opt_diff;
 
 
-         for (new_address = address; new_address < address + 4; new_address++)
-           {
-             if ((new_address + loop_insn_offset + insn_size) >> 2 ==
-                 (new_address + loop_insn_offset + insn_size + 2) >> 2)
-               return new_address;
-           }
-         as_bad (_("internal error aligning"));
-         return address;
+    case RELAX_ALIGN_NEXT_OPCODE:
+      target_size = get_loop_align_size (next_frag_format_size (fragP));
+      loop_insn_offset = 0;
+      is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
+      assert (is_loop);
+
+      /* If the loop has been expanded then the LOOP instruction
+        could be at an offset from this fragment.  */
+      if (next_non_empty_frag(fragP)->tc_frag_data.slot_subtypes[0]
+         != RELAX_IMMED)
+       loop_insn_offset = get_expanded_loop_offset (loop_opcode);
+
+      /* In an ideal world, which is what we are shooting for here,
+        we wouldn't need to use any NOPs immediately prior to the
+        LOOP instruction.  If this approach fails, relax_frag_loop_align
+        will call get_noop_aligned_address.  */
+      target_address =
+       address + loop_insn_offset + xg_get_single_size (loop_opcode);
+      align_power = get_text_align_power (target_size),
+      opt_diff = get_text_align_fill_size (target_address, align_power,
+                                          target_size, FALSE, FALSE);
+
+      *max_diff = xtensa_fetch_width
+       - ((target_address + opt_diff) % xtensa_fetch_width)
+       - target_size + opt_diff;
+      assert (*max_diff >= opt_diff);
+      return opt_diff;
 
 
-       default:
-         as_bad (_("internal error aligning"));
-         return address;
-       }
+    default:
+      break;
     }
     }
-  as_bad (_("internal error aligning"));
-  return address;
+  assert (0);
+  return 0;
 }
 
 \f
 /* md_relax_frag Hook and Helper Functions.  */
 
 }
 
 \f
 /* md_relax_frag Hook and Helper Functions.  */
 
+static long relax_frag_loop_align (fragS *, long);
+static long relax_frag_for_align (fragS *, long);
+static long relax_frag_immed
+  (segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
+
+
 /* Return the number of bytes added to this fragment, given that the
    input has been stretched already by "stretch".  */
 
 long
 /* Return the number of bytes added to this fragment, given that the
    input has been stretched already by "stretch".  */
 
 long
-xtensa_relax_frag (fragP, stretch, stretched_p)
-     fragS *fragP;
-     long stretch;
-     int *stretched_p;
+xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
 {
 {
+  xtensa_isa isa = xtensa_default_isa;
   int unreported = fragP->tc_frag_data.unreported_expansion;
   long new_stretch = 0;
   char *file_name;
   int unreported = fragP->tc_frag_data.unreported_expansion;
   long new_stretch = 0;
   char *file_name;
-  int line, lit_size;
+  unsigned line;
+  int lit_size;
+  static xtensa_insnbuf vbuf = NULL;
+  int slot, num_slots;
+  xtensa_format fmt;
 
   as_where (&file_name, &line);
   new_logical_line (fragP->fr_file, fragP->fr_line);
 
   as_where (&file_name, &line);
   new_logical_line (fragP->fr_file, fragP->fr_line);
@@ -6508,7 +8294,8 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
     {
     case RELAX_ALIGN_NEXT_OPCODE:
       /* Always convert.  */
     {
     case RELAX_ALIGN_NEXT_OPCODE:
       /* Always convert.  */
-      new_stretch = relax_frag_text_align (fragP, stretch);
+      if (fragP->tc_frag_data.relax_seen)
+       new_stretch = relax_frag_loop_align (fragP, stretch);
       break;
 
     case RELAX_LOOP_END:
       break;
 
     case RELAX_LOOP_END:
@@ -6518,11 +8305,11 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
     case RELAX_LOOP_END_ADD_NOP:
       /* Add a NOP and switch to .fill 0.  */
       new_stretch = relax_frag_add_nop (fragP);
     case RELAX_LOOP_END_ADD_NOP:
       /* Add a NOP and switch to .fill 0.  */
       new_stretch = relax_frag_add_nop (fragP);
+      frag_wane (fragP);
       break;
 
     case RELAX_DESIRE_ALIGN:
       break;
 
     case RELAX_DESIRE_ALIGN:
-      /* We REALLY want to change the relaxation order here.  This
-         should do NOTHING.  The narrowing before it will either align
+      /* Do nothing. The narrowing before this frag will either align
          it or not.  */
       break;
 
          it or not.  */
       break;
 
@@ -6540,54 +8327,86 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
       new_stretch = 4;
       break;
 
       new_stretch = 4;
       break;
 
-    case RELAX_NARROW:
-      new_stretch = relax_frag_narrow (fragP, stretch);
-      break;
+    case RELAX_SLOTS:
+      if (vbuf == NULL)
+       vbuf = xtensa_insnbuf_alloc (isa);
+
+      xtensa_insnbuf_from_chars
+       (isa, vbuf, (unsigned char *) fragP->fr_opcode, 0);
+      fmt = xtensa_format_decode (isa, vbuf);
+      num_slots = xtensa_format_num_slots (isa, fmt);
+
+      for (slot = 0; slot < num_slots; slot++)
+       {
+         switch (fragP->tc_frag_data.slot_subtypes[slot])
+           {
+           case RELAX_NARROW:
+             if (fragP->tc_frag_data.relax_seen)
+               new_stretch += relax_frag_for_align (fragP, stretch);
+             break;
+
+           case RELAX_IMMED:
+           case RELAX_IMMED_STEP1:
+           case RELAX_IMMED_STEP2:
+           case RELAX_IMMED_STEP3:
+             /* Place the immediate.  */
+             new_stretch += relax_frag_immed
+               (now_seg, fragP, stretch,
+                fragP->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
+                fmt, slot, stretched_p, FALSE);
+             break;
 
 
-    case RELAX_IMMED:
-    case RELAX_IMMED_STEP1:
-    case RELAX_IMMED_STEP2:
-      /* Place the immediate.  */
-      new_stretch = relax_frag_immed (now_seg, fragP, stretch,
-                                     fragP->fr_subtype - RELAX_IMMED,
-                                     stretched_p);
+           default:
+             /* This is OK; see the note in xg_assemble_vliw_tokens.  */
+             break;
+           }
+       }
       break;
 
     case RELAX_LITERAL_POOL_BEGIN:
     case RELAX_LITERAL_POOL_END:
       break;
 
     case RELAX_LITERAL_POOL_BEGIN:
     case RELAX_LITERAL_POOL_END:
+    case RELAX_MAYBE_UNREACHABLE:
+    case RELAX_MAYBE_DESIRE_ALIGN:
       /* No relaxation required.  */
       break;
 
       /* No relaxation required.  */
       break;
 
+    case RELAX_FILL_NOP:
+    case RELAX_UNREACHABLE:
+      if (fragP->tc_frag_data.relax_seen)
+       new_stretch += relax_frag_for_align (fragP, stretch);
+      break;
+
     default:
       as_bad (_("bad relaxation state"));
     }
 
     default:
       as_bad (_("bad relaxation state"));
     }
 
+  /* Tell gas we need another relaxation pass.  */
+  if (! fragP->tc_frag_data.relax_seen)
+    {
+      fragP->tc_frag_data.relax_seen = TRUE;
+      *stretched_p = 1;
+    }
+
   new_logical_line (file_name, line);
   return new_stretch;
 }
 
 
 static long
   new_logical_line (file_name, line);
   return new_stretch;
 }
 
 
 static long
-relax_frag_text_align (fragP, stretch)
-     fragS *fragP;
-     long stretch;
+relax_frag_loop_align (fragS *fragP, long stretch)
 {
   addressT old_address, old_next_address, old_size;
   addressT new_address, new_next_address, new_size;
   addressT growth;
 
 {
   addressT old_address, old_next_address, old_size;
   addressT new_address, new_next_address, new_size;
   addressT growth;
 
-  /* Overview of the relaxation procedure for alignment
-     inside an executable section:
-    
-     The old size is stored in the tc_frag_data.text_expansion field.
-    
-     Calculate the new address, fix up the text_expansion and
-     return the growth.  */
+  /* 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.
+     But, this will put it in if necessary.  */
 
   /* Calculate the old address of this fragment and the next fragment.  */
   old_address = fragP->fr_address - stretch;
   old_next_address = (fragP->fr_address - stretch + fragP->fr_fix +
 
   /* Calculate the old address of this fragment and the next fragment.  */
   old_address = fragP->fr_address - stretch;
   old_next_address = (fragP->fr_address - stretch + fragP->fr_fix +
-                     fragP->tc_frag_data.text_expansion);
+                     fragP->tc_frag_data.text_expansion[0]);
   old_size = old_next_address - old_address;
 
   /* Calculate the new address of this fragment and the next fragment.  */
   old_size = old_next_address - old_address;
 
   /* Calculate the new address of this fragment and the next fragment.  */
@@ -6599,198 +8418,510 @@ relax_frag_text_align (fragP, stretch)
   growth = new_size - old_size;
 
   /* Fix up the text_expansion field and return the new growth.  */
   growth = new_size - old_size;
 
   /* Fix up the text_expansion field and return the new growth.  */
-  fragP->tc_frag_data.text_expansion += growth;
+  fragP->tc_frag_data.text_expansion[0] += growth;
   return growth;
 }
 
 
   return growth;
 }
 
 
-/* Add a NOP (i.e., "or a1, a1, a1").  Use the 3-byte one because we
-   don't know about the availability of density yet.  TODO: When the
-   flags are stored per fragment, use NOP.N when possible.  */
+/* Add a NOP instruction.  */
 
 static long
 
 static long
-relax_frag_add_nop (fragP)
-     fragS *fragP;
+relax_frag_add_nop (fragS *fragP)
 {
 {
-  static xtensa_insnbuf insnbuf = NULL;
-  TInsn t_insn;
   char *nop_buf = fragP->fr_literal + fragP->fr_fix;
   char *nop_buf = fragP->fr_literal + fragP->fr_fix;
-  int length;
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
-  tinsn_init (&t_insn);
-  t_insn.opcode = xtensa_or_opcode;
-  assert (t_insn.opcode != XTENSA_UNDEFINED);
-
-  t_insn.ntok = 3;
-  set_expr_const (&t_insn.tok[0], 1);
-  set_expr_const (&t_insn.tok[1], 1);
-  set_expr_const (&t_insn.tok[2], 1);
-
-  tinsn_to_insnbuf (&t_insn, insnbuf);
+  int length = fragP->tc_frag_data.is_no_density ? 3 : 2;
+  assemble_nop (length, nop_buf);
   fragP->tc_frag_data.is_insn = TRUE;
   fragP->tc_frag_data.is_insn = TRUE;
-  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, nop_buf);
 
 
-  length = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
   if (fragP->fr_var < length)
     {
   if (fragP->fr_var < length)
     {
-      as_warn (_("fr_var (%ld) < length (%d); ignoring"),
-              fragP->fr_var, length);
-      frag_wane (fragP);
+      as_fatal (_("fr_var (%ld) < length (%d)"), (long) fragP->fr_var, length);
       return 0;
     }
 
   fragP->fr_fix += length;
   fragP->fr_var -= length;
       return 0;
     }
 
   fragP->fr_fix += length;
   fragP->fr_var -= length;
-  frag_wane (fragP);
   return length;
 }
 
 
   return length;
 }
 
 
+static long future_alignment_required (fragS *, long);
+
 static long
 static long
-relax_frag_narrow (fragP, stretch)
-     fragS *fragP;
-     long stretch;
+relax_frag_for_align (fragS *fragP, long stretch)
 {
 {
-  /* Overview of the relaxation procedure for alignment inside an
-     executable section: Find the number of widenings required and the
-     number of nop bytes required. Store the number of bytes ALREADY
-     widened. If there are enough instructions to widen (must go back
-     ONLY through NARROW fragments), mark each of the fragments as TO BE
-     widened, recalculate the fragment addresses.  */
+  /* Overview of the relaxation procedure for alignment:
+     We can widen with NOPs or by widening instructions or by filling
+     bytes after jump instructions.  Find the opportune places and widen
+     them if necessary.  */
+
+  long stretch_me;
+  long diff;
 
 
-  assert (fragP->fr_type == rs_machine_dependent
-         && fragP->fr_subtype == RELAX_NARROW);
+  assert (fragP->fr_subtype == RELAX_FILL_NOP
+         || fragP->fr_subtype == RELAX_UNREACHABLE
+         || (fragP->fr_subtype == RELAX_SLOTS
+             && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW));
+
+  stretch_me = future_alignment_required (fragP, stretch);
+  diff = stretch_me - fragP->tc_frag_data.text_expansion[0];
+  if (diff == 0)
+    return 0;
 
 
-  if (!future_alignment_required (fragP, 0))
+  if (diff < 0)
     {
     {
-      /* If already expanded but no longer needed because of a prior
-         stretch, it is SAFE to unexpand because the next fragment will
-         NEVER start at an address > the previous time through the
-         relaxation.  */
-      if (fragP->tc_frag_data.text_expansion)
+      /* We expanded on a previous pass.  Can we shrink now?  */
+      long shrink = fragP->tc_frag_data.text_expansion[0] - stretch_me;
+      if (shrink <= stretch && stretch > 0)
        {
        {
-         if (stretch > 0)
-           {
-             fragP->tc_frag_data.text_expansion = 0;
-             return -1;
-           }
-         /* Otherwise we have to live with this bad choice.  */
-         return 0;
+         fragP->tc_frag_data.text_expansion[0] = stretch_me;
+         return -shrink;
        }
       return 0;
     }
 
        }
       return 0;
     }
 
-  if (fragP->tc_frag_data.text_expansion == 0)
-    {
-      fragP->tc_frag_data.text_expansion = 1;
-      return 1;
-    }
+  /* Below here, diff > 0.  */
+  fragP->tc_frag_data.text_expansion[0] = stretch_me;
 
 
-  return 0;
+  return diff;
 }
 
 
 }
 
 
-static bfd_boolean
-future_alignment_required (fragP, stretch)
-     fragS *fragP;
-     long stretch;
+/* Return the address of the next frag that should be aligned.
+
+   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.
+   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
+   to align targets have already expanded on a previous relaxation
+   pass.
+
+   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
+   aligned.  */
+
+static addressT
+find_address_of_next_align_frag (fragS **fragPP,
+                                int *wide_nops,
+                                int *narrow_nops,
+                                int *widens,
+                                bfd_boolean *paddable)
 {
 {
-  long address = fragP->fr_address + stretch;
-  int num_widens = 0;
-  addressT aligned_address;
-  offsetT desired_diff;
+  fragS *fragP = *fragPP;
+  addressT address = fragP->fr_address;
+
+  /* Do not reset the counts to 0.  */
 
   while (fragP)
     {
       /* Limit this to a small search.  */
 
   while (fragP)
     {
       /* Limit this to a small search.  */
-      if (num_widens > 8)
-       return FALSE;
+      if (*widens >= (int) xtensa_fetch_width)
+       {
+         *fragPP = fragP;
+         return 0;
+       }
       address += fragP->fr_fix;
 
       address += fragP->fr_fix;
 
-      switch (fragP->fr_type)
+      if (fragP->fr_type == rs_fill)
+       address += fragP->fr_offset * fragP->fr_var;
+      else if (fragP->fr_type == rs_machine_dependent)
        {
        {
-       case rs_fill:
-         address += fragP->fr_offset * fragP->fr_var;
-         break;
-
-       case rs_machine_dependent:
          switch (fragP->fr_subtype)
            {
          switch (fragP->fr_subtype)
            {
-           case RELAX_NARROW:
-             /* address += fragP->fr_fix; */
-             num_widens++;
+           case RELAX_UNREACHABLE:
+             *paddable = TRUE;
+             break;
+
+           case RELAX_FILL_NOP:
+             (*wide_nops)++;
+             if (!fragP->tc_frag_data.is_no_density)
+               (*narrow_nops)++;
+             break;
+
+           case RELAX_SLOTS:
+             if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+               {
+                 (*widens)++;
+                 break;
+               }
+             address += total_frag_text_expansion (fragP);;
              break;
 
            case RELAX_IMMED:
              break;
 
            case RELAX_IMMED:
-             address += (/* fragP->fr_fix + */
-                         fragP->tc_frag_data.text_expansion);
+             address += fragP->tc_frag_data.text_expansion[0];
              break;
 
            case RELAX_ALIGN_NEXT_OPCODE:
            case RELAX_DESIRE_ALIGN:
              break;
 
            case RELAX_ALIGN_NEXT_OPCODE:
            case RELAX_DESIRE_ALIGN:
-             /* address += fragP->fr_fix; */
-             aligned_address = get_widen_aligned_address (fragP, address);
-             desired_diff = aligned_address - address;
-             assert (desired_diff >= 0);
-             /* If there are enough wideners in between do it.  */
-             /* return (num_widens == desired_diff); */
-             if (num_widens == desired_diff)
-               return TRUE;
-             if (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
-               return FALSE;
+             *fragPP = fragP;
+             return address;
+
+           case RELAX_MAYBE_UNREACHABLE:
+           case RELAX_MAYBE_DESIRE_ALIGN:
+             /* Do nothing.  */
              break;
 
            default:
              break;
 
            default:
-             return FALSE;
+             /* Just punt if we don't know the type.  */
+             *fragPP = fragP;
+             return 0;
            }
            }
-         break;
-
-       default:
-         return FALSE;
+       }
+      else
+       {
+         /* Just punt if we don't know the type.  */
+         *fragPP = fragP;
+         return 0;
        }
       fragP = fragP->fr_next;
     }
 
        }
       fragP = fragP->fr_next;
     }
 
-  return FALSE;
+  *fragPP = fragP;
+  return 0;
 }
 
 
 }
 
 
+static long bytes_to_stretch (fragS *, int, int, int, int);
+
 static long
 static long
-relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
-     segT segP;
-     fragS *fragP;
-     long stretch;
-     int min_steps;
-     int *stretched_p;
+future_alignment_required (fragS *fragP, long stretch ATTRIBUTE_UNUSED)
 {
 {
-  static xtensa_insnbuf insnbuf = NULL;
-  TInsn t_insn;
+  fragS *this_frag = fragP;
+  long address;
+  int num_widens = 0;
+  int wide_nops = 0;
+  int narrow_nops = 0;
+  bfd_boolean paddable = FALSE;
+  offsetT local_opt_diff;
+  offsetT opt_diff;
+  offsetT max_diff;
+  int stretch_amount = 0;
+  int local_stretch_amount;
+  int global_stretch_amount;
+
+  address = find_address_of_next_align_frag
+    (&fragP, &wide_nops, &narrow_nops, &num_widens, &paddable);
+
+  if (!address)
+    {
+      if (this_frag->tc_frag_data.is_aligning_branch)
+       this_frag->tc_frag_data.slot_subtypes[0] = RELAX_IMMED;
+      else
+       frag_wane (this_frag);
+    }
+  else
+    {
+      local_opt_diff = get_aligned_diff (fragP, address, &max_diff);
+      opt_diff = local_opt_diff;
+      assert (opt_diff >= 0);
+      assert (max_diff >= opt_diff);
+      if (max_diff == 0)
+       return 0;
+
+      if (fragP)
+       fragP = fragP->fr_next;
+
+      while (fragP && opt_diff < max_diff && address)
+       {
+         /* We only use these to determine if we can exit early
+            because there will be plenty of ways to align future
+            align frags.  */
+         int glob_widens = 0;
+         int dnn = 0;
+         int dw = 0;
+         bfd_boolean glob_pad = 0;
+         address = find_address_of_next_align_frag
+           (&fragP, &glob_widens, &dnn, &dw, &glob_pad);
+         /* If there is a padable portion, then skip.  */
+         if (glob_pad || glob_widens >= (1 << branch_align_power (now_seg)))
+           address = 0;
+
+         if (address)
+           {
+             offsetT next_m_diff;
+             offsetT next_o_diff;
+
+             /* Downrange frags haven't had stretch added to them yet.  */
+             address += stretch;
+
+             /* The address also includes any text expansion from this
+                frag in a previous pass, but we don't want that.  */
+             address -= this_frag->tc_frag_data.text_expansion[0];
+
+             /* Assume we are going to move at least opt_diff.  In
+                reality, we might not be able to, but assuming that
+                we will helps catch cases where moving opt_diff pushes
+                the next target from aligned to unaligned.  */
+             address += opt_diff;
+
+             next_o_diff = get_aligned_diff (fragP, address, &next_m_diff);
+
+             /* Now cleanup for the adjustments to address.  */
+             next_o_diff += opt_diff;
+             next_m_diff += opt_diff;
+             if (next_o_diff <= max_diff && next_o_diff > opt_diff)
+               opt_diff = next_o_diff;
+             if (next_m_diff < max_diff)
+               max_diff = next_m_diff;
+             fragP = fragP->fr_next;
+           }
+       }
+
+      /* If there are enough wideners in between, do it.  */
+      if (paddable)
+       {
+         if (this_frag->fr_subtype == RELAX_UNREACHABLE)
+           {
+             assert (opt_diff <= UNREACHABLE_MAX_WIDTH);
+             return opt_diff;
+           }
+         return 0;
+       }
+      local_stretch_amount
+       = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
+                           num_widens, local_opt_diff);
+      global_stretch_amount
+       = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
+                           num_widens, opt_diff);
+      /* If the condition below is true, then the frag couldn't
+        stretch the correct amount for the global case, so we just
+        optimize locally.  We'll rely on the subsequent frags to get
+        the correct alignment in the global case.  */
+      if (global_stretch_amount < local_stretch_amount)
+       stretch_amount = local_stretch_amount;
+      else
+       stretch_amount = global_stretch_amount;
+
+      if (this_frag->fr_subtype == RELAX_SLOTS
+         && this_frag->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+       assert (stretch_amount <= 1);
+      else if (this_frag->fr_subtype == RELAX_FILL_NOP)
+       {
+         if (this_frag->tc_frag_data.is_no_density)
+           assert (stretch_amount == 3 || stretch_amount == 0);
+         else
+           assert (stretch_amount <= 3);
+       }
+    }
+  return stretch_amount;
+}
+
+
+/* The idea: widen everything you can to get a target or loop aligned,
+   then start using NOPs.
+
+   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):
+
+   wide_nops   = the number of wide NOPs available for aligning
+   narrow_nops = the number of narrow NOPs available for aligning
+                (a subset of wide_nops)
+   widens      = the number of narrow instructions that should be widened
+
+   Desired   wide   narrow
+   Diff      nop    nop      widens
+   1           0      0         1
+   2           0      1         0
+   3a          1      0         0
+    b          0      1         1 (case 3a makes this case unnecessary)
+   4a          1      0         1
+    b          0      2         0
+    c          0      1         2 (case 4a makes this case unnecessary)
+   5a          1      0         2
+    b          1      1         0
+    c          0      2         1 (case 5b makes this case unnecessary)
+   6a          2      0         0
+    b          1      0         3
+    c          0      1         4 (case 6b makes this case unnecessary)
+    d          1      1         1 (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)
+   7a          1      0         4
+    b          2      0         1
+    c          1      1         2 (case 7b makes this case unnecessary)
+    d          0      1         5 (case 7a makes this case unnecessary)
+    e          0      2         3 (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)
+*/
+
+static long
+bytes_to_stretch (fragS *this_frag,
+                 int wide_nops,
+                 int narrow_nops,
+                 int num_widens,
+                 int desired_diff)
+{
+  int bytes_short = desired_diff - num_widens;
+
+  assert (desired_diff >= 0 && desired_diff < 8);
+  if (desired_diff == 0)
+    return 0;
+
+  assert (wide_nops > 0 || num_widens > 0);
+
+  /* Always prefer widening to NOP-filling.  */
+  if (bytes_short < 0)
+    {
+      /* There are enough RELAX_NARROW frags after this one
+        to align the target without widening this frag in any way.  */
+      return 0;
+    }
+
+  if (bytes_short == 0)
+    {
+      /* Widen every narrow between here and the align target
+        and the align target will be properly aligned.  */
+      if (this_frag->fr_subtype == RELAX_FILL_NOP)
+       return 0;
+      else
+       return 1;
+    }
+
+  /* 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,
+     don't widen.  */
+  if (this_frag->fr_subtype == RELAX_FILL_NOP)
+    {
+      switch (desired_diff)
+       {
+       case 1:
+         return 0;
+       case 2:
+         if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 1)
+           return 2; /* case 2 */
+         return 0;
+       case 3:
+         if (wide_nops > 1)
+           return 0;
+         else
+           return 3; /* case 3a */
+       case 4:
+         if (num_widens >= 1 && wide_nops == 1)
+           return 3; /* case 4a */
+         if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 2)
+           return 2; /* case 4b */
+         return 0;
+       case 5:
+         if (num_widens >= 2 && wide_nops == 1)
+           return 3; /* case 5a */
+         /* We will need two nops.  Are there enough nops
+            between here and the align target?  */
+         if (wide_nops < 2 || narrow_nops == 0)
+           return 0;
+         /* Are there other nops closer that can serve instead?  */
+         if (wide_nops > 2 && narrow_nops > 1)
+           return 0;
+         /* Take the density one first, because there might not be
+            another density one available.  */
+         if (!this_frag->tc_frag_data.is_no_density)
+           return 2; /* case 5b narrow */
+         else
+           return 3; /* case 5b wide */
+         return 0;
+       case 6:
+         if (wide_nops == 2)
+           return 3; /* case 6a */
+         else if (num_widens >= 3 && wide_nops == 1)
+           return 3; /* case 6b */
+         return 0;
+       case 7:
+         if (wide_nops == 1 && num_widens >= 4)
+           return 3; /* case 7a */
+         else if (wide_nops == 2 && num_widens >= 1)
+           return 3; /* case 7b */
+         return 0;
+       default:
+         assert (0);
+       }
+    }
+  else
+    {
+      /* We will need a NOP no matter what, but should we widen
+        this instruction to help?
+
+        This is a RELAX_NARROW frag.  */
+      switch (desired_diff)
+       {
+       case 1:
+         assert (0);
+         return 0;
+       case 2:
+       case 3:
+         return 0;
+       case 4:
+         if (wide_nops >= 1 && num_widens == 1)
+           return 1; /* case 4a */
+         return 0;
+       case 5:
+         if (wide_nops >= 1 && num_widens == 2)
+           return 1; /* case 5a */
+         return 0;
+       case 6:
+         if (wide_nops >= 2)
+           return 0; /* case 6a */
+         else if (wide_nops >= 1 && num_widens == 3)
+           return 1; /* case 6b */
+         return 0;
+       case 7:
+         if (wide_nops >= 1 && num_widens == 4)
+           return 1; /* case 7a */
+         else if (wide_nops >= 2 && num_widens == 1)
+           return 1; /* case 7b */
+         return 0;
+       default:
+         assert (0);
+         return 0;
+       }
+    }
+  assert (0);
+  return 0;
+}
+
+
+static long
+relax_frag_immed (segT segP,
+                 fragS *fragP,
+                 long stretch,
+                 int min_steps,
+                 xtensa_format fmt,
+                 int slot,
+                 int *stretched_p,
+                 bfd_boolean estimate_only)
+{
+  TInsn tinsn;
   int old_size;
   bfd_boolean negatable_branch = FALSE;
   bfd_boolean branch_jmp_to_next = FALSE;
   int old_size;
   bfd_boolean negatable_branch = FALSE;
   bfd_boolean branch_jmp_to_next = FALSE;
+  bfd_boolean wide_insn = FALSE;
+  xtensa_isa isa = xtensa_default_isa;
   IStack istack;
   offsetT frag_offset;
   int num_steps;
   fragS *lit_fragP;
   int num_text_bytes, num_literal_bytes;
   IStack istack;
   offsetT frag_offset;
   int num_steps;
   fragS *lit_fragP;
   int num_text_bytes, num_literal_bytes;
-  int literal_diff, text_diff;
+  int literal_diff, total_text_diff, this_text_diff, first;
 
   assert (fragP->fr_opcode != NULL);
 
 
   assert (fragP->fr_opcode != NULL);
 
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+  xg_clear_vinsn (&cur_vinsn);
+  vinsn_from_chars (&cur_vinsn, fragP->fr_opcode);
+  if (cur_vinsn.num_slots > 1)
+    wide_insn = TRUE;
 
 
-  tinsn_from_chars (&t_insn, fragP->fr_opcode);
-  tinsn_immed_from_frag (&t_insn, fragP);
+  tinsn = cur_vinsn.slots[slot];
+  tinsn_immed_from_frag (&tinsn, fragP, slot);
+
+  if (estimate_only && xtensa_opcode_is_loop (isa, tinsn.opcode) == 1)
+    return 0;
 
 
-  negatable_branch = is_negatable_branch (&t_insn);
+  if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
+    branch_jmp_to_next = is_branch_jmp_to_next (&tinsn, fragP);
 
 
-  old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
+  negatable_branch = (xtensa_opcode_is_branch (isa, tinsn.opcode) == 1);
 
 
-  if (software_avoid_b_j_loop_end)
-    branch_jmp_to_next = is_branch_jmp_to_next (&t_insn, fragP);
+  old_size = xtensa_format_length (isa, fmt);
 
   /* 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
 
   /* 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
@@ -6807,7 +8938,7 @@ relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
 
   frag_offset = fragP->fr_opcode - fragP->fr_literal;
   istack_init (&istack);
 
   frag_offset = fragP->fr_opcode - fragP->fr_literal;
   istack_init (&istack);
-  num_steps = xg_assembly_relax (&istack, &t_insn, segP, fragP, frag_offset,
+  num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP, frag_offset,
                                 min_steps, stretch);
   if (num_steps < min_steps)
     {
                                 min_steps, stretch);
   if (num_steps < min_steps)
     {
@@ -6821,26 +8952,40 @@ relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
       return 0;
     }
 
       return 0;
     }
 
-  fragP->fr_subtype = (int) RELAX_IMMED + num_steps;
+  fragP->tc_frag_data.slot_subtypes[slot] = (int) RELAX_IMMED + num_steps;
 
   /* Figure out the number of bytes needed.  */
   lit_fragP = 0;
 
   /* Figure out the number of bytes needed.  */
   lit_fragP = 0;
-  num_text_bytes = get_num_stack_text_bytes (&istack) - old_size;
   num_literal_bytes = get_num_stack_literal_bytes (&istack);
   num_literal_bytes = get_num_stack_literal_bytes (&istack);
-  literal_diff = num_literal_bytes - fragP->tc_frag_data.literal_expansion;
-  text_diff = num_text_bytes - fragP->tc_frag_data.text_expansion;
+  literal_diff =
+    num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
+  first = 0;
+  while (istack.insn[first].opcode == XTENSA_UNDEFINED)
+    first++;
+  num_text_bytes = get_num_stack_text_bytes (&istack);
+  if (wide_insn)
+    {
+      num_text_bytes += old_size;
+      if (opcode_fits_format_slot (istack.insn[first].opcode, fmt, slot))
+       num_text_bytes -= xg_get_single_size (istack.insn[first].opcode);
+    }
+  total_text_diff = num_text_bytes - old_size;
+  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.  */
-  know (num_text_bytes >= 0);
-  know (literal_diff >= 0 && text_diff >= 0);
+  assert (num_text_bytes >= 0);
+  assert (literal_diff >= 0);
+  assert (total_text_diff >= 0);
 
 
-  fragP->tc_frag_data.text_expansion = num_text_bytes;
-  fragP->tc_frag_data.literal_expansion = num_literal_bytes;
+  fragP->tc_frag_data.text_expansion[slot] = total_text_diff;
+  fragP->tc_frag_data.literal_expansion[slot] = num_literal_bytes;
+  assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
+  assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
 
   /* Find the associated expandable literal for this.  */
   if (literal_diff != 0)
     {
 
   /* Find the associated expandable literal for this.  */
   if (literal_diff != 0)
     {
-      lit_fragP = fragP->tc_frag_data.literal_frag;
+      lit_fragP = fragP->tc_frag_data.literal_frags[slot];
       if (lit_fragP)
        {
          assert (literal_diff == 4);
       if (lit_fragP)
        {
          assert (literal_diff == 4);
@@ -6858,29 +9003,30 @@ relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
        }
     }
 
        }
     }
 
-  /* This implicitly uses the assumption that a branch is negated
-     when the size of the output increases by at least 2 bytes.  */
-
-  if (negatable_branch && num_text_bytes >= 2)
-    {
-      /* If next frag is a loop end, then switch it to add a NOP.  */
-      update_next_frag_nop_state (fragP);
-    }
+  if (negatable_branch && istack.ninsn > 1)
+    update_next_frag_state (fragP);
 
 
-  return text_diff;
+  return this_text_diff;
 }
 
 \f
 /* md_convert_frag Hook and Helper Functions.  */
 
 }
 
 \f
 /* md_convert_frag Hook and Helper Functions.  */
 
+static void convert_frag_align_next_opcode (fragS *);
+static void convert_frag_narrow (segT, fragS *, xtensa_format, int);
+static void convert_frag_fill_nop (fragS *);
+static void convert_frag_immed (segT, fragS *, int, xtensa_format, int);
+
 void
 void
-md_convert_frag (abfd, sec, fragp)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     segT sec;
-     fragS *fragp;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragp)
 {
 {
+  static xtensa_insnbuf vbuf = NULL;
+  xtensa_isa isa = xtensa_default_isa;
+  int slot;
+  int num_slots;
+  xtensa_format fmt;
   char *file_name;
   char *file_name;
-  int line;
+  unsigned line;
 
   as_where (&file_name, &line);
   new_logical_line (fragp->fr_file, fragp->fr_line);
 
   as_where (&file_name, &line);
   new_logical_line (fragp->fr_file, fragp->fr_line);
@@ -6900,16 +9046,56 @@ md_convert_frag (abfd, sec, fragp)
     case RELAX_LITERAL_FINAL:
       break;
 
     case RELAX_LITERAL_FINAL:
       break;
 
-    case RELAX_NARROW:
-      /* No conversion.  */
-      convert_frag_narrow (fragp);
+    case RELAX_SLOTS:
+      if (vbuf == NULL)
+       vbuf = xtensa_insnbuf_alloc (isa);
+
+      xtensa_insnbuf_from_chars
+       (isa, vbuf, (unsigned char *) fragp->fr_opcode, 0);
+      fmt = xtensa_format_decode (isa, vbuf);
+      num_slots = xtensa_format_num_slots (isa, fmt);
+
+      for (slot = 0; slot < num_slots; slot++)
+       {
+         switch (fragp->tc_frag_data.slot_subtypes[slot])
+           {
+           case RELAX_NARROW:
+             convert_frag_narrow (sec, fragp, fmt, slot);
+             break;
+
+           case RELAX_IMMED:
+           case RELAX_IMMED_STEP1:
+           case RELAX_IMMED_STEP2:
+           case RELAX_IMMED_STEP3:
+             /* Place the immediate.  */
+             convert_frag_immed
+               (sec, fragp,
+                fragp->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
+                fmt, slot);
+             break;
+
+           default:
+             /* This is OK because some slots could have
+                relaxations and others have none.  */
+             break;
+           }
+       }
+      break;
+
+    case RELAX_UNREACHABLE:
+      memset (&fragp->fr_literal[fragp->fr_fix], 0, fragp->fr_var);
+      fragp->fr_fix += fragp->tc_frag_data.text_expansion[0];
+      fragp->fr_var -= fragp->tc_frag_data.text_expansion[0];
+      frag_wane (fragp);
       break;
 
       break;
 
-    case RELAX_IMMED:
-    case RELAX_IMMED_STEP1:
-    case RELAX_IMMED_STEP2:
-      /* Place the immediate.  */
-      convert_frag_immed (sec, fragp, fragp->fr_subtype - RELAX_IMMED);
+    case RELAX_MAYBE_UNREACHABLE:
+    case RELAX_MAYBE_DESIRE_ALIGN:
+      frag_wane (fragp);
+      break;
+
+    case RELAX_FILL_NOP:
+      convert_frag_fill_nop (fragp);
       break;
 
     case RELAX_LITERAL_NR:
       break;
 
     case RELAX_LITERAL_NR:
@@ -6947,16 +9133,14 @@ md_convert_frag (abfd, sec, fragp)
 }
 
 
 }
 
 
-void
-convert_frag_align_next_opcode (fragp)
-     fragS *fragp;
+static void
+convert_frag_align_next_opcode (fragS *fragp)
 {
   char *nop_buf;               /* Location for Writing.  */
 {
   char *nop_buf;               /* Location for Writing.  */
-  size_t i;
-
   bfd_boolean use_no_density = fragp->tc_frag_data.is_no_density;
   addressT aligned_address;
   bfd_boolean use_no_density = fragp->tc_frag_data.is_no_density;
   addressT aligned_address;
-  size_t fill_size, nop_count;
+  offsetT fill_size;
+  int nop, nop_count;
 
   aligned_address = get_noop_aligned_address (fragp, fragp->fr_address +
                                              fragp->fr_fix);
 
   aligned_address = get_noop_aligned_address (fragp, fragp->fr_address +
                                              fragp->fr_fix);
@@ -6964,10 +9148,10 @@ convert_frag_align_next_opcode (fragp)
   nop_count = get_text_align_nop_count (fill_size, use_no_density);
   nop_buf = fragp->fr_literal + fragp->fr_fix;
 
   nop_count = get_text_align_nop_count (fill_size, use_no_density);
   nop_buf = fragp->fr_literal + fragp->fr_fix;
 
-  for (i = 0; i < nop_count; i++)
+  for (nop = 0; nop < nop_count; nop++)
     {
     {
-      size_t nop_size;
-      nop_size = get_text_align_nth_nop_size (fill_size, i, use_no_density);
+      int nop_size;
+      nop_size = get_text_align_nth_nop_size (fill_size, nop, use_no_density);
 
       assemble_nop (nop_size, nop_buf);
       nop_buf += nop_size;
 
       assemble_nop (nop_size, nop_buf);
       nop_buf += nop_size;
@@ -6979,15 +9163,25 @@ convert_frag_align_next_opcode (fragp)
 
 
 static void
 
 
 static void
-convert_frag_narrow (fragP)
-     fragS *fragP;
+convert_frag_narrow (segT segP, fragS *fragP, xtensa_format fmt, int slot)
 {
 {
-  static xtensa_insnbuf insnbuf = NULL;
-  TInsn t_insn, single_target;
-  int size, old_size, diff, error_val;
+  TInsn tinsn, single_target;
+  int size, old_size, diff;
   offsetT frag_offset;
 
   offsetT frag_offset;
 
-  if (fragP->tc_frag_data.text_expansion == 0)
+  assert (slot == 0);
+  tinsn_from_chars (&tinsn, fragP->fr_opcode, 0);
+
+  if (fragP->tc_frag_data.is_aligning_branch == 1)
+    {
+      assert (fragP->tc_frag_data.text_expansion[0] == 1
+             || fragP->tc_frag_data.text_expansion[0] == 0);
+      convert_frag_immed (segP, fragP, fragP->tc_frag_data.text_expansion[0],
+                         fmt, slot);
+      return;
+    }
+
+  if (fragP->tc_frag_data.text_expansion[0] == 0)
     {
       /* No conversion.  */
       fragP->fr_var = 0;
     {
       /* No conversion.  */
       fragP->fr_var = 0;
@@ -6996,26 +9190,26 @@ convert_frag_narrow (fragP)
 
   assert (fragP->fr_opcode != NULL);
 
 
   assert (fragP->fr_opcode != NULL);
 
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
-  tinsn_from_chars (&t_insn, fragP->fr_opcode);
-  tinsn_immed_from_frag (&t_insn, fragP);
+  /* Frags in this relaxation state should only contain
+     single instruction bundles.  */
+  tinsn_immed_from_frag (&tinsn, fragP, 0);
 
   /* Just convert it to a wide form....  */
   size = 0;
 
   /* Just convert it to a wide form....  */
   size = 0;
-  old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
+  old_size = xg_get_single_size (tinsn.opcode);
 
   tinsn_init (&single_target);
   frag_offset = fragP->fr_opcode - fragP->fr_literal;
 
 
   tinsn_init (&single_target);
   frag_offset = fragP->fr_opcode - fragP->fr_literal;
 
-  error_val = xg_expand_narrow (&single_target, &t_insn);
-  if (error_val)
-    as_bad (_("unable to widen instruction"));
+  if (! xg_is_single_relaxable_insn (&tinsn, &single_target, FALSE))
+    {
+      as_bad (_("unable to widen instruction"));
+      return;
+    }
 
 
-  size = xtensa_insn_length (xtensa_default_isa, single_target.opcode);
-  xg_emit_insn_to_buf (&single_target, fragP->fr_opcode,
-                      fragP, frag_offset, TRUE);
+  size = xg_get_single_size (single_target.opcode);
+  xg_emit_insn_to_buf (&single_target, fragP->fr_opcode, fragP,
+                      frag_offset, TRUE);
 
   diff = size - old_size;
   assert (diff >= 0);
 
   diff = size - old_size;
   assert (diff >= 0);
@@ -7029,81 +9223,132 @@ convert_frag_narrow (fragP)
 
 
 static void
 
 
 static void
-convert_frag_immed (segP, fragP, min_steps)
-     segT segP;
-     fragS *fragP;
-     int min_steps;
+convert_frag_fill_nop (fragS *fragP)
+{
+  char *loc = &fragP->fr_literal[fragP->fr_fix];
+  int size = fragP->tc_frag_data.text_expansion[0];
+  assert ((unsigned) size == (fragP->fr_next->fr_address
+                             - fragP->fr_address - fragP->fr_fix));
+  if (size == 0)
+    {
+      /* No conversion.  */
+      fragP->fr_var = 0;
+      return;
+    }
+  assemble_nop (size, loc);
+  fragP->tc_frag_data.is_insn = TRUE;
+  fragP->fr_var -= size;
+  fragP->fr_fix += size;
+  frag_wane (fragP);
+}
+
+
+static fixS *fix_new_exp_in_seg
+  (segT, subsegT, fragS *, int, int, expressionS *, int,
+   bfd_reloc_code_real_type);
+static void convert_frag_immed_finish_loop (segT, fragS *, TInsn *);
+
+static void
+convert_frag_immed (segT segP,
+                   fragS *fragP,
+                   int min_steps,
+                   xtensa_format fmt,
+                   int slot)
 {
   char *immed_instr = fragP->fr_opcode;
 {
   char *immed_instr = fragP->fr_opcode;
-  static xtensa_insnbuf insnbuf = NULL;
-  TInsn orig_t_insn;
+  TInsn orig_tinsn;
   bfd_boolean expanded = FALSE;
   bfd_boolean expanded = FALSE;
-  char *fr_opcode = fragP->fr_opcode;
   bfd_boolean branch_jmp_to_next = FALSE;
   bfd_boolean branch_jmp_to_next = FALSE;
-  int size;
+  char *fr_opcode = fragP->fr_opcode;
+  xtensa_isa isa = xtensa_default_isa;
+  bfd_boolean wide_insn = FALSE;
+  int bytes;
+  bfd_boolean is_loop;
 
 
-  assert (fragP->fr_opcode != NULL);
+  assert (fr_opcode != NULL);
 
 
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+  xg_clear_vinsn (&cur_vinsn);
 
 
-  tinsn_from_chars (&orig_t_insn, fragP->fr_opcode);
-  tinsn_immed_from_frag (&orig_t_insn, fragP);
+  vinsn_from_chars (&cur_vinsn, fr_opcode);
+  if (cur_vinsn.num_slots > 1)
+    wide_insn = TRUE;
 
 
-  /* Here is the fun stuff:  Get the immediate field from this
-     instruction.  If it fits, we're done.  If not, find the next
-     instruction sequence that fits.  */
+  orig_tinsn = cur_vinsn.slots[slot];
+  tinsn_immed_from_frag (&orig_tinsn, fragP, slot);
+
+  is_loop = xtensa_opcode_is_loop (xtensa_default_isa, orig_tinsn.opcode) == 1;
 
 
-  if (software_avoid_b_j_loop_end)
-    branch_jmp_to_next = is_branch_jmp_to_next (&orig_t_insn, fragP);
+  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);
 
   if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
     {
       /* Conversion just inserts a NOP and marks the fix as completed.  */
 
   if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
     {
       /* Conversion just inserts a NOP and marks the fix as completed.  */
-      size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode);
-      assemble_nop (size, fragP->fr_opcode);
+      bytes = xtensa_format_length (isa, fmt);
+      if (bytes >= 4)
+       {
+         cur_vinsn.slots[slot].opcode =
+           xtensa_format_slot_nop_opcode (isa, cur_vinsn.format, slot);
+         cur_vinsn.slots[slot].ntok = 0;
+       }
+      else
+       {
+         bytes += fragP->tc_frag_data.text_expansion[0];
+         assert (bytes == 2 || bytes == 3);
+         build_nop (&cur_vinsn.slots[0], bytes);
+         fragP->fr_fix += fragP->tc_frag_data.text_expansion[0];
+       }
+      vinsn_to_insnbuf (&cur_vinsn, fr_opcode, frag_now, TRUE);
+      xtensa_insnbuf_to_chars
+       (isa, cur_vinsn.insnbuf, (unsigned char *) fr_opcode, 0);
       fragP->fr_var = 0;
     }
   else
     {
       fragP->fr_var = 0;
     }
   else
     {
+      /* Here is the fun stuff:  Get the immediate field from this
+        instruction.  If it fits, we're done.  If not, find the next
+        instruction sequence that fits.  */
+
       IStack istack;
       int i;
       symbolS *lit_sym = NULL;
       int total_size = 0;
       IStack istack;
       int i;
       symbolS *lit_sym = NULL;
       int total_size = 0;
+      int target_offset = 0;
       int old_size;
       int diff;
       symbolS *gen_label = NULL;
       offsetT frag_offset;
       int old_size;
       int diff;
       symbolS *gen_label = NULL;
       offsetT frag_offset;
+      bfd_boolean first = TRUE;
+      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 = fragP->fr_opcode - fragP->fr_literal;
+      frag_offset = fr_opcode - fragP->fr_literal;
       istack_init (&istack);
       istack_init (&istack);
-      xg_assembly_relax (&istack, &orig_t_insn,
+      xg_assembly_relax (&istack, &orig_tinsn,
                         segP, fragP, frag_offset, min_steps, 0);
 
                         segP, fragP, frag_offset, min_steps, 0);
 
-      old_size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode);
+      old_size = xtensa_format_length (isa, fmt);
 
       /* Assemble this right inline.  */
 
       /* First, create the mapping from a label name to the REAL label.  */
 
       /* Assemble this right inline.  */
 
       /* First, create the mapping from a label name to the REAL label.  */
-      total_size = 0;
+      target_offset = 0;
       for (i = 0; i < istack.ninsn; i++)
        {
       for (i = 0; i < istack.ninsn; i++)
        {
-         TInsn *t_insn = &istack.insn[i];
-         int size = 0;
+         TInsn *tinsn = &istack.insn[i];
          fragS *lit_frag;
 
          fragS *lit_frag;
 
-         switch (t_insn->insn_type)
+         switch (tinsn->insn_type)
            {
            case ITYPE_LITERAL:
              if (lit_sym != NULL)
                as_bad (_("multiple literals in expansion"));
              /* First find the appropriate space in the literal pool.  */
            {
            case ITYPE_LITERAL:
              if (lit_sym != NULL)
                as_bad (_("multiple literals in expansion"));
              /* First find the appropriate space in the literal pool.  */
-             lit_frag = fragP->tc_frag_data.literal_frag;
+             lit_frag = fragP->tc_frag_data.literal_frags[slot];
              if (lit_frag == NULL)
                as_bad (_("no registered fragment for literal"));
              if (lit_frag == NULL)
                as_bad (_("no registered fragment for literal"));
-             if (t_insn->ntok != 1)
+             if (tinsn->ntok != 1)
                as_bad (_("number of literal tokens != 1"));
 
              /* Set the literal symbol and add a fixup.  */
                as_bad (_("number of literal tokens != 1"));
 
              /* Set the literal symbol and add a fixup.  */
@@ -7111,53 +9356,109 @@ convert_frag_immed (segP, fragP, min_steps)
              break;
 
            case ITYPE_LABEL:
              break;
 
            case ITYPE_LABEL:
+             if (align_targets && !is_loop)
+               {
+                 fragS *unreach = fragP->fr_next;
+                 while (!(unreach->fr_type == rs_machine_dependent
+                          && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
+                              || unreach->fr_subtype == RELAX_UNREACHABLE)))
+                   {
+                     unreach = unreach->fr_next;
+                   }
+
+                 assert (unreach->fr_type == rs_machine_dependent
+                         && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
+                             || unreach->fr_subtype == RELAX_UNREACHABLE));
+
+                 target_offset += unreach->tc_frag_data.text_expansion[0];
+               }
              assert (gen_label == NULL);
              gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
              assert (gen_label == NULL);
              gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
-                                     fragP->fr_opcode - fragP->fr_literal +
-                                     total_size, fragP);
+                                     fr_opcode - fragP->fr_literal
+                                     + target_offset, fragP);
              break;
 
            case ITYPE_INSN:
              break;
 
            case ITYPE_INSN:
-             size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode);
-             total_size += size;
+             if (first && wide_insn)
+               {
+                 target_offset += xtensa_format_length (isa, fmt);
+                 first = FALSE;
+                 if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+                   target_offset += xg_get_single_size (tinsn->opcode);
+               }
+             else
+               target_offset += xg_get_single_size (tinsn->opcode);
              break;
            }
        }
 
       total_size = 0;
              break;
            }
        }
 
       total_size = 0;
+      first = TRUE;
+      last_is_jump = FALSE;
       for (i = 0; i < istack.ninsn; i++)
        {
       for (i = 0; i < istack.ninsn; i++)
        {
-         TInsn *t_insn = &istack.insn[i];
+         TInsn *tinsn = &istack.insn[i];
          fragS *lit_frag;
          int size;
          segT target_seg;
          fragS *lit_frag;
          int size;
          segT target_seg;
+         bfd_reloc_code_real_type reloc_type;
 
 
-         switch (t_insn->insn_type)
+         switch (tinsn->insn_type)
            {
            case ITYPE_LITERAL:
            {
            case ITYPE_LITERAL:
-             lit_frag = fragP->tc_frag_data.literal_frag;
-             /* already checked */
+             lit_frag = fragP->tc_frag_data.literal_frags[slot];
+             /* Already checked.  */
              assert (lit_frag != NULL);
              assert (lit_sym != NULL);
              assert (lit_frag != NULL);
              assert (lit_sym != NULL);
-             assert (t_insn->ntok == 1);
-             /* add a fixup */
+             assert (tinsn->ntok == 1);
+             /* Add a fixup.  */
              target_seg = S_GET_SEGMENT (lit_sym);
              assert (target_seg);
              target_seg = S_GET_SEGMENT (lit_sym);
              assert (target_seg);
+             reloc_type = map_operator_to_reloc (tinsn->tok[0].X_op);
              fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
              fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
-                                 &t_insn->tok[0], FALSE, BFD_RELOC_32);
+                                 &tinsn->tok[0], FALSE, reloc_type);
              break;
 
            case ITYPE_LABEL:
              break;
 
            case ITYPE_INSN:
              break;
 
            case ITYPE_LABEL:
              break;
 
            case ITYPE_INSN:
-             xg_resolve_labels (t_insn, gen_label);
-             xg_resolve_literals (t_insn, lit_sym);
-             size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode);
-             total_size += size;
-             xg_emit_insn_to_buf (t_insn, immed_instr, fragP,
-                                  immed_instr - fragP->fr_literal, TRUE);
+             xg_resolve_labels (tinsn, gen_label);
+             xg_resolve_literals (tinsn, lit_sym);
+             if (wide_insn && first)
+               {
+                 first = FALSE;
+                 if (opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+                   {
+                     cur_vinsn.slots[slot] = *tinsn;
+                   }
+                 else
+                   {
+                     cur_vinsn.slots[slot].opcode =
+                       xtensa_format_slot_nop_opcode (isa, fmt, slot);
+                     cur_vinsn.slots[slot].ntok = 0;
+                   }
+                 vinsn_to_insnbuf (&cur_vinsn, immed_instr, fragP, TRUE);
+                 xtensa_insnbuf_to_chars (isa, cur_vinsn.insnbuf,
+                                          (unsigned char *) immed_instr, 0);
+                 fragP->tc_frag_data.is_insn = TRUE;
+                 size = xtensa_format_length (isa, fmt);
+                 if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+                   {
+                     xg_emit_insn_to_buf
+                       (tinsn, immed_instr + size, fragP,
+                        immed_instr - fragP->fr_literal + size, TRUE);
+                     size += xg_get_single_size (tinsn->opcode);
+                   }
+               }
+             else
+               {
+                 size = xg_get_single_size (tinsn->opcode);
+                 xg_emit_insn_to_buf (tinsn, immed_instr, fragP,
+                                      immed_instr - fragP->fr_literal, TRUE);
+               }
              immed_instr += size;
              immed_instr += size;
+             total_size += size;
              break;
            }
        }
              break;
            }
        }
@@ -7171,20 +9472,17 @@ convert_frag_immed (segP, fragP, min_steps)
       fragP->fr_fix += diff;
     }
 
       fragP->fr_fix += diff;
     }
 
-  /* Clean it up.  */
-  fragP->fr_var = 0;
-
   /* Check for undefined immediates in LOOP instructions.  */
   /* Check for undefined immediates in LOOP instructions.  */
-  if (is_loop_opcode (orig_t_insn.opcode))
+  if (is_loop)
     {
       symbolS *sym;
     {
       symbolS *sym;
-      sym = orig_t_insn.tok[1].X_add_symbol;
+      sym = orig_tinsn.tok[1].X_add_symbol;
       if (sym != NULL && !S_IS_DEFINED (sym))
        {
          as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
          return;
        }
       if (sym != NULL && !S_IS_DEFINED (sym))
        {
          as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
          return;
        }
-      sym = orig_t_insn.tok[1].X_op_symbol;
+      sym = orig_tinsn.tok[1].X_op_symbol;
       if (sym != NULL && !S_IS_DEFINED (sym))
        {
          as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
       if (sym != NULL && !S_IS_DEFINED (sym))
        {
          as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
@@ -7192,16 +9490,15 @@ convert_frag_immed (segP, fragP, min_steps)
        }
     }
 
        }
     }
 
-  if (expanded && is_loop_opcode (orig_t_insn.opcode))
-    convert_frag_immed_finish_loop (segP, fragP, &orig_t_insn);
+  if (expanded && xtensa_opcode_is_loop (isa, orig_tinsn.opcode) == 1)
+    convert_frag_immed_finish_loop (segP, fragP, &orig_tinsn);
 
 
-  if (expanded && is_direct_call_opcode (orig_t_insn.opcode))
+  if (expanded && is_direct_call_opcode (orig_tinsn.opcode))
     {
       /* Add an expansion note on the expanded instruction.  */
       fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4,
     {
       /* Add an expansion note on the expanded instruction.  */
       fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4,
-                         &orig_t_insn.tok[0], TRUE,
+                         &orig_tinsn.tok[0], TRUE,
                          BFD_RELOC_XTENSA_ASM_EXPAND);
                          BFD_RELOC_XTENSA_ASM_EXPAND);
-
     }
 }
 
     }
 }
 
@@ -7210,57 +9507,47 @@ convert_frag_immed (segP, fragP, min_steps)
    switch to that segment to do this.  */
 
 static fixS *
    switch to that segment to do this.  */
 
 static fixS *
-fix_new_exp_in_seg (new_seg, new_subseg,
-                   frag, where, size, exp, pcrel, r_type)
-     segT new_seg;
-     subsegT new_subseg;
-     fragS *frag;
-     int where;
-     int size;
-     expressionS *exp;
-     int pcrel;
-     bfd_reloc_code_real_type r_type;
+fix_new_exp_in_seg (segT new_seg,
+                   subsegT new_subseg,
+                   fragS *frag,
+                   int where,
+                   int size,
+                   expressionS *exp,
+                   int pcrel,
+                   bfd_reloc_code_real_type r_type)
 {
   fixS *new_fix;
   segT seg = now_seg;
   subsegT subseg = now_subseg;
 {
   fixS *new_fix;
   segT seg = now_seg;
   subsegT subseg = now_subseg;
+
   assert (new_seg != 0);
   subseg_set (new_seg, new_subseg);
 
   assert (new_seg != 0);
   subseg_set (new_seg, new_subseg);
 
-  if (r_type == BFD_RELOC_32
-      && exp->X_add_symbol
-      && symbol_get_tc (exp->X_add_symbol)->plt == 1)
-    {
-      r_type = BFD_RELOC_XTENSA_PLT;
-    }
-
   new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
   subseg_set (seg, subseg);
   return new_fix;
 }
 
 
   new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
   subseg_set (seg, subseg);
   return new_fix;
 }
 
 
-/* Relax a loop instruction so that it can span loop >256 bytes.  */
-/* 
-                  loop    as, .L1 
-          .L0: 
-                  rsr     as, LEND 
-                  wsr     as, LBEG 
-                  addi    as, as, lo8(label-.L1) 
-                  addmi   as, as, mid8(label-.L1) 
-                  wsr     as, LEND 
-                  isync 
-                  rsr     as, LCOUNT 
-                  addi    as, as, 1 
-          .L1: 
-                  <<body>> 
-          label:                                     */
+/* Relax a loop instruction so that it can span loop >256 bytes.
+
+                  loop    as, .L1
+          .L0:
+                  rsr     as, LEND
+                  wsr     as, LBEG
+                  addi    as, as, lo8 (label-.L1)
+                  addmi   as, as, mid8 (label-.L1)
+                  wsr     as, LEND
+                  isync
+                  rsr     as, LCOUNT
+                  addi    as, as, 1
+          .L1:
+                  <<body>>
+          label:
+*/
 
 static void
 
 static void
-convert_frag_immed_finish_loop (segP, fragP, t_insn)
-     segT segP;
-     fragS *fragP;
-     TInsn *t_insn;
+convert_frag_immed_finish_loop (segT segP, fragS *fragP, TInsn *tinsn)
 {
   TInsn loop_insn;
   TInsn addi_insn;
 {
   TInsn loop_insn;
   TInsn addi_insn;
@@ -7272,31 +9559,41 @@ convert_frag_immed_finish_loop (segP, fragP, t_insn)
   addressT loop_offset;
   addressT addi_offset = 9;
   addressT addmi_offset = 12;
   addressT loop_offset;
   addressT addi_offset = 9;
   addressT addmi_offset = 12;
+  fragS *next_fragP;
+  int target_count;
 
   if (!insnbuf)
     insnbuf = xtensa_insnbuf_alloc (isa);
 
   /* Get the loop offset.  */
 
   if (!insnbuf)
     insnbuf = xtensa_insnbuf_alloc (isa);
 
   /* Get the loop offset.  */
-  loop_offset = get_expanded_loop_offset (t_insn->opcode);
-  /* Validate that there really is a LOOP at the loop_offset.  */
-  tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset);
+  loop_offset = get_expanded_loop_offset (tinsn->opcode);
 
 
-  if (!is_loop_opcode (loop_insn.opcode))
-    {
-      as_bad_where (fragP->fr_file, fragP->fr_line,
-                   _("loop relaxation specification does not correspond"));
-      assert (0);
-    }
+  /* Validate that there really is a LOOP at the loop_offset.  Because
+     loops are not bundleable, we can assume that the instruction will be
+     in slot 0.  */
+  tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
+  tinsn_immed_from_frag (&loop_insn, fragP, 0);
+
+  assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
   addi_offset += loop_offset;
   addmi_offset += loop_offset;
 
   addi_offset += loop_offset;
   addmi_offset += loop_offset;
 
-  assert (t_insn->ntok == 2);
-  target = get_expression_value (segP, &t_insn->tok[1]);
-
-  know (symbolP);
-  know (symbolP->sy_frag);
-  know (!(S_GET_SEGMENT (symbolP) == absolute_section)
-       || symbol_get_frag (symbolP) == &zero_address_frag);
+  assert (tinsn->ntok == 2);
+  if (tinsn->tok[1].X_op == O_constant)
+    target = tinsn->tok[1].X_add_number;
+  else if (tinsn->tok[1].X_op == O_symbol)
+    {
+      /* Find the fragment.  */
+      symbolS *sym = tinsn->tok[1].X_add_symbol;
+      assert (S_GET_SEGMENT (sym) == segP
+             || S_GET_SEGMENT (sym) == absolute_section);
+      target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
+    }
+  else
+    {
+      as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
+      target = 0;
+    }
 
   loop_length = target - (fragP->fr_address + fragP->fr_fix);
   loop_length_hi = loop_length & ~0x0ff;
 
   loop_length = target - (fragP->fr_address + fragP->fr_fix);
   loop_length_hi = loop_length & ~0x0ff;
@@ -7307,49 +9604,45 @@ convert_frag_immed_finish_loop (segP, fragP, t_insn)
       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.  */
   if (loop_length_hi > 32512)
     as_bad_where (fragP->fr_file, fragP->fr_line,
                  _("loop too long for LOOP instruction"));
 
      32512.  If the loop is larger than that, then we just fail.  */
   if (loop_length_hi > 32512)
     as_bad_where (fragP->fr_file, fragP->fr_line,
                  _("loop too long for LOOP instruction"));
 
-  tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset);
+  tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset, 0);
   assert (addi_insn.opcode == xtensa_addi_opcode);
 
   assert (addi_insn.opcode == xtensa_addi_opcode);
 
-  tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset);
+  tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
   assert (addmi_insn.opcode == xtensa_addmi_opcode);
 
   set_expr_const (&addi_insn.tok[2], loop_length_lo);
   tinsn_to_insnbuf (&addi_insn, insnbuf);
   assert (addmi_insn.opcode == xtensa_addmi_opcode);
 
   set_expr_const (&addi_insn.tok[2], loop_length_lo);
   tinsn_to_insnbuf (&addi_insn, insnbuf);
-  
+
   fragP->tc_frag_data.is_insn = TRUE;
   fragP->tc_frag_data.is_insn = TRUE;
-  xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addi_offset);
+  xtensa_insnbuf_to_chars
+    (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addi_offset, 0);
 
   set_expr_const (&addmi_insn.tok[2], loop_length_hi);
   tinsn_to_insnbuf (&addmi_insn, insnbuf);
 
   set_expr_const (&addmi_insn.tok[2], loop_length_hi);
   tinsn_to_insnbuf (&addmi_insn, insnbuf);
-  xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addmi_offset);
-}
+  xtensa_insnbuf_to_chars
+    (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addmi_offset, 0);
 
 
+  /* Walk through all of the frags from here to the loop end
+     and mark them as no_transform to keep them from being modified
+     by the linker.  If we ever have a relocation for the
+     addi/addmi of the difference of two symbols we can remove this.  */
 
 
-static offsetT
-get_expression_value (segP, exp)
-     segT segP;
-     expressionS *exp;
-{
-  if (exp->X_op == O_constant)
-    return exp->X_add_number;
-  if (exp->X_op == O_symbol)
+  target_count = 0;
+  for (next_fragP = fragP; next_fragP != NULL;
+       next_fragP = next_fragP->fr_next)
     {
     {
-      /* Find the fragment.  */
-      symbolS *sym = exp->X_add_symbol;
-
-      assert (S_GET_SEGMENT (sym) == segP
-             || S_GET_SEGMENT (sym) == absolute_section);
-
-      return (S_GET_VALUE (sym) + exp->X_add_number);
+      next_fragP->tc_frag_data.is_no_transform = TRUE;
+      if (next_fragP->tc_frag_data.is_loop_target)
+       target_count++;
+      if (target_count == 2)
+       break;
     }
     }
-  as_bad (_("invalid expression evaluation type %d"), exp->X_op);
-  return 0;
 }
 
 \f
 }
 
 \f
@@ -7366,99 +9659,107 @@ typedef struct subseg_map_struct
 
   /* the data */
   unsigned flags;
 
   /* the data */
   unsigned flags;
+  float total_freq;    /* fall-through + branch target frequency */
+  float target_freq;   /* branch target frequency alone */
 
   struct subseg_map_struct *next;
 } subseg_map;
 
 
   struct subseg_map_struct *next;
 } subseg_map;
 
-static subseg_map *sseg_map = NULL;
 
 
+static subseg_map *sseg_map = NULL;
 
 
-static unsigned
-get_last_insn_flags (seg, subseg)
-     segT seg;
-     subsegT subseg;
+static subseg_map *
+get_subseg_info (segT seg, subsegT subseg)
 {
   subseg_map *subseg_e;
 
 {
   subseg_map *subseg_e;
 
-  for (subseg_e = sseg_map; subseg_e != NULL; subseg_e = subseg_e->next)
-    if (seg == subseg_e->seg && subseg == subseg_e->subseg)
-      return subseg_e->flags;
-
-  return 0;
+  for (subseg_e = sseg_map; subseg_e; subseg_e = subseg_e->next)
+    {
+      if (seg == subseg_e->seg && subseg == subseg_e->subseg)
+       break;
+    }
+  return subseg_e;
 }
 
 
 }
 
 
-static void
-set_last_insn_flags (seg, subseg, fl, val)
-     segT seg;
-     subsegT subseg;
-     unsigned fl;
-     bfd_boolean val;
+static subseg_map *
+add_subseg_info (segT seg, subsegT subseg)
 {
 {
-  subseg_map *subseg_e;
+  subseg_map *subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map));
+  memset (subseg_e, 0, sizeof (subseg_map));
+  subseg_e->seg = seg;
+  subseg_e->subseg = subseg;
+  subseg_e->flags = 0;
+  /* Start off considering every branch target very important.  */
+  subseg_e->target_freq = 1.0;
+  subseg_e->total_freq = 1.0;
+  subseg_e->next = sseg_map;
+  sseg_map = subseg_e;
+  return subseg_e;
+}
 
 
-  for (subseg_e = sseg_map; subseg_e; subseg_e = subseg_e->next)
-    if (seg == subseg_e->seg && subseg == subseg_e->subseg)
-      break;
 
 
-  if (!subseg_e)
-    {
-      subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map));
-      memset (subseg_e, 0, sizeof (subseg_map));
-      subseg_e->seg = seg;
-      subseg_e->subseg = subseg;
-      subseg_e->flags = 0;
-      subseg_e->next = sseg_map;
-      sseg_map = subseg_e;
-    }
+static unsigned
+get_last_insn_flags (segT seg, subsegT subseg)
+{
+  subseg_map *subseg_e = get_subseg_info (seg, subseg);
+  if (subseg_e)
+    return subseg_e->flags;
+  return 0;
+}
 
 
+
+static void
+set_last_insn_flags (segT seg,
+                    subsegT subseg,
+                    unsigned fl,
+                    bfd_boolean val)
+{
+  subseg_map *subseg_e = get_subseg_info (seg, subseg);
+  if (! subseg_e)
+    subseg_e = add_subseg_info (seg, subseg);
   if (val)
     subseg_e->flags |= fl;
   else
     subseg_e->flags &= ~fl;
 }
 
   if (val)
     subseg_e->flags |= fl;
   else
     subseg_e->flags &= ~fl;
 }
 
-\f
-/* Segment Lists and emit_state Stuff.  */
 
 
-/* Remove the segment from the global sections list.  */
-
-static void
-xtensa_remove_section (sec)
-     segT sec;
+static float
+get_subseg_total_freq (segT seg, subsegT subseg)
 {
 {
-  /* Handle brain-dead bfd_section_list_remove macro, which
-     expect the address of the prior section's "next" field, not
-     just the address of the section to remove.  */
+  subseg_map *subseg_e = get_subseg_info (seg, subseg);
+  if (subseg_e)
+    return subseg_e->total_freq;
+  return 1.0;
+}
 
 
-  segT *ps_next_ptr = &stdoutput->sections;
-  while (*ps_next_ptr != sec && *ps_next_ptr != NULL) 
-    ps_next_ptr = &(*ps_next_ptr)->next;
-  
-  assert (*ps_next_ptr != NULL);
 
 
-  bfd_section_list_remove (stdoutput, ps_next_ptr);
+static float
+get_subseg_target_freq (segT seg, subsegT subseg)
+{
+  subseg_map *subseg_e = get_subseg_info (seg, subseg);
+  if (subseg_e)
+    return subseg_e->target_freq;
+  return 1.0;
 }
 
 
 static void
 }
 
 
 static void
-xtensa_insert_section (after_sec, sec)
-     segT after_sec;
-     segT sec;
+set_subseg_freq (segT seg, subsegT subseg, float total_f, float target_f)
 {
 {
-  segT *after_sec_next;
-  if (after_sec == NULL)
-    after_sec_next = &stdoutput->sections;
-  else
-    after_sec_next = &after_sec->next;
-
-  bfd_section_list_insert (stdoutput, after_sec_next, sec);
+  subseg_map *subseg_e = get_subseg_info (seg, subseg);
+  if (! subseg_e)
+    subseg_e = add_subseg_info (seg, subseg);
+  subseg_e->total_freq = total_f;
+  subseg_e->target_freq = target_f;
 }
 
 }
 
+\f
+/* Segment Lists and emit_state Stuff.  */
 
 static void
 
 static void
-xtensa_move_seg_list_to_beginning (head)
-     seg_list *head;
+xtensa_move_seg_list_to_beginning (seg_list *head)
 {
   head = head->next;
   while (head)
 {
   head = head->next;
   while (head)
@@ -7467,16 +9768,20 @@ xtensa_move_seg_list_to_beginning (head)
 
       /* Move the literal section to the front of the section list.  */
       assert (literal_section);
 
       /* Move the literal section to the front of the section list.  */
       assert (literal_section);
-      xtensa_remove_section (literal_section);
-      xtensa_insert_section (NULL, literal_section);
-
+      if (literal_section != stdoutput->sections)
+       {
+         bfd_section_list_remove (stdoutput, literal_section);
+         bfd_section_list_prepend (stdoutput, literal_section);
+       }
       head = head->next;
     }
 }
 
 
       head = head->next;
     }
 }
 
 
-void
-xtensa_move_literals ()
+static void mark_literal_frags (seg_list *);
+
+static void
+xtensa_move_literals (void)
 {
   seg_list *segment;
   frchainS *frchain_from, *frchain_to;
 {
   seg_list *segment;
   frchainS *frchain_from, *frchain_to;
@@ -7488,15 +9793,17 @@ xtensa_move_literals ()
   sym_list *lit;
 
   mark_literal_frags (literal_head->next);
   sym_list *lit;
 
   mark_literal_frags (literal_head->next);
-  mark_literal_frags (init_literal_head->next);
-  mark_literal_frags (fini_literal_head->next);
 
   if (use_literal_section)
     return;
 
 
   if (use_literal_section)
     return;
 
-  segment = literal_head->next;
-  while (segment)
+  for (segment = literal_head->next; segment; segment = segment->next)
     {
     {
+      /* Keep the literals for .init and .fini in separate sections.  */
+      if (!strcmp (segment_name (segment->seg), INIT_SECTION_NAME)
+         || !strcmp (segment_name (segment->seg), FINI_SECTION_NAME))
+       continue;
+
       frchain_from = seg_info (segment->seg)->frchainP;
       search_frag = frchain_from->frch_root;
       literal_pool = NULL;
       frchain_from = seg_info (segment->seg)->frchainP;
       search_frag = frchain_from->frch_root;
       literal_pool = NULL;
@@ -7519,35 +9826,32 @@ xtensa_move_literals ()
         prevents us from making a segment with an frchain without any
         frags in it.  */
       frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
         prevents us from making a segment with an frchain without any
         frags in it.  */
       frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+      xtensa_set_frag_assembly_state (frag_now);
       last_frag = frag_now;
       frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
       last_frag = frag_now;
       frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+      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.  */
          if (search_frag->tc_frag_data.literal_frag)
            {
              literal_pool = search_frag->tc_frag_data.literal_frag;
              assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
             to the appropriate place.  */
          if (search_frag->tc_frag_data.literal_frag)
            {
              literal_pool = search_frag->tc_frag_data.literal_frag;
              assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
-             /* Note that we set this fr_var to be a fix 
-                chain when we created the literal pool location
-                as RELAX_LITERAL_POOL_BEGIN.  */
-             frchain_to = (frchainS *) literal_pool->fr_var;
+             frchain_to = literal_pool->tc_frag_data.lit_frchain;
+             assert (frchain_to);
            }
            }
-         insert_after = literal_pool;
-         
-         while (insert_after->fr_next->fr_subtype != RELAX_LITERAL_POOL_END)
-           insert_after = insert_after->fr_next;
+         insert_after = literal_pool->tc_frag_data.literal_frag;
+         dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
 
 
-         dest_seg = (segT) insert_after->fr_next->fr_var;
-         
          *frag_splice = next_frag;
          search_frag->fr_next = insert_after->fr_next;
          insert_after->fr_next = search_frag;
          search_frag->tc_frag_data.lit_seg = dest_seg;
          *frag_splice = next_frag;
          search_frag->fr_next = insert_after->fr_next;
          insert_after->fr_next = search_frag;
          search_frag->tc_frag_data.lit_seg = dest_seg;
+         literal_pool->tc_frag_data.literal_frag = search_frag;
 
          /* Now move any fixups associated with this frag to the
             right section.  */
 
          /* Now move any fixups associated with this frag to the
             right section.  */
@@ -7580,7 +9884,6 @@ xtensa_move_literals ()
        }
       frchain_from->fix_tail = NULL;
       xtensa_restore_emit_state (&state);
        }
       frchain_from->fix_tail = NULL;
       xtensa_restore_emit_state (&state);
-      segment = segment->next;
     }
 
   /* Now fix up the SEGMENT value for all the literal symbols.  */
     }
 
   /* Now fix up the SEGMENT value for all the literal symbols.  */
@@ -7588,7 +9891,8 @@ xtensa_move_literals ()
     {
       symbolS *lit_sym = lit->sym;
       segT dest_seg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
     {
       symbolS *lit_sym = lit->sym;
       segT dest_seg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
-      S_SET_SEGMENT (lit_sym, dest_seg);
+      if (dest_seg)
+       S_SET_SEGMENT (lit_sym, dest_seg);
     }
 }
 
     }
 }
 
@@ -7598,8 +9902,7 @@ xtensa_move_literals ()
    and frag_variant to get called in all situations.  */
 
 static void
    and frag_variant to get called in all situations.  */
 
 static void
-mark_literal_frags (segment)
-     seg_list *segment;
+mark_literal_frags (seg_list *segment)
 {
   frchainS *frchain_from;
   fragS *search_frag;
 {
   frchainS *frchain_from;
   fragS *search_frag;
@@ -7608,7 +9911,7 @@ mark_literal_frags (segment)
     {
       frchain_from = seg_info (segment->seg)->frchainP;
       search_frag = frchain_from->frch_root;
     {
       frchain_from = seg_info (segment->seg)->frchainP;
       search_frag = frchain_from->frch_root;
-      while (search_frag) 
+      while (search_frag)
        {
          search_frag->tc_frag_data.is_literal = TRUE;
          search_frag = search_frag->fr_next;
        {
          search_frag->tc_frag_data.is_literal = TRUE;
          search_frag = search_frag->fr_next;
@@ -7619,9 +9922,7 @@ mark_literal_frags (segment)
 
 
 static void
 
 
 static void
-xtensa_reorder_seg_list (head, after)
-     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
      after "after" in the gnu segment list.  */
 {
   /* Move all of the sections in the section list to come
      after "after" in the gnu segment list.  */
@@ -7635,8 +9936,8 @@ xtensa_reorder_seg_list (head, after)
       assert (literal_section);
       if (literal_section != after)
        {
       assert (literal_section);
       if (literal_section != after)
        {
-         xtensa_remove_section (literal_section);
-         xtensa_insert_section (after, literal_section);
+         bfd_section_list_remove (stdoutput, literal_section);
+         bfd_section_list_insert_after (stdoutput, after, literal_section);
        }
 
       head = head->next;
        }
 
       head = head->next;
@@ -7646,23 +9947,23 @@ xtensa_reorder_seg_list (head, after)
 
 /* Push all the literal segments to the end of the gnu list.  */
 
 
 /* Push all the literal segments to the end of the gnu list.  */
 
-void
-xtensa_reorder_segments ()
+static void
+xtensa_reorder_segments (void)
 {
   segT sec;
 {
   segT sec;
-  segT last_sec;
+  segT last_sec = 0;
   int old_count = 0;
   int new_count = 0;
 
   for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
   int old_count = 0;
   int new_count = 0;
 
   for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
-    old_count++;
+    {
+      last_sec = sec;
+      old_count++;
+    }
 
   /* Now that we have the last section, push all the literal
      sections to the end.  */
 
   /* Now that we have the last section, push all the literal
      sections to the end.  */
-  last_sec = get_last_sec ();
   xtensa_reorder_seg_list (literal_head, last_sec);
   xtensa_reorder_seg_list (literal_head, last_sec);
-  xtensa_reorder_seg_list (init_literal_head, last_sec);
-  xtensa_reorder_seg_list (fini_literal_head, last_sec);
 
   /* Now perform the final error check.  */
   for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
 
   /* Now perform the final error check.  */
   for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
@@ -7671,93 +9972,70 @@ xtensa_reorder_segments ()
 }
 
 
 }
 
 
-segT
-get_last_sec ()
-{
-  segT last_sec = stdoutput->sections;
-  while (last_sec->next != NULL)
-    last_sec = last_sec->next;
-
-  return last_sec;
-}
-
-
 /* Change the emit state (seg, subseg, and frag related stuff) to the
    correct location.  Return a emit_state which can be passed to
    xtensa_restore_emit_state to return to current fragment.  */
 
 /* Change the emit state (seg, subseg, and frag related stuff) to the
    correct location.  Return a emit_state which can be passed to
    xtensa_restore_emit_state to return to current fragment.  */
 
-void
-xtensa_switch_to_literal_fragment (result)
-     emit_state *result;
+static void
+xtensa_switch_to_literal_fragment (emit_state *result)
 {
 {
-  /* 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
-     switch_to_literal_fragment.  But literal sections don't have
-     literal pools, so their location is always null, so we would
-     recurse forever.  This is kind of hacky, but it works.  */
+  if (directive_state[directive_absolute_literals])
+    {
+      segT lit4_seg = cache_literal_section (TRUE);
+      xtensa_switch_section_emit_state (result, lit4_seg, 0);
+    }
+  else
+    xtensa_switch_to_non_abs_literal_fragment (result);
+
+  /* Do a 4-byte align here.  */
+  frag_align (2, 0, 0);
+  record_alignment (now_seg, 2);
+}
 
 
+
+static void
+xtensa_switch_to_non_abs_literal_fragment (emit_state *result)
+{
   static bfd_boolean recursive = FALSE;
   fragS *pool_location = get_literal_pool_location (now_seg);
   static bfd_boolean recursive = FALSE;
   fragS *pool_location = get_literal_pool_location (now_seg);
-  bfd_boolean is_init = 
+  segT lit_seg;
+  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 
-      && !use_literal_section 
+  if (pool_location == NULL
+      && !use_literal_section
       && !recursive
       && !is_init && ! is_fini)
     {
       && !recursive
       && !is_init && ! is_fini)
     {
-      as_warn (_("inlining literal pool; "
-                "specify location 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
+        the literal pool that points to it.  But to do that, we want to
+        switch_to_literal_fragment.  But literal sections don't have
+        literal pools, so their location is always null, so we would
+        recurse forever.  This is kind of hacky, but it works.  */
+
       recursive = TRUE;
       xtensa_mark_literal_pool_location ();
       recursive = FALSE;
     }
 
       recursive = TRUE;
       xtensa_mark_literal_pool_location ();
       recursive = FALSE;
     }
 
-  /* Special case: If we are in the ".fini" or ".init" section, then
-     we will ALWAYS be generating to the ".fini.literal" and
-     ".init.literal" sections.  */
-
-  if (is_init)
-    {
-      cache_literal_section (init_literal_head,
-                            default_lit_sections.init_lit_seg_name,
-                            &default_lit_sections.init_lit_seg);
-      xtensa_switch_section_emit_state (result,
-                                       default_lit_sections.init_lit_seg, 0);
-    }
-  else if (is_fini)
-    {
-      cache_literal_section (fini_literal_head,
-                            default_lit_sections.fini_lit_seg_name,
-                            &default_lit_sections.fini_lit_seg);
-      xtensa_switch_section_emit_state (result,
-                                       default_lit_sections.fini_lit_seg, 0);
-    }
-  else 
-    {
-      cache_literal_section (literal_head,
-                            default_lit_sections.lit_seg_name,
-                            &default_lit_sections.lit_seg);
-      xtensa_switch_section_emit_state (result,
-                                       default_lit_sections.lit_seg, 0);
-    }
+  lit_seg = cache_literal_section (FALSE);
+  xtensa_switch_section_emit_state (result, lit_seg, 0);
 
 
-  if (!use_literal_section &&
-      !is_init && !is_fini &&
-      get_literal_pool_location (now_seg) != pool_location)
+  if (!use_literal_section
+      && !is_init && !is_fini
+      && get_literal_pool_location (now_seg) != pool_location)
     {
       /* Close whatever frag is there.  */
       frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
     {
       /* Close whatever frag is there.  */
       frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+      xtensa_set_frag_assembly_state (frag_now);
       frag_now->tc_frag_data.literal_frag = pool_location;
       frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
       frag_now->tc_frag_data.literal_frag = pool_location;
       frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+      xtensa_set_frag_assembly_state (frag_now);
     }
     }
-
-  /* Do a 4 byte align here.  */
-  frag_align (2, 0, 0);
 }
 
 
 }
 
 
@@ -7765,182 +10043,383 @@ xtensa_switch_to_literal_fragment (result)
    This is a helper function for xtensa_switch_to_literal_fragment.
    This is similar to a .section new_now_seg subseg. */
 
    This is a helper function for xtensa_switch_to_literal_fragment.
    This is similar to a .section new_now_seg subseg. */
 
-void
-xtensa_switch_section_emit_state (state, new_now_seg, new_now_subseg)
-     emit_state *state;
-     segT new_now_seg;
-     subsegT new_now_subseg;
+static void
+xtensa_switch_section_emit_state (emit_state *state,
+                                 segT new_now_seg,
+                                 subsegT new_now_subseg)
 {
   state->name = now_seg->name;
   state->now_seg = now_seg;
   state->now_subseg = now_subseg;
   state->generating_literals = generating_literals;
   generating_literals++;
 {
   state->name = now_seg->name;
   state->now_seg = now_seg;
   state->now_subseg = now_subseg;
   state->generating_literals = generating_literals;
   generating_literals++;
-  subseg_new (segment_name (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.  */
 
-void
-xtensa_restore_emit_state (state)
-     emit_state *state;
+static void
+xtensa_restore_emit_state (emit_state *state)
 {
   generating_literals = state->generating_literals;
 {
   generating_literals = state->generating_literals;
-  subseg_new (state->name, state->now_subseg);
+  subseg_set (state->now_seg, state->now_subseg);
 }
 
 
 }
 
 
-/* Get a segment of a given name.  If the segment is already
-   present, return it; otherwise, create a new one.  */
+/* Predicate function used to look up a section in a particular group.  */
 
 
-static void
-cache_literal_section (head, name, seg)
-     seg_list *head;
-     const char *name;
-     segT *seg;
+static bfd_boolean
+match_section_group (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 {
 {
-  segT current_section = now_seg;
-  int current_subsec = now_subseg;
-
-  if (*seg != 0)
-    return;
-  *seg = retrieve_literal_seg (head, name);
-  subseg_set (current_section, current_subsec);
+  const char *gname = inf;
+  const char *group_name = elf_group_name (sec);
+  
+  return (group_name == gname
+         || (group_name != NULL
+             && gname != NULL
+             && strcmp (group_name, gname) == 0));
 }
 
 
 }
 
 
-/* Get a segment of a given name.  If the segment is already
-   present, return it; otherwise, create a new one.  */
+/* Get the literal section to be used for the current text section.
+   The result may be cached in the default_lit_sections structure.  */
 
 static segT
 
 static segT
-retrieve_literal_seg (head, name)
-     seg_list *head;
-     const char *name;
+cache_literal_section (bfd_boolean use_abs_literals)
 {
 {
-  segT ret = 0;
+  const char *text_name, *group_name = 0;
+  char *base_name, *name, *suffix;
+  segT *pcached;
+  segT seg, current_section;
+  int current_subsec;
+  bfd_boolean linkonce = FALSE;
 
 
-  assert (head);
+  /* Save the current section/subsection.  */
+  current_section = now_seg;
+  current_subsec = now_subseg;
 
 
-  ret = seg_present (name);
-  if (!ret)
+  /* Clear the cached values if they are no longer valid.  */
+  if (now_seg != default_lit_sections.current_text_seg)
     {
     {
-      ret = subseg_new (name, (subsegT) 0);
-      add_seg_list (head, ret);
-      bfd_set_section_flags (stdoutput, ret, SEC_HAS_CONTENTS |
-                            SEC_READONLY | SEC_ALLOC | SEC_LOAD | SEC_CODE);
-      bfd_set_section_alignment (stdoutput, ret, 2);
+      default_lit_sections.current_text_seg = now_seg;
+      default_lit_sections.lit_seg = NULL;
+      default_lit_sections.lit4_seg = NULL;
     }
 
     }
 
-  return ret;
-}
-
+  /* Check if the literal section is already cached.  */
+  if (use_abs_literals)
+    pcached = &default_lit_sections.lit4_seg;
+  else
+    pcached = &default_lit_sections.lit_seg;
 
 
-/* Return a segment of a given name if it is present.  */
+  if (*pcached)
+    return *pcached;
+  
+  text_name = default_lit_sections.lit_prefix;
+  if (! text_name || ! *text_name)
+    {
+      text_name = segment_name (current_section);
+      group_name = elf_group_name (current_section);
+      linkonce = (current_section->flags & SEC_LINK_ONCE) != 0;
+    }
 
 
-static segT
-seg_present (name)
-     const char *name;
-{
-  segT seg;
-  seg = stdoutput->sections;
+  base_name = use_abs_literals ? ".lit4" : ".literal";
+  if (group_name)
+    {
+      name = xmalloc (strlen (base_name) + strlen (group_name) + 2);
+      sprintf (name, "%s.%s", base_name, group_name);
+    }
+  else if (strncmp (text_name, ".gnu.linkonce.", linkonce_len) == 0)
+    {
+      suffix = strchr (text_name + linkonce_len, '.');
 
 
-  while (seg)
+      name = xmalloc (linkonce_len + strlen (base_name) + 1
+                     + (suffix ? strlen (suffix) : 0));
+      strcpy (name, ".gnu.linkonce");
+      strcat (name, base_name);
+      if (suffix)
+       strcat (name, suffix);
+      linkonce = TRUE;
+    }
+  else
     {
     {
-      if (!strcmp (segment_name (seg), name))
-       return seg;
-      seg = seg->next;
+      /* If the section name ends with ".text", then replace that suffix
+        instead of appending an additional suffix.  */
+      size_t len = strlen (text_name);
+      if (len >= 5 && strcmp (text_name + len - 5, ".text") == 0)
+       len -= 5;
+
+      name = xmalloc (len + strlen (base_name) + 1);
+      strcpy (name, text_name);
+      strcpy (name + len, base_name);
     }
 
     }
 
-  return 0;
-}
+  /* Canonicalize section names to allow renaming literal sections.
+     The group name, if any, came from the current text section and
+     has already been canonicalized.  */
+  name = tc_canonicalize_symbol_name (name);
 
 
+  seg = bfd_get_section_by_name_if (stdoutput, name, match_section_group,
+                                   (void *) group_name);
+  if (! seg)
+    {
+      flagword flags;
 
 
-/* Add a segment to a segment list.  */
+      seg = subseg_force_new (name, 0);
 
 
-static void
-add_seg_list (head, seg)
-     seg_list *head;
-     segT seg;
-{
-  seg_list *n;
-  n = (seg_list *) xmalloc (sizeof (seg_list));
-  assert (n);
+      if (! use_abs_literals)
+       {
+         /* Add the newly created literal segment to the list.  */
+         seg_list *n = (seg_list *) xmalloc (sizeof (seg_list));
+         n->seg = seg;
+         n->next = literal_head->next;
+         literal_head->next = n;
+       }
+
+      flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_ALLOC | SEC_LOAD
+              | (linkonce ? (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD) : 0)
+              | (use_abs_literals ? SEC_DATA : SEC_CODE));
+
+      elf_group_name (seg) = group_name;
+
+      bfd_set_section_flags (stdoutput, seg, flags);
+      bfd_set_section_alignment (stdoutput, seg, 2);
+    }
 
 
-  n->seg = seg;
-  n->next = head->next;
-  head->next = n;
+  *pcached = seg;
+  subseg_set (current_section, current_subsec);
+  return seg;
 }
 
 \f
 }
 
 \f
-/* Set up Property Tables after Relaxation.  */
+/* Property Tables Stuff.  */
 
 #define XTENSA_INSN_SEC_NAME ".xt.insn"
 #define XTENSA_LIT_SEC_NAME ".xt.lit"
 
 #define XTENSA_INSN_SEC_NAME ".xt.insn"
 #define XTENSA_LIT_SEC_NAME ".xt.lit"
+#define XTENSA_PROP_SEC_NAME ".xt.prop"
+
+typedef bfd_boolean (*frag_predicate) (const fragS *);
+typedef void (*frag_flags_fn) (const fragS *, frag_flags *);
+
+static bfd_boolean get_frag_is_literal (const fragS *);
+static void xtensa_create_property_segments
+  (frag_predicate, frag_predicate, const char *, xt_section_type);
+static void xtensa_create_xproperty_segments
+  (frag_flags_fn, const char *, xt_section_type);
+static segment_info_type *retrieve_segment_info (segT);
+static bfd_boolean section_has_property (segT, frag_predicate);
+static bfd_boolean section_has_xproperty (segT, frag_flags_fn);
+static void add_xt_block_frags
+  (segT, segT, xtensa_block_info **, frag_predicate, frag_predicate);
+static bfd_boolean xtensa_frag_flags_is_empty (const frag_flags *);
+static void xtensa_frag_flags_init (frag_flags *);
+static void get_frag_property_flags (const fragS *, frag_flags *);
+static bfd_vma frag_flags_to_number (const frag_flags *);
+static void add_xt_prop_frags
+  (segT, segT, xtensa_block_info **, frag_flags_fn);
+
+/* Set up property tables after relaxation.  */
+
+void
+xtensa_post_relax_hook (void)
+{
+  xtensa_move_seg_list_to_beginning (literal_head);
+
+  xtensa_find_unmarked_state_frags ();
+  xtensa_mark_frags_for_org ();
+
+  xtensa_create_property_segments (get_frag_is_literal,
+                                  NULL,
+                                  XTENSA_LIT_SEC_NAME,
+                                  xt_literal_sec);
+  xtensa_create_xproperty_segments (get_frag_property_flags,
+                                   XTENSA_PROP_SEC_NAME,
+                                   xt_prop_sec);
+
+  if (warn_unaligned_branch_targets)
+    bfd_map_over_sections (stdoutput, xtensa_find_unaligned_branch_targets, 0);
+  bfd_map_over_sections (stdoutput, xtensa_find_unaligned_loops, 0);
+}
+
+
+/* This function is only meaningful after xtensa_move_literals.  */
+
+static bfd_boolean
+get_frag_is_literal (const fragS *fragP)
+{
+  assert (fragP != NULL);
+  return fragP->tc_frag_data.is_literal;
+}
+
+
+static void
+xtensa_create_property_segments (frag_predicate property_function,
+                                frag_predicate end_property_function,
+                                const char *section_name_base,
+                                xt_section_type sec_type)
+{
+  segT *seclist;
+
+  /* Walk over all of the current segments.
+     Walk over each fragment
+     For each non-empty fragment,
+     Build a property record (append where possible).  */
+
+  for (seclist = &stdoutput->sections;
+       seclist && *seclist;
+       seclist = &(*seclist)->next)
+    {
+      segT sec = *seclist;
+      flagword flags;
+
+      flags = bfd_get_section_flags (stdoutput, sec);
+      if (flags & SEC_DEBUGGING)
+       continue;
+      if (!(flags & SEC_ALLOC))
+       continue;
+
+      if (section_has_property (sec, property_function))
+       {
+         segT insn_sec = 
+           xtensa_get_property_section (sec, section_name_base);
+         segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
+         xtensa_block_info **xt_blocks =
+           &xt_seg_info->tc_segment_info_data.blocks[sec_type];
+         /* Walk over all of the frchains here and add new sections.  */
+         add_xt_block_frags (sec, insn_sec, xt_blocks, property_function,
+                             end_property_function);
+       }
+    }
+
+  /* Now we fill them out....  */
+
+  for (seclist = &stdoutput->sections;
+       seclist && *seclist;
+       seclist = &(*seclist)->next)
+    {
+      segment_info_type *seginfo;
+      xtensa_block_info *block;
+      segT sec = *seclist;
+
+      seginfo = seg_info (sec);
+      block = seginfo->tc_segment_info_data.blocks[sec_type];
+
+      if (block)
+       {
+         xtensa_block_info *cur_block;
+         /* This is a section with some data.  */
+         int num_recs = 0;
+         bfd_size_type rec_size;
+
+         for (cur_block = block; cur_block; cur_block = cur_block->next)
+           num_recs++;
+
+         rec_size = num_recs * 8;
+         bfd_set_section_size (stdoutput, sec, rec_size);
+
+         /* In order to make this work with the assembler, we have to
+            build some frags and then build the "fixups" for it.  It
+            would be easier to just set the contents then set the
+            arlents.  */
+
+         if (num_recs)
+           {
+             /* Allocate a fragment and leak it.  */
+             fragS *fragP;
+             bfd_size_type frag_size;
+             fixS *fixes;
+             frchainS *frchainP;
+             int i;
+             char *frag_data;
+
+             frag_size = sizeof (fragS) + rec_size;
+             fragP = (fragS *) xmalloc (frag_size);
 
 
-void
-xtensa_post_relax_hook ()
-{
-  xtensa_move_seg_list_to_beginning (literal_head);
-  xtensa_move_seg_list_to_beginning (init_literal_head);
-  xtensa_move_seg_list_to_beginning (fini_literal_head);
+             memset (fragP, 0, frag_size);
+             fragP->fr_address = 0;
+             fragP->fr_next = NULL;
+             fragP->fr_fix = rec_size;
+             fragP->fr_var = 0;
+             fragP->fr_type = rs_fill;
+             /* The rest are zeros.  */
 
 
-  xtensa_create_property_segments (get_frag_is_insn,
-                                  XTENSA_INSN_SEC_NAME,
-                                  xt_insn_sec);
-  xtensa_create_property_segments (get_frag_is_literal,
-                                  XTENSA_LIT_SEC_NAME,
-                                  xt_literal_sec);
-}
+             frchainP = seginfo->frchainP;
+             frchainP->frch_root = fragP;
+             frchainP->frch_last = fragP;
 
 
+             fixes = (fixS *) xmalloc (sizeof (fixS) * num_recs);
+             memset (fixes, 0, sizeof (fixS) * num_recs);
 
 
-static bfd_boolean
-get_frag_is_literal (fragP)
-     const fragS *fragP;
-{
-  assert (fragP != NULL);
-  return (fragP->tc_frag_data.is_literal);
-}
+             seginfo->fix_root = fixes;
+             seginfo->fix_tail = &fixes[num_recs - 1];
+             cur_block = block;
+             frag_data = &fragP->fr_literal[0];
+             for (i = 0; i < num_recs; i++)
+               {
+                 fixS *fix = &fixes[i];
+                 assert (cur_block);
 
 
-static bfd_boolean
-get_frag_is_insn (fragP)
-     const fragS *fragP;
-{
-  assert (fragP != NULL);
-  return (fragP->tc_frag_data.is_insn);
+                 /* Write the fixup.  */
+                 if (i != num_recs - 1)
+                   fix->fx_next = &fixes[i + 1];
+                 else
+                   fix->fx_next = NULL;
+                 fix->fx_size = 4;
+                 fix->fx_done = 0;
+                 fix->fx_frag = fragP;
+                 fix->fx_where = i * 8;
+                 fix->fx_addsy = section_symbol (cur_block->sec);
+                 fix->fx_offset = cur_block->offset;
+                 fix->fx_r_type = BFD_RELOC_32;
+                 fix->fx_file = "Internal Assembly";
+                 fix->fx_line = 0;
+
+                 /* Write the length.  */
+                 md_number_to_chars (&frag_data[4 + 8 * i],
+                                     cur_block->size, 4);
+                 cur_block = cur_block->next;
+               }
+           }
+       }
+    }
 }
 
 
 static void
 }
 
 
 static void
-xtensa_create_property_segments (property_function, section_name_base, 
-                                sec_type)
-     frag_predicate property_function;
-     const char * section_name_base;
-     xt_section_type sec_type;
+xtensa_create_xproperty_segments (frag_flags_fn flag_fn,
+                                 const char *section_name_base,
+                                 xt_section_type sec_type)
 {
   segT *seclist;
 
   /* Walk over all of the current segments.
 {
   segT *seclist;
 
   /* Walk over all of the current segments.
-     Walk over each fragment
-      For each fragment that has instructions
-      Build an instruction record (append where possible).  */
+     Walk over each fragment.
+     For each fragment that has instructions,
+     build an instruction record (append where possible).  */
 
   for (seclist = &stdoutput->sections;
        seclist && *seclist;
        seclist = &(*seclist)->next)
     {
       segT sec = *seclist;
 
   for (seclist = &stdoutput->sections;
        seclist && *seclist;
        seclist = &(*seclist)->next)
     {
       segT sec = *seclist;
-      if (section_has_property (sec, property_function))
+      flagword flags;
+
+      flags = bfd_get_section_flags (stdoutput, sec);
+      if ((flags & SEC_DEBUGGING)
+         || !(flags & SEC_ALLOC)
+         || (flags & SEC_MERGE))
+       continue;
+
+      if (section_has_xproperty (sec, flag_fn))
        {
        {
-         char *property_section_name =
-           xtensa_get_property_section_name (sec, section_name_base);
-         segT insn_sec = retrieve_xtensa_section (property_section_name);
+         segT insn_sec =
+           xtensa_get_property_section (sec, section_name_base);
          segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
          segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
-         xtensa_block_info **xt_blocks = 
+         xtensa_block_info **xt_blocks =
            &xt_seg_info->tc_segment_info_data.blocks[sec_type];
          /* Walk over all of the frchains here and add new sections.  */
            &xt_seg_info->tc_segment_info_data.blocks[sec_type];
          /* Walk over all of the frchains here and add new sections.  */
-         add_xt_block_frags (sec, insn_sec, xt_blocks, property_function);
+         add_xt_prop_frags (sec, insn_sec, xt_blocks, flag_fn);
        }
     }
 
        }
     }
 
@@ -7953,6 +10432,7 @@ xtensa_create_property_segments (property_function, section_name_base,
       segment_info_type *seginfo;
       xtensa_block_info *block;
       segT sec = *seclist;
       segment_info_type *seginfo;
       xtensa_block_info *block;
       segT sec = *seclist;
+
       seginfo = seg_info (sec);
       block = seginfo->tc_segment_info_data.blocks[sec_type];
 
       seginfo = seg_info (sec);
       block = seginfo->tc_segment_info_data.blocks[sec_type];
 
@@ -7960,28 +10440,29 @@ xtensa_create_property_segments (property_function, section_name_base,
        {
          xtensa_block_info *cur_block;
          /* This is a section with some data.  */
        {
          xtensa_block_info *cur_block;
          /* This is a section with some data.  */
-         size_t num_recs = 0;
-         size_t rec_size;
+         int num_recs = 0;
+         bfd_size_type rec_size;
 
          for (cur_block = block; cur_block; cur_block = cur_block->next)
            num_recs++;
 
 
          for (cur_block = block; cur_block; cur_block = cur_block->next)
            num_recs++;
 
-         rec_size = num_recs * 8;
+         rec_size = num_recs * (8 + 4);
          bfd_set_section_size (stdoutput, sec, rec_size);
 
          bfd_set_section_size (stdoutput, sec, rec_size);
 
-         /* In order to make this work with the assembler, we have to
-            build some frags and then build the "fixups" for it.  It
-            would be easier to just set the contents then set the
-            arlents.  */
+         /* elf_section_data (sec)->this_hdr.sh_entsize = 12; */
+
+         /* In order to make this work with the assembler, we have to build
+            some frags then build the "fixups" for it.  It would be easier to
+            just set the contents then set the arlents.  */
 
          if (num_recs)
            {
 
          if (num_recs)
            {
-             /* Allocate a fragment and leak it.  */
+             /* Allocate a fragment and (unfortunately) leak it.  */
              fragS *fragP;
              fragS *fragP;
-             size_t frag_size;
+             bfd_size_type frag_size;
              fixS *fixes;
              frchainS *frchainP;
              fixS *fixes;
              frchainS *frchainP;
-             size_t i;
+             int i;
              char *frag_data;
 
              frag_size = sizeof (fragS) + rec_size;
              char *frag_data;
 
              frag_size = sizeof (fragS) + rec_size;
@@ -7993,7 +10474,7 @@ xtensa_create_property_segments (property_function, section_name_base,
              fragP->fr_fix = rec_size;
              fragP->fr_var = 0;
              fragP->fr_type = rs_fill;
              fragP->fr_fix = rec_size;
              fragP->fr_var = 0;
              fragP->fr_type = rs_fill;
-             /* the rest are zeros */
+             /* The rest are zeros.  */
 
              frchainP = seginfo->frchainP;
              frchainP->frch_root = fragP;
 
              frchainP = seginfo->frchainP;
              frchainP->frch_root = fragP;
@@ -8019,7 +10500,7 @@ xtensa_create_property_segments (property_function, section_name_base,
                  fix->fx_size = 4;
                  fix->fx_done = 0;
                  fix->fx_frag = fragP;
                  fix->fx_size = 4;
                  fix->fx_done = 0;
                  fix->fx_frag = fragP;
-                 fix->fx_where = i * 8;
+                 fix->fx_where = i * (8 + 4);
                  fix->fx_addsy = section_symbol (cur_block->sec);
                  fix->fx_offset = cur_block->offset;
                  fix->fx_r_type = BFD_RELOC_32;
                  fix->fx_addsy = section_symbol (cur_block->sec);
                  fix->fx_offset = cur_block->offset;
                  fix->fx_r_type = BFD_RELOC_32;
@@ -8027,8 +10508,11 @@ xtensa_create_property_segments (property_function, section_name_base,
                  fix->fx_line = 0;
 
                  /* Write the length.  */
                  fix->fx_line = 0;
 
                  /* Write the length.  */
-                 md_number_to_chars (&frag_data[4 + 8 * i],
+                 md_number_to_chars (&frag_data[4 + (8+4) * i],
                                      cur_block->size, 4);
                                      cur_block->size, 4);
+                 md_number_to_chars (&frag_data[8 + (8+4) * i],
+                                     frag_flags_to_number (&cur_block->flags),
+                                     4);
                  cur_block = cur_block->next;
                }
            }
                  cur_block = cur_block->next;
                }
            }
@@ -8037,9 +10521,8 @@ xtensa_create_property_segments (property_function, section_name_base,
 }
 
 
 }
 
 
-segment_info_type *
-retrieve_segment_info (seg)
-     segT seg;
+static segment_info_type *
+retrieve_segment_info (segT seg)
 {
   segment_info_type *seginfo;
   seginfo = (segment_info_type *) bfd_get_section_userdata (stdoutput, seg);
 {
   segment_info_type *seginfo;
   seginfo = (segment_info_type *) bfd_get_section_userdata (stdoutput, seg);
@@ -8048,30 +10531,18 @@ retrieve_segment_info (seg)
       frchainS *frchainP;
 
       seginfo = (segment_info_type *) xmalloc (sizeof (*seginfo));
       frchainS *frchainP;
 
       seginfo = (segment_info_type *) xmalloc (sizeof (*seginfo));
-      memset ((PTR) seginfo, 0, sizeof (*seginfo));
+      memset ((void *) seginfo, 0, sizeof (*seginfo));
       seginfo->fix_root = NULL;
       seginfo->fix_tail = NULL;
       seginfo->bfd_section = seg;
       seginfo->sym = 0;
       /* We will not be dealing with these, only our special ones.  */
       seginfo->fix_root = NULL;
       seginfo->fix_tail = NULL;
       seginfo->bfd_section = seg;
       seginfo->sym = 0;
       /* We will not be dealing with these, only our special ones.  */
-#if 0
-      if (seg == bfd_abs_section_ptr)
-       abs_seg_info = seginfo;
-      else if (seg == bfd_und_section_ptr)
-       und_seg_info = seginfo;
-      else
-#endif
-       bfd_set_section_userdata (stdoutput, seg, (PTR) seginfo);
-#if 0
-      seg_fix_rootP = &segment_info[seg].fix_root;
-      seg_fix_tailP = &segment_info[seg].fix_tail;
-#endif
+      bfd_set_section_userdata (stdoutput, seg, (void *) seginfo);
 
       frchainP = (frchainS *) xmalloc (sizeof (frchainS));
       frchainP->frch_root = NULL;
       frchainP->frch_last = NULL;
       frchainP->frch_next = NULL;
 
       frchainP = (frchainS *) xmalloc (sizeof (frchainS));
       frchainP->frch_root = NULL;
       frchainP->frch_last = NULL;
       frchainP->frch_next = NULL;
-      frchainP->frch_seg = seg;
       frchainP->frch_subseg = 0;
       frchainP->fix_root = NULL;
       frchainP->fix_tail = NULL;
       frchainP->frch_subseg = 0;
       frchainP->fix_root = NULL;
       frchainP->fix_tail = NULL;
@@ -8087,34 +10558,27 @@ retrieve_segment_info (seg)
 }
 
 
 }
 
 
-segT
-retrieve_xtensa_section (sec_name)
-     char *sec_name;
+static bfd_boolean
+section_has_property (segT sec, frag_predicate property_function)
 {
 {
-  bfd *abfd = stdoutput;
-  flagword flags, out_flags, link_once_flags;
-  segT s;
-
-  flags = bfd_get_section_flags (abfd, now_seg);
-  link_once_flags = (flags & SEC_LINK_ONCE);
-  if (link_once_flags)
-    link_once_flags |= (flags & SEC_LINK_DUPLICATES);
-  out_flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY | link_once_flags);
-
-  s = bfd_make_section_old_way (abfd, sec_name);
-  if (s == NULL)
-    as_bad (_("could not create section %s"), sec_name);
-  if (!bfd_set_section_flags (abfd, s, out_flags))
-    as_bad (_("invalid flag combination on section %s"), sec_name);
+  segment_info_type *seginfo = seg_info (sec);
+  fragS *fragP;
 
 
-  return s;
+  if (seginfo && seginfo->frchainP)
+    {
+      for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
+       {
+         if (property_function (fragP)
+             && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
+           return TRUE;
+       }
+    }
+  return FALSE;
 }
 
 
 }
 
 
-bfd_boolean
-section_has_property (sec, property_function)
-     segT sec;
-     frag_predicate property_function;
+static bfd_boolean
+section_has_xproperty (segT sec, frag_flags_fn property_function)
 {
   segment_info_type *seginfo = seg_info (sec);
   fragS *fragP;
 {
   segment_info_type *seginfo = seg_info (sec);
   fragS *fragP;
@@ -8123,8 +10587,9 @@ section_has_property (sec, property_function)
     {
       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)
-             && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
+         frag_flags prop_flags;
+         property_function (fragP, &prop_flags);
+         if (!xtensa_frag_flags_is_empty (&prop_flags))
            return TRUE;
        }
     }
            return TRUE;
        }
     }
@@ -8134,12 +10599,12 @@ section_has_property (sec, property_function)
 
 /* Two types of block sections exist right now: literal and insns.  */
 
 
 /* Two types of block sections exist right now: literal and insns.  */
 
-void
-add_xt_block_frags (sec, xt_block_sec, xt_block, property_function)
-     segT sec;
-     segT xt_block_sec;
-     xtensa_block_info **xt_block;
-     frag_predicate property_function;
+static void
+add_xt_block_frags (segT sec,
+                   segT xt_block_sec,
+                   xtensa_block_info **xt_block,
+                   frag_predicate property_function,
+                   frag_predicate end_property_function)
 {
   segment_info_type *seg_info;
   segment_info_type *xt_seg_info;
 {
   segment_info_type *seg_info;
   segment_info_type *xt_seg_info;
@@ -8154,47 +10619,397 @@ add_xt_block_frags (sec, xt_block_sec, xt_block, property_function)
     xt_block = &(*xt_block)->next;
   /* We are either at NULL at the beginning or at the end.  */
 
     xt_block = &(*xt_block)->next;
   /* We are either at NULL at the beginning or at the end.  */
 
-  /* Walk through the frags.  */
-  seg_offset = 0;
+  /* Walk through the frags.  */
+  seg_offset = 0;
+
+  if (seg_info->frchainP)
+    {
+      for (fragP = seg_info->frchainP->frch_root;
+          fragP;
+          fragP = fragP->fr_next)
+       {
+         if (property_function (fragP)
+             && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
+           {
+             if (*xt_block != NULL)
+               {
+                 if ((*xt_block)->offset + (*xt_block)->size
+                     == fragP->fr_address)
+                   (*xt_block)->size += fragP->fr_fix;
+                 else
+                   xt_block = &((*xt_block)->next);
+               }
+             if (*xt_block == NULL)
+               {
+                 xtensa_block_info *new_block = (xtensa_block_info *)
+                   xmalloc (sizeof (xtensa_block_info));
+                 new_block->sec = sec;
+                 new_block->offset = fragP->fr_address;
+                 new_block->size = fragP->fr_fix;
+                 new_block->next = NULL;
+                 xtensa_frag_flags_init (&new_block->flags);
+                 *xt_block = new_block;
+               }
+             if (end_property_function
+                 && end_property_function (fragP))
+               {
+                 xt_block = &((*xt_block)->next);
+               }
+           }
+       }
+    }
+}
+
+
+/* Break the encapsulation of add_xt_prop_frags here.  */
+
+static bfd_boolean
+xtensa_frag_flags_is_empty (const frag_flags *prop_flags)
+{
+  if (prop_flags->is_literal
+      || prop_flags->is_insn
+      || prop_flags->is_data
+      || prop_flags->is_unreachable)
+    return FALSE;
+  return TRUE;
+}
+
+
+static void
+xtensa_frag_flags_init (frag_flags *prop_flags)
+{
+  memset (prop_flags, 0, sizeof (frag_flags));
+}
+
+
+static void
+get_frag_property_flags (const fragS *fragP, frag_flags *prop_flags)
+{
+  xtensa_frag_flags_init (prop_flags);
+  if (fragP->tc_frag_data.is_literal)
+    prop_flags->is_literal = TRUE;
+  if (fragP->tc_frag_data.is_specific_opcode
+      || fragP->tc_frag_data.is_no_transform)
+    prop_flags->is_no_transform = TRUE;
+  if (fragP->tc_frag_data.is_unreachable)
+    prop_flags->is_unreachable = TRUE;
+  else if (fragP->tc_frag_data.is_insn)
+    {
+      prop_flags->is_insn = TRUE;
+      if (fragP->tc_frag_data.is_loop_target)
+       prop_flags->insn.is_loop_target = TRUE;
+      if (fragP->tc_frag_data.is_branch_target)
+       prop_flags->insn.is_branch_target = TRUE;
+      if (fragP->tc_frag_data.is_no_density)
+       prop_flags->insn.is_no_density = TRUE;
+      if (fragP->tc_frag_data.use_absolute_literals)
+       prop_flags->insn.is_abslit = TRUE;
+    }
+  if (fragP->tc_frag_data.is_align)
+    {
+      prop_flags->is_align = TRUE;
+      prop_flags->alignment = fragP->tc_frag_data.alignment;
+      if (xtensa_frag_flags_is_empty (prop_flags))
+       prop_flags->is_data = TRUE;
+    }
+}
+
+
+static bfd_vma
+frag_flags_to_number (const frag_flags *prop_flags)
+{
+  bfd_vma num = 0;
+  if (prop_flags->is_literal)
+    num |= XTENSA_PROP_LITERAL;
+  if (prop_flags->is_insn)
+    num |= XTENSA_PROP_INSN;
+  if (prop_flags->is_data)
+    num |= XTENSA_PROP_DATA;
+  if (prop_flags->is_unreachable)
+    num |= XTENSA_PROP_UNREACHABLE;
+  if (prop_flags->insn.is_loop_target)
+    num |= XTENSA_PROP_INSN_LOOP_TARGET;
+  if (prop_flags->insn.is_branch_target)
+    {
+      num |= XTENSA_PROP_INSN_BRANCH_TARGET;
+      num = SET_XTENSA_PROP_BT_ALIGN (num, prop_flags->insn.bt_align_priority);
+    }
+
+  if (prop_flags->insn.is_no_density)
+    num |= XTENSA_PROP_INSN_NO_DENSITY;
+  if (prop_flags->is_no_transform)
+    num |= XTENSA_PROP_NO_TRANSFORM;
+  if (prop_flags->insn.is_no_reorder)
+    num |= XTENSA_PROP_INSN_NO_REORDER;
+  if (prop_flags->insn.is_abslit)
+    num |= XTENSA_PROP_INSN_ABSLIT;
+
+  if (prop_flags->is_align)
+    {
+      num |= XTENSA_PROP_ALIGN;
+      num = SET_XTENSA_PROP_ALIGNMENT (num, prop_flags->alignment);
+    }
+
+  return num;
+}
+
+
+static bfd_boolean
+xtensa_frag_flags_combinable (const frag_flags *prop_flags_1,
+                             const frag_flags *prop_flags_2)
+{
+  /* Cannot combine with an end marker.  */
+
+  if (prop_flags_1->is_literal != prop_flags_2->is_literal)
+    return FALSE;
+  if (prop_flags_1->is_insn != prop_flags_2->is_insn)
+    return FALSE;
+  if (prop_flags_1->is_data != prop_flags_2->is_data)
+    return FALSE;
+
+  if (prop_flags_1->is_insn)
+    {
+      /* Properties of the beginning of the frag.  */
+      if (prop_flags_2->insn.is_loop_target)
+       return FALSE;
+      if (prop_flags_2->insn.is_branch_target)
+       return FALSE;
+      if (prop_flags_1->insn.is_no_density !=
+         prop_flags_2->insn.is_no_density)
+       return FALSE;
+      if (prop_flags_1->is_no_transform !=
+         prop_flags_2->is_no_transform)
+       return FALSE;
+      if (prop_flags_1->insn.is_no_reorder !=
+         prop_flags_2->insn.is_no_reorder)
+       return FALSE;
+      if (prop_flags_1->insn.is_abslit !=
+         prop_flags_2->insn.is_abslit)
+       return FALSE;
+    }
+
+  if (prop_flags_1->is_align)
+    return FALSE;
+
+  return TRUE;
+}
+
+
+static bfd_vma
+xt_block_aligned_size (const xtensa_block_info *xt_block)
+{
+  bfd_vma end_addr;
+  unsigned align_bits;
+
+  if (!xt_block->flags.is_align)
+    return xt_block->size;
+
+  end_addr = xt_block->offset + xt_block->size;
+  align_bits = xt_block->flags.alignment;
+  end_addr = ((end_addr + ((1 << align_bits) -1)) >> align_bits) << align_bits;
+  return end_addr - xt_block->offset;
+}
+
+
+static bfd_boolean
+xtensa_xt_block_combine (xtensa_block_info *xt_block,
+                        const xtensa_block_info *xt_block_2)
+{
+  if (xt_block->sec != xt_block_2->sec)
+    return FALSE;
+  if (xt_block->offset + xt_block_aligned_size (xt_block)
+      != xt_block_2->offset)
+    return FALSE;
+
+  if (xt_block_2->size == 0
+      && (!xt_block_2->flags.is_unreachable
+         || xt_block->flags.is_unreachable))
+    {
+      if (xt_block_2->flags.is_align
+         && xt_block->flags.is_align)
+       {
+         /* Nothing needed.  */
+         if (xt_block->flags.alignment >= xt_block_2->flags.alignment)
+           return TRUE;
+       }
+      else
+       {
+         if (xt_block_2->flags.is_align)
+           {
+             /* Push alignment to previous entry.  */
+             xt_block->flags.is_align = xt_block_2->flags.is_align;
+             xt_block->flags.alignment = xt_block_2->flags.alignment;
+           }
+         return TRUE;
+       }
+    }
+  if (!xtensa_frag_flags_combinable (&xt_block->flags,
+                                    &xt_block_2->flags))
+    return FALSE;
+
+  xt_block->size += xt_block_2->size;
+
+  if (xt_block_2->flags.is_align)
+    {
+      xt_block->flags.is_align = TRUE;
+      xt_block->flags.alignment = xt_block_2->flags.alignment;
+    }
+
+  return TRUE;
+}
+
+
+static void
+add_xt_prop_frags (segT sec,
+                  segT xt_block_sec,
+                  xtensa_block_info **xt_block,
+                  frag_flags_fn property_function)
+{
+  segment_info_type *seg_info;
+  segment_info_type *xt_seg_info;
+  bfd_vma seg_offset;
+  fragS *fragP;
+
+  xt_seg_info = retrieve_segment_info (xt_block_sec);
+  seg_info = retrieve_segment_info (sec);
+  /* Build it if needed.  */
+  while (*xt_block != NULL)
+    {
+      xt_block = &(*xt_block)->next;
+    }
+  /* We are either at NULL at the beginning or at the end.  */
+
+  /* Walk through the frags.  */
+  seg_offset = 0;
+
+  if (seg_info->frchainP)
+    {
+      for (fragP = seg_info->frchainP->frch_root; fragP;
+          fragP = fragP->fr_next)
+       {
+         xtensa_block_info tmp_block;
+         tmp_block.sec = sec;
+         tmp_block.offset = fragP->fr_address;
+         tmp_block.size = fragP->fr_fix;
+         tmp_block.next = NULL;
+         property_function (fragP, &tmp_block.flags);
+
+         if (!xtensa_frag_flags_is_empty (&tmp_block.flags))
+           /* && fragP->fr_fix != 0) */
+           {
+             if ((*xt_block) == NULL
+                 || !xtensa_xt_block_combine (*xt_block, &tmp_block))
+               {
+                 xtensa_block_info *new_block;
+                 if ((*xt_block) != NULL)
+                   xt_block = &(*xt_block)->next;
+                 new_block = (xtensa_block_info *)
+                   xmalloc (sizeof (xtensa_block_info));
+                 *new_block = tmp_block;
+                 *xt_block = new_block;
+               }
+           }
+       }
+    }
+}
+
+\f
+/* op_placement_info_table */
+
+/* op_placement_info makes it easier to determine which
+   ops can go in which slots.  */
 
 
-  if (seg_info->frchainP)
-    {
-      for (fragP = seg_info->frchainP->frch_root;
-          fragP;
-          fragP = fragP->fr_next)
+static void
+init_op_placement_info_table (void)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_insnbuf ibuf = xtensa_insnbuf_alloc (isa);
+  xtensa_opcode opcode;
+  xtensa_format fmt;
+  int slot;
+  int num_opcodes = xtensa_isa_num_opcodes (isa);
+
+  op_placement_table = (op_placement_info_table)
+    xmalloc (sizeof (op_placement_info) * num_opcodes);
+  assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
+
+  for (opcode = 0; opcode < num_opcodes; opcode++)
+    {
+      op_placement_info *opi = &op_placement_table[opcode];
+      /* FIXME: Make tinsn allocation dynamic.  */
+      if (xtensa_opcode_num_operands (isa, opcode) >= MAX_INSN_ARGS)
+       as_fatal (_("too many operands in instruction"));
+      opi->narrowest = XTENSA_UNDEFINED;
+      opi->narrowest_size = 0x7F;
+      opi->narrowest_slot = 0;
+      opi->formats = 0;
+      opi->num_formats = 0;
+      opi->issuef = 0;
+      for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
        {
        {
-         if (property_function (fragP)
-             && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
+         opi->slots[fmt] = 0;
+         for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
            {
            {
-             if (*xt_block != NULL)
-               {
-                 if ((*xt_block)->offset + (*xt_block)->size
-                     == fragP->fr_address)
-                   (*xt_block)->size += fragP->fr_fix;
-                 else
-                   xt_block = &((*xt_block)->next);
-               }
-             if (*xt_block == NULL)
+             if (xtensa_opcode_encode (isa, fmt, slot, ibuf, opcode) == 0)
                {
                {
-                 xtensa_block_info *new_block = (xtensa_block_info *)
-                   xmalloc (sizeof (xtensa_block_info));
-                 new_block->sec = sec;
-                 new_block->offset = fragP->fr_address;
-                 new_block->size = fragP->fr_fix;
-                 new_block->next = NULL;
-                 *xt_block = new_block;
+                 int fmt_length = xtensa_format_length (isa, fmt);
+                 opi->issuef++;
+                 set_bit (fmt, opi->formats);
+                 set_bit (slot, opi->slots[fmt]);
+                 if (fmt_length < opi->narrowest_size
+                     || (fmt_length == opi->narrowest_size
+                         && (xtensa_format_num_slots (isa, fmt)
+                             < xtensa_format_num_slots (isa,
+                                                        opi->narrowest))))
+                   {
+                     opi->narrowest = fmt;
+                     opi->narrowest_size = fmt_length;
+                     opi->narrowest_slot = slot;
+                   }
                }
            }
                }
            }
+         if (opi->formats)
+           opi->num_formats++;
        }
     }
        }
     }
+  xtensa_insnbuf_free (isa, ibuf);
+}
+
+
+bfd_boolean
+opcode_fits_format_slot (xtensa_opcode opcode, xtensa_format fmt, int slot)
+{
+  return bit_is_set (slot, op_placement_table[opcode].slots[fmt]);
+}
+
+
+/* If the opcode is available in a single slot format, return its size.  */
+
+static int
+xg_get_single_size (xtensa_opcode opcode)
+{
+  return op_placement_table[opcode].narrowest_size;
+}
+
+
+static xtensa_format
+xg_get_single_format (xtensa_opcode opcode)
+{
+  return op_placement_table[opcode].narrowest;
+}
+
+
+static int
+xg_get_single_slot (xtensa_opcode opcode)
+{
+  return op_placement_table[opcode].narrowest_slot;
 }
 
 \f
 /* Instruction Stack Functions (from "xtensa-istack.h").  */
 
 void
 }
 
 \f
 /* Instruction Stack Functions (from "xtensa-istack.h").  */
 
 void
-istack_init (stack)
-     IStack *stack;
+istack_init (IStack *stack)
 {
   memset (stack, 0, sizeof (IStack));
   stack->ninsn = 0;
 {
   memset (stack, 0, sizeof (IStack));
   stack->ninsn = 0;
@@ -8202,27 +11017,24 @@ istack_init (stack)
 
 
 bfd_boolean
 
 
 bfd_boolean
-istack_empty (stack)
-     IStack *stack;
+istack_empty (IStack *stack)
 {
   return (stack->ninsn == 0);
 }
 
 
 bfd_boolean
 {
   return (stack->ninsn == 0);
 }
 
 
 bfd_boolean
-istack_full (stack)
-     IStack *stack;
+istack_full (IStack *stack)
 {
   return (stack->ninsn == MAX_ISTACK);
 }
 
 
 /* Return a pointer to the top IStack entry.
 {
   return (stack->ninsn == MAX_ISTACK);
 }
 
 
 /* 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 (stack)
-     IStack *stack;
+istack_top (IStack *stack)
 {
   int rec = stack->ninsn - 1;
   assert (!istack_empty (stack));
 {
   int rec = stack->ninsn - 1;
   assert (!istack_empty (stack));
@@ -8231,89 +11043,60 @@ istack_top (stack)
 
 
 /* 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 (stack, insn)
-     IStack *stack;
-     TInsn *insn;
+istack_push (IStack *stack, TInsn *insn)
 {
   int rec = stack->ninsn;
   assert (!istack_full (stack));
 {
   int rec = stack->ninsn;
   assert (!istack_full (stack));
-  tinsn_copy (&stack->insn[rec], insn);
+  stack->insn[rec] = *insn;
   stack->ninsn++;
 }
 
 
 /* Clear space for the next TInsn on the IStack and return a pointer
   stack->ninsn++;
 }
 
 
 /* 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 (stack)
-     IStack *stack;
+istack_push_space (IStack *stack)
 {
   int rec = stack->ninsn;
   TInsn *insn;
   assert (!istack_full (stack));
   insn = &stack->insn[rec];
 {
   int rec = stack->ninsn;
   TInsn *insn;
   assert (!istack_full (stack));
   insn = &stack->insn[rec];
-  memset (insn, 0, sizeof (TInsn));
+  tinsn_init (insn);
   stack->ninsn++;
   return insn;
 }
 
 
 /* Remove the last pushed instruction.  It is an error to call this if
   stack->ninsn++;
   return insn;
 }
 
 
 /* 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 (stack)
-     IStack *stack;
+istack_pop (IStack *stack)
 {
   int rec = stack->ninsn - 1;
   assert (!istack_empty (stack));
   stack->ninsn--;
 {
   int rec = stack->ninsn - 1;
   assert (!istack_empty (stack));
   stack->ninsn--;
-  memset (&stack->insn[rec], 0, sizeof (TInsn));
+  tinsn_init (&stack->insn[rec]);
 }
 
 \f
 /* TInsn functions.  */
 
 void
 }
 
 \f
 /* TInsn functions.  */
 
 void
-tinsn_init (dst)
-     TInsn *dst;
+tinsn_init (TInsn *dst)
 {
   memset (dst, 0, sizeof (TInsn));
 }
 
 
 {
   memset (dst, 0, sizeof (TInsn));
 }
 
 
-void
-tinsn_copy (dst, src)
-     TInsn *dst;
-     const TInsn *src;
-{
-  tinsn_init (dst);
-  memcpy (dst, src, sizeof (TInsn));
-}
-
-
-/* Get the ``num''th token of the TInsn.
-   It is illegal to call this if num > insn->ntoks.  */
-
-expressionS *
-tinsn_get_tok (insn, num)
-     TInsn *insn;
-     int num;
-{
-  assert (num < insn->ntok);
-  return &insn->tok[num];
-}
-
-
-/* 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 (insn)
-     const TInsn *insn;
+tinsn_has_symbolic_operands (const TInsn *insn)
 {
   int i;
   int n = insn->ntok;
 {
   int i;
   int n = insn->ntok;
@@ -8336,9 +11119,9 @@ tinsn_has_symbolic_operands (insn)
 
 
 bfd_boolean
 
 
 bfd_boolean
-tinsn_has_invalid_symbolic_operands (insn)
-     const TInsn *insn;
+tinsn_has_invalid_symbolic_operands (const TInsn *insn)
 {
 {
+  xtensa_isa isa = xtensa_default_isa;
   int i;
   int n = insn->ntok;
 
   int i;
   int n = insn->ntok;
 
@@ -8351,12 +11134,24 @@ tinsn_has_invalid_symbolic_operands (insn)
        case O_register:
        case O_constant:
          break;
        case O_register:
        case O_constant:
          break;
+       case O_big:
+       case O_illegal:
+       case O_absent:
+         /* Errors for these types are caught later.  */
+         break;
+       case O_hi16:
+       case O_lo16:
        default:
        default:
-         if (i == get_relaxable_immed (insn->opcode))
-           break;
-         as_bad (_("invalid symbolic operand %d on '%s'"),
-                 i, xtensa_opcode_name (xtensa_default_isa, insn->opcode));
-         return TRUE;
+         /* Symbolic immediates are only allowed on the last immediate
+            operand.  At this time, CONST16 is the only opcode where we
+            support non-PC-relative relocations.  */
+         if (i != get_relaxable_immed (insn->opcode)
+             || (xtensa_operand_is_PCrelative (isa, insn->opcode, i) != 1
+                 && insn->opcode != xtensa_const16_opcode))
+           {
+             as_bad (_("invalid symbolic operand"));
+             return TRUE;
+           }
        }
     }
   return FALSE;
        }
     }
   return FALSE;
@@ -8370,8 +11165,7 @@ tinsn_has_invalid_symbolic_operands (insn)
    boil down to SYMBOL + OFFSET.  */
 
 static bfd_boolean
    boil down to SYMBOL + OFFSET.  */
 
 static bfd_boolean
-tinsn_has_complex_operands (insn)
-     const TInsn *insn;
+tinsn_has_complex_operands (const TInsn *insn)
 {
   int i;
   int n = insn->ntok;
 {
   int i;
   int n = insn->ntok;
@@ -8383,6 +11177,8 @@ tinsn_has_complex_operands (insn)
        case O_register:
        case O_constant:
        case O_symbol:
        case O_register:
        case O_constant:
        case O_symbol:
+       case O_lo16:
+       case O_hi16:
          break;
        default:
          return TRUE;
          break;
        default:
          return TRUE;
@@ -8392,73 +11188,122 @@ tinsn_has_complex_operands (insn)
 }
 
 
 }
 
 
-/* Convert the constant operands in the t_insn to insnbuf.
-   Return true if there is a symbol in the immediate field.
-
-   Before this is called, 
-   1) the number of operands are correct
-   2) the t_insn is a ITYPE_INSN
-   3) ONLY the relaxable_ is built
-   4) All operands are O_constant, O_symbol.  All constants fit
-   The return value tells whether there are any remaining O_symbols.  */
+/* Encode a TInsn opcode and its constant operands into slotbuf.
+   Return TRUE if there is a symbol in the immediate field.  This
+   function assumes that:
+   1) The number of operands are correct.
+   2) The insn_type is ITYPE_INSN.
+   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.  */
 
 static bfd_boolean
 
 static bfd_boolean
-tinsn_to_insnbuf (t_insn, insnbuf)
-     TInsn *t_insn;
-     xtensa_insnbuf insnbuf;
+tinsn_to_slotbuf (xtensa_format fmt,
+                 int slot,
+                 TInsn *tinsn,
+                 xtensa_insnbuf slotbuf)
 {
   xtensa_isa isa = xtensa_default_isa;
 {
   xtensa_isa isa = xtensa_default_isa;
-  xtensa_opcode opcode = t_insn->opcode;
+  xtensa_opcode opcode = tinsn->opcode;
   bfd_boolean has_fixup = FALSE;
   bfd_boolean has_fixup = FALSE;
-  int noperands = xtensa_num_operands (isa, opcode);
+  int noperands = xtensa_opcode_num_operands (isa, opcode);
   int i;
   int i;
-  uint32 opnd_value;
-  char *file_name;
-  int line;
 
 
-  assert (t_insn->insn_type == ITYPE_INSN);
-  if (noperands != t_insn->ntok)
+  assert (tinsn->insn_type == ITYPE_INSN);
+  if (noperands != tinsn->ntok)
     as_fatal (_("operand number mismatch"));
 
     as_fatal (_("operand number mismatch"));
 
-  xtensa_encode_insn (isa, opcode, insnbuf);
+  if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opcode))
+    {
+      as_bad (_("cannot encode opcode \"%s\" in the given format \"%s\""),
+             xtensa_opcode_name (isa, opcode), xtensa_format_name (isa, fmt));
+      return FALSE;
+    }
 
 
-  for (i = 0; i < noperands; ++i)
+  for (i = 0; i < noperands; i++)
     {
     {
-      expressionS *expr = &t_insn->tok[i];
-      xtensa_operand operand = xtensa_get_operand (isa, opcode, i);
+      expressionS *expr = &tinsn->tok[i];
+      int rc;
+      unsigned line;
+      char *file_name;
+      uint32 opnd_value;
+
       switch (expr->X_op)
        {
        case O_register:
       switch (expr->X_op)
        {
        case O_register:
-         /* The register number has already been checked in  
+         if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+           break;
+         /* The register number has already been checked in
             expression_maybe_register, so we don't need to check here.  */
          opnd_value = expr->X_add_number;
             expression_maybe_register, so we don't need to check here.  */
          opnd_value = expr->X_add_number;
-         (void) xtensa_operand_encode (operand, &opnd_value);
-         xtensa_operand_set_field (operand, insnbuf, opnd_value);
+         (void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
+         rc = xtensa_operand_set_field (isa, opcode, i, fmt, slot, slotbuf,
+                                        opnd_value);
+         if (rc != 0)
+           as_warn (_("xtensa-isa failure: %s"), xtensa_isa_error_msg (isa));
          break;
 
        case O_constant:
          break;
 
        case O_constant:
+         if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+           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 (insnbuf, opcode, operand,
+         xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
                                      expr->X_add_number, file_name, line);
          break;
 
                                      expr->X_add_number, file_name, line);
          break;
 
-       case O_symbol:
        default:
          has_fixup = TRUE;
          break;
        }
     }
        default:
          has_fixup = TRUE;
          break;
        }
     }
+
   return has_fixup;
 }
 
 
   return has_fixup;
 }
 
 
-/* Check the instruction arguments.  Return true on failure.  */
+/* 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.
+   Return TRUE if there is a symbol in the immediate field.  See also the
+   assumptions listed for tinsn_to_slotbuf.  */
 
 
-bfd_boolean
-tinsn_check_arguments (insn)
-     const TInsn *insn;
+static bfd_boolean
+tinsn_to_insnbuf (TInsn *tinsn, xtensa_insnbuf insnbuf)
+{
+  static xtensa_insnbuf slotbuf = 0;
+  static vliw_insn vinsn;
+  xtensa_isa isa = xtensa_default_isa;
+  bfd_boolean has_fixup = FALSE;
+  int i;
+
+  if (!slotbuf)
+    {
+      slotbuf = xtensa_insnbuf_alloc (isa);
+      xg_init_vinsn (&vinsn);
+    }
+
+  xg_clear_vinsn (&vinsn);
+
+  bundle_tinsn (tinsn, &vinsn);
+
+  xtensa_format_encode (isa, vinsn.format, insnbuf);
+
+  for (i = 0; i < vinsn.num_slots; i++)
+    {
+      /* Only one slot may have a fix-up because the rest contains NOPs.  */
+      has_fixup |=
+       tinsn_to_slotbuf (vinsn.format, i, &vinsn.slots[i], vinsn.slotbuf[i]);
+      xtensa_format_set_slot (isa, vinsn.format, i, insnbuf, vinsn.slotbuf[i]);
+    }
+
+  return has_fixup;
+}
+
+
+/* Check the instruction arguments.  Return TRUE on failure.  */
+
+static bfd_boolean
+tinsn_check_arguments (const TInsn *insn)
 {
   xtensa_isa isa = xtensa_default_isa;
   xtensa_opcode opcode = insn->opcode;
 {
   xtensa_isa isa = xtensa_default_isa;
   xtensa_opcode opcode = insn->opcode;
@@ -8469,13 +11314,13 @@ tinsn_check_arguments (insn)
       return TRUE;
     }
 
       return TRUE;
     }
 
-  if (xtensa_num_operands (isa, opcode) > insn->ntok)
+  if (xtensa_opcode_num_operands (isa, opcode) > insn->ntok)
     {
       as_bad (_("too few operands"));
       return TRUE;
     }
 
     {
       as_bad (_("too few operands"));
       return TRUE;
     }
 
-  if (xtensa_num_operands (isa, opcode) < insn->ntok)
+  if (xtensa_opcode_num_operands (isa, opcode) < insn->ntok)
     {
       as_bad (_("too many operands"));
       return TRUE;
     {
       as_bad (_("too many operands"));
       return TRUE;
@@ -8487,31 +11332,38 @@ tinsn_check_arguments (insn)
 /* Load an instruction from its encoded form.  */
 
 static void
 /* Load an instruction from its encoded form.  */
 
 static void
-tinsn_from_chars (t_insn, f)
-     TInsn *t_insn;
-     char *f;
+tinsn_from_chars (TInsn *tinsn, char *f, int slot)
 {
 {
-  static xtensa_insnbuf insnbuf = NULL;
-  int i;
-  xtensa_opcode opcode;
-  xtensa_isa isa = xtensa_default_isa;
+  vliw_insn vinsn;
 
 
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (isa);
+  xg_init_vinsn (&vinsn);
+  vinsn_from_chars (&vinsn, f);
 
 
-  xtensa_insnbuf_from_chars (isa, insnbuf, f);
-  opcode = xtensa_decode_insn (isa, insnbuf);
+  *tinsn = vinsn.slots[slot];
+  xg_free_vinsn (&vinsn);
+}
+
+
+static void
+tinsn_from_insnbuf (TInsn *tinsn,
+                   xtensa_insnbuf slotbuf,
+                   xtensa_format fmt,
+                   int slot)
+{
+  int i;
+  xtensa_isa isa = xtensa_default_isa;
 
   /* Find the immed.  */
 
   /* Find the immed.  */
-  tinsn_init (t_insn);
-  t_insn->insn_type = ITYPE_INSN;
-  t_insn->is_specific_opcode = FALSE;  /* Must not be specific.  */
-  t_insn->opcode = opcode;
-  t_insn->ntok = xtensa_num_operands (isa, opcode);
-  for (i = 0; i < t_insn->ntok; i++)
+  tinsn_init (tinsn);
+  tinsn->insn_type = ITYPE_INSN;
+  tinsn->is_specific_opcode = FALSE;   /* must not be specific */
+  tinsn->opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+  tinsn->ntok = xtensa_opcode_num_operands (isa, tinsn->opcode);
+  for (i = 0; i < tinsn->ntok; i++)
     {
     {
-      set_expr_const (&t_insn->tok[i],
-                     xtensa_insnbuf_get_operand (insnbuf, opcode, i));
+      set_expr_const (&tinsn->tok[i],
+                     xtensa_insnbuf_get_operand (slotbuf, fmt, slot,
+                                                 tinsn->opcode, i));
     }
 }
 
     }
 }
 
@@ -8519,75 +11371,249 @@ tinsn_from_chars (t_insn, f)
 /* Read the value of the relaxable immed from the fr_symbol and fr_offset.  */
 
 static void
 /* Read the value of the relaxable immed from the fr_symbol and fr_offset.  */
 
 static void
-tinsn_immed_from_frag (t_insn, fragP)
-     TInsn *t_insn;
-     fragS *fragP;
+tinsn_immed_from_frag (TInsn *tinsn, fragS *fragP, int slot)
 {
 {
-  xtensa_opcode opcode = t_insn->opcode;
+  xtensa_opcode opcode = tinsn->opcode;
   int opnum;
 
   int opnum;
 
-  if (fragP->fr_symbol)
+  if (fragP->tc_frag_data.slot_symbols[slot])
     {
       opnum = get_relaxable_immed (opcode);
     {
       opnum = get_relaxable_immed (opcode);
-      set_expr_symbol_offset (&t_insn->tok[opnum],
-                             fragP->fr_symbol, fragP->fr_offset);
+      assert (opnum >= 0);
+      set_expr_symbol_offset (&tinsn->tok[opnum],
+                             fragP->tc_frag_data.slot_symbols[slot],
+                             fragP->tc_frag_data.slot_offsets[slot]);
     }
 }
 
 
 static int
     }
 }
 
 
 static int
-get_num_stack_text_bytes (istack)
-     IStack *istack;
+get_num_stack_text_bytes (IStack *istack)
 {
   int i;
   int text_bytes = 0;
 
   for (i = 0; i < istack->ninsn; i++)
     {
 {
   int i;
   int text_bytes = 0;
 
   for (i = 0; i < istack->ninsn; i++)
     {
-      TInsn *t_insn = &istack->insn[i];
-      if (t_insn->insn_type == ITYPE_INSN)
-       text_bytes += xg_get_insn_size (t_insn);
+      TInsn *tinsn = &istack->insn[i];
+      if (tinsn->insn_type == ITYPE_INSN)
+       text_bytes += xg_get_single_size (tinsn->opcode);
     }
   return text_bytes;
 }
 
 
 static int
     }
   return text_bytes;
 }
 
 
 static int
-get_num_stack_literal_bytes (istack)
-     IStack *istack;
+get_num_stack_literal_bytes (IStack *istack)
 {
   int i;
   int lit_bytes = 0;
 
   for (i = 0; i < istack->ninsn; i++)
     {
 {
   int i;
   int lit_bytes = 0;
 
   for (i = 0; i < istack->ninsn; i++)
     {
-      TInsn *t_insn = &istack->insn[i];
-
-      if (t_insn->insn_type == ITYPE_LITERAL && t_insn->ntok == 1)
+      TInsn *tinsn = &istack->insn[i];
+      if (tinsn->insn_type == ITYPE_LITERAL && tinsn->ntok == 1)
        lit_bytes += 4;
     }
   return lit_bytes;
 }
 
        lit_bytes += 4;
     }
   return lit_bytes;
 }
 
+\f
+/* vliw_insn functions.  */
+
+static void
+xg_init_vinsn (vliw_insn *v)
+{
+  int i;
+  xtensa_isa isa = xtensa_default_isa;
+
+  xg_clear_vinsn (v);
+
+  v->insnbuf = xtensa_insnbuf_alloc (isa);
+  if (v->insnbuf == NULL)
+    as_fatal (_("out of memory"));
+
+  for (i = 0; i < MAX_SLOTS; i++)
+    {
+      v->slotbuf[i] = xtensa_insnbuf_alloc (isa);
+      if (v->slotbuf[i] == NULL)
+       as_fatal (_("out of memory"));
+    }
+}
+
+
+static void
+xg_clear_vinsn (vliw_insn *v)
+{
+  int i;
+
+  memset (v, 0, offsetof (vliw_insn, insnbuf));
+
+  v->format = XTENSA_UNDEFINED;
+  v->num_slots = 0;
+  v->inside_bundle = FALSE;
+
+  if (xt_saved_debug_type != DEBUG_NONE)
+    debug_type = xt_saved_debug_type;
+
+  for (i = 0; i < MAX_SLOTS; i++)
+    v->slots[i].opcode = XTENSA_UNDEFINED;
+}
+
+
+static bfd_boolean
+vinsn_has_specific_opcodes (vliw_insn *v)
+{
+  int i;
+
+  for (i = 0; i < v->num_slots; i++)
+    {
+      if (v->slots[i].is_specific_opcode)
+       return TRUE;
+    }
+  return FALSE;
+}
+
+
+static void
+xg_free_vinsn (vliw_insn *v)
+{
+  int i;
+  xtensa_insnbuf_free (xtensa_default_isa, v->insnbuf);
+  for (i = 0; i < MAX_SLOTS; i++)
+    xtensa_insnbuf_free (xtensa_default_isa, v->slotbuf[i]);
+}
+
+
+/* Encode a vliw_insn into an insnbuf.  Return TRUE if there are any symbolic
+   operands.  See also the assumptions listed for tinsn_to_slotbuf.  */
+
+static bfd_boolean
+vinsn_to_insnbuf (vliw_insn *vinsn,
+                 char *frag_offset,
+                 fragS *fragP,
+                 bfd_boolean record_fixup)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt = vinsn->format;
+  xtensa_insnbuf insnbuf = vinsn->insnbuf;
+  int slot;
+  bfd_boolean has_fixup = FALSE;
+
+  xtensa_format_encode (isa, fmt, insnbuf);
+
+  for (slot = 0; slot < vinsn->num_slots; slot++)
+    {
+      TInsn *tinsn = &vinsn->slots[slot];
+      bfd_boolean tinsn_has_fixup =
+       tinsn_to_slotbuf (vinsn->format, slot, tinsn,
+                         vinsn->slotbuf[slot]);
+
+      xtensa_format_set_slot (isa, fmt, slot,
+                             insnbuf, vinsn->slotbuf[slot]);
+      if (tinsn_has_fixup)
+       {
+         int i;
+         xtensa_opcode opcode = tinsn->opcode;
+         int noperands = xtensa_opcode_num_operands (isa, opcode);
+         has_fixup = TRUE;
+
+         for (i = 0; i < noperands; i++)
+           {
+             expressionS* expr = &tinsn->tok[i];
+             switch (expr->X_op)
+               {
+               case O_symbol:
+               case O_lo16:
+               case O_hi16:
+                 if (get_relaxable_immed (opcode) == i)
+                   {
+                     /* Add a fix record for the instruction, except if this
+                        function is being called prior to relaxation, i.e.,
+                        if record_fixup is false, and the instruction might
+                        be relaxed later.  */
+                     if (record_fixup
+                         || tinsn->is_specific_opcode
+                         || !xg_is_relaxable_insn (tinsn, 0))
+                       {
+                         xg_add_opcode_fix (tinsn, i, fmt, slot, expr, fragP,
+                                            frag_offset - fragP->fr_literal);
+                       }
+                     else
+                       {
+                         if (expr->X_op != O_symbol)
+                           as_bad (_("invalid operand"));
+                         tinsn->symbol = expr->X_add_symbol;
+                         tinsn->offset = expr->X_add_number;
+                       }
+                   }
+                 else
+                   as_bad (_("symbolic operand not allowed"));
+                 break;
+
+               case O_constant:
+               case O_register:
+                 break;
+
+               default:
+                 as_bad (_("expression too complex"));
+                 break;
+               }
+           }
+       }
+    }
+
+  return has_fixup;
+}
+
+
+static void
+vinsn_from_chars (vliw_insn *vinsn, char *f)
+{
+  static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
+  int i;
+  xtensa_format fmt;
+  xtensa_isa isa = xtensa_default_isa;
+
+  if (!insnbuf)
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
+
+  xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) f, 0);
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (fmt == XTENSA_UNDEFINED)
+    as_fatal (_("cannot decode instruction format"));
+  vinsn->format = fmt;
+  vinsn->num_slots = xtensa_format_num_slots (isa, fmt);
+
+  for (i = 0; i < vinsn->num_slots; i++)
+    {
+      TInsn *tinsn = &vinsn->slots[i];
+      xtensa_format_get_slot (isa, fmt, i, insnbuf, slotbuf);
+      tinsn_from_insnbuf (tinsn, slotbuf, fmt, i);
+    }
+}
+
 \f
 /* Expression utilities.  */
 
 \f
 /* 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 (s)
-     const expressionS *s;
+expr_is_const (const expressionS *s)
 {
   return (s->X_op == O_constant);
 }
 
 
 /* Get the expression constant.
 {
   return (s->X_op == O_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 (s)
-     const expressionS *s;
+get_expr_const (const expressionS *s)
 {
   assert (expr_is_const (s));
   return s->X_add_number;
 {
   assert (expr_is_const (s));
   return s->X_add_number;
@@ -8597,9 +11623,7 @@ get_expr_const (s)
 /* Set the expression to a constant value.  */
 
 void
 /* Set the expression to a constant value.  */
 
 void
-set_expr_const (s, val)
-     expressionS *s;
-     offsetT val;
+set_expr_const (expressionS *s, offsetT val)
 {
   s->X_op = O_constant;
   s->X_add_number = val;
 {
   s->X_op = O_constant;
   s->X_add_number = val;
@@ -8608,13 +11632,28 @@ set_expr_const (s, val)
 }
 
 
 }
 
 
+bfd_boolean
+expr_is_register (const expressionS *s)
+{
+  return (s->X_op == O_register);
+}
+
+
+/* Get the expression constant.
+   Calling this is illegal if expr_is_const () returns TRUE.  */
+
+offsetT
+get_expr_register (const expressionS *s)
+{
+  assert (expr_is_register (s));
+  return s->X_add_number;
+}
+
+
 /* Set the expression to a symbol + constant offset.  */
 
 void
 /* Set the expression to a symbol + constant offset.  */
 
 void
-set_expr_symbol_offset (s, sym, offset)
-     expressionS *s;
-     symbolS *sym;
-     offsetT offset;
+set_expr_symbol_offset (expressionS *s, symbolS *sym, offsetT offset)
 {
   s->X_op = O_symbol;
   s->X_add_symbol = sym;
 {
   s->X_op = O_symbol;
   s->X_add_symbol = sym;
@@ -8623,10 +11662,10 @@ set_expr_symbol_offset (s, sym, offset)
 }
 
 
 }
 
 
+/* Return TRUE if the two expressions are equal.  */
+
 bfd_boolean
 bfd_boolean
-expr_is_equal (s1, s2)
-     expressionS *s1;
-     expressionS *s2;
+expr_is_equal (expressionS *s1, expressionS *s2)
 {
   if (s1->X_op != s2->X_op)
     return FALSE;
 {
   if (s1->X_op != s2->X_op)
     return FALSE;
@@ -8641,17 +11680,13 @@ expr_is_equal (s1, s2)
 
 
 static void
 
 
 static void
-copy_expr (dst, src)
-     expressionS *dst;
-     const expressionS *src;
+copy_expr (expressionS *dst, const expressionS *src)
 {
   memcpy (dst, src, sizeof (expressionS));
 }
 
 \f
 {
   memcpy (dst, src, sizeof (expressionS));
 }
 
 \f
-/* Support for Tensilica's "--rename-section" option.  */
-
-#ifdef XTENSA_SECTION_RENAME
+/* Support for the "--rename-section" option.  */
 
 struct rename_section_struct
 {
 
 struct rename_section_struct
 {
@@ -8663,18 +11698,22 @@ struct rename_section_struct
 static struct rename_section_struct *section_rename;
 
 
 static struct rename_section_struct *section_rename;
 
 
-/* Parse the string oldname=new_name:oldname2=new_name2 
-   and call add_section_rename.  */
+/* Parse the string "oldname=new_name(:oldname2=new_name2)*" and add
+   entries to the section_rename list.  Note: Specifying multiple
+   renamings separated by colons is not documented and is retained only
+   for backward compatibility.  */
 
 
-void
-build_section_rename (arg)
-     const char *arg;
+static void
+build_section_rename (const char *arg)
 {
 {
+  struct rename_section_struct *r;
   char *this_arg = NULL;
   char *next_arg = NULL;
 
   char *this_arg = NULL;
   char *next_arg = NULL;
 
-  for (this_arg = strdup (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;
+
       if (this_arg)
        {
          next_arg = strchr (this_arg, ':');
       if (this_arg)
        {
          next_arg = strchr (this_arg, ':');
@@ -8684,67 +11723,55 @@ build_section_rename (arg)
              next_arg++;
            }
        }
              next_arg++;
            }
        }
-      {
-       char *old_name = this_arg;
-       char *new_name = strchr (this_arg, '=');
 
 
-       if (*old_name == '\0')
-         {
-           as_warn (_("ignoring extra '-rename-section' delimiter ':'"));
-           continue;
-         }
-       if (!new_name || new_name[1] == '\0')
-         {
-           as_warn (_("ignoring invalid '-rename-section' "
-                      "specification: '%s'"), old_name);
-           continue;
-         }
-       *new_name = '\0';
-       new_name++;
-       add_section_rename (old_name, new_name);
-      }
-    }
-}
+      old_name = this_arg;
+      new_name = strchr (this_arg, '=');
 
 
+      if (*old_name == '\0')
+       {
+         as_warn (_("ignoring extra '-rename-section' delimiter ':'"));
+         continue;
+       }
+      if (!new_name || new_name[1] == '\0')
+       {
+         as_warn (_("ignoring invalid '-rename-section' specification: '%s'"),
+                  old_name);
+         continue;
+       }
+      *new_name = '\0';
+      new_name++;
 
 
-static void
-add_section_rename (old_name, new_name)
-     char *old_name;
-     char *new_name;
-{
-  struct rename_section_struct *r = section_rename;
+      /* Check for invalid section renaming.  */
+      for (r = section_rename; r != NULL; r = r->next)
+       {
+         if (strcmp (r->old_name, old_name) == 0)
+           as_bad (_("section %s renamed multiple times"), old_name);
+         if (strcmp (r->new_name, new_name) == 0)
+           as_bad (_("multiple sections remapped to output section %s"),
+                   new_name);
+       }
 
 
-  /* Check for invalid section renaming.  */
-  for (r = section_rename; r != NULL; r = r->next)
-    {
-      if (strcmp (r->old_name, old_name) == 0)
-       as_bad (_("section %s renamed multiple times"), old_name);
-      if (strcmp (r->new_name, new_name) == 0)
-       as_bad (_("multiple sections remapped to output section %s"),
-               new_name);
+      /* Now add it.  */
+      r = (struct rename_section_struct *)
+       xmalloc (sizeof (struct rename_section_struct));
+      r->old_name = xstrdup (old_name);
+      r->new_name = xstrdup (new_name);
+      r->next = section_rename;
+      section_rename = r;
     }
     }
-
-  /* Now add it.  */
-  r = (struct rename_section_struct *)
-    xmalloc (sizeof (struct rename_section_struct));
-  r->old_name = strdup (old_name);
-  r->new_name = strdup (new_name);
-  r->next = section_rename;
-  section_rename = r;
 }
 
 
 }
 
 
-const char *
-xtensa_section_rename (name)
-     const char *name;
+char *
+xtensa_section_rename (char *name)
 {
   struct rename_section_struct *r = section_rename;
 
   for (r = section_rename; r != NULL; r = r->next)
 {
   struct rename_section_struct *r = section_rename;
 
   for (r = section_rename; r != NULL; r = r->next)
-    if (strcmp (r->old_name, name) == 0)
-      return r->new_name;
+    {
+      if (strcmp (r->old_name, name) == 0)
+       return r->new_name;
+    }
 
   return name;
 }
 
   return name;
 }
-
-#endif /* XTENSA_SECTION_RENAME */
This page took 0.179029 seconds and 4 git commands to generate.