* config/tc-xtensa.c (xg_assembly_relax): Comment termination rules.
[deliverable/binutils-gdb.git] / gas / config / tc-xtensa.c
index 9e0349e3837f665114d88803231a1fa2f2b0f2c0..aef6b65e77bff99c6627d2fe34dd5a4e54ece9f4 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
 
 
    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 "frags.h"
 #include "subsegs.h"
 #include "xtensa-relax.h"
 #include "xtensa-istack.h"
+#include "dwarf2dbg.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
 
 /* 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
@@ -70,17 +68,25 @@ const char EXP_CHARS[] = "eE";
 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;
-#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
@@ -93,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")
-#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 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
 {
-  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 init_lit_seg;
-  segT fini_lit_seg;
+  segT lit4_seg;
 } 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
 {
@@ -127,15 +135,169 @@ typedef struct seg_list_struct
 
 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
+   instruction, so that we can adjust the symbols when inserting alignment
+   for various instructions.  We also keep a list of all the symbols on
+   literals, so that we can fix up those symbols when the literals are
+   later moved into the text sections.  */
+
+typedef struct sym_list_struct
+{
+  struct sym_list_struct *next;
+  symbolS *sym;
+} sym_list;
+
+static sym_list *insn_labels = NULL;
+static sym_list *free_insn_labels = NULL;
+static sym_list *saved_insn_labels = NULL;
+
+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;
 
+/* 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
@@ -147,6 +309,71 @@ typedef struct emit_state_struct
 } 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
@@ -154,11 +381,13 @@ typedef enum
   directive_none = 0,
   directive_literal,
   directive_density,
-  directive_generics,
-  directive_relax,
+  directive_transform,
   directive_freeregs,
   directive_longcalls,
-  directive_literal_prefix
+  directive_literal_prefix,
+  directive_schedule,
+  directive_absolute_literals,
+  directive_last_directive
 } directiveE;
 
 typedef struct
@@ -169,551 +398,142 @@ typedef struct
 
 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 */
-#if STATIC_LIBISA && !XCHAL_HAVE_DENSITY
+#if !XCHAL_HAVE_DENSITY
   FALSE,                       /* density */
 #else
   TRUE,                                /* density */
 #endif
-  TRUE,                                /* generics */
-  TRUE,                                /* relax */
+  TRUE,                                /* transform */
   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.  */
 
-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 *));
-
-/* Functions for Internal Lists of Symbols.  */
-static void xtensa_define_label
-  PARAMS ((symbolS *));
-static void add_target_symbol
-  PARAMS ((symbolS *, bfd_boolean));
-static symbolS *xtensa_find_label
-  PARAMS ((fragS *, offsetT, bfd_boolean));
-static void map_over_defined_symbols
-  PARAMS ((void (*fn) (symbolS *)));
-static bfd_boolean is_loop_target_label
-  PARAMS ((symbolS *));
-static void xtensa_mark_target_fragments
-  PARAMS ((void));
+static bfd_reloc_code_real_type xtensa_elf_suffix (char **, expressionS *);
 
 /* 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 symbolS *xtensa_create_local_symbol
-  PARAMS ((bfd *, const char *, segT, valueT, fragS *));
-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 ((bfd_boolean));
-static void xtensa_move_labels
-  PARAMS ((fragS *, valueT, fragS *, valueT));
-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.  */
 
-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().  */
 
-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 xtensa_move_frag_symbol
-  PARAMS ((symbolS *));
-static void xtensa_move_frag_symbols
-  PARAMS ((void));
-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 ((bfd *, 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.  */
-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.  */
-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.  */
@@ -731,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_const16_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_j_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_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_rsr_opcode;
+static xtensa_opcode xtensa_rsr_lcount_opcode;
 static xtensa_opcode xtensa_waiti_opcode;
 
 \f
@@ -752,34 +576,43 @@ static xtensa_opcode xtensa_waiti_opcode;
 
 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 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
 {
@@ -789,17 +622,25 @@ enum
   option_relax,
   option_no_relax,
 
+  option_link_relax,
+  option_no_link_relax,
+
   option_generics,
   option_no_generics,
 
+  option_transform,
+  option_no_transform,
+
   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_only_targets,
-  option_no_align_only_targets,
+  option_warn_unaligned_targets,
 
   option_longcalls,
   option_no_longcalls,
@@ -821,119 +662,115 @@ enum
 
   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,
-#endif
 
-  option_eb,
-  option_el
+  option_prefer_l32r,
+  option_prefer_const16,
+
+  option_target_hardware
 };
 
 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.  */
-  {"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
-md_parse_option (c, arg)
-     int c;
-     char *arg;
+md_parse_option (int c, char *arg)
 {
   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:
-      directive_state[directive_density] = FALSE;
+      as_warn (_("--no-density option is ignored"));
       return 1;
-    case option_generics:
-      directive_state[directive_generics] = TRUE;
+    case option_link_relax:
+      linkrelax = 1;
       return 1;
-    case option_no_generics:
-      directive_state[directive_generics] = FALSE;
+    case option_no_link_relax:
+      linkrelax = 0;
       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;
@@ -946,63 +783,60 @@ md_parse_option (c, arg)
     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;
-      software_a0_b_retw_interlock = TRUE;
       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;
-      software_avoid_b_j_loop_end = TRUE;
       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;
-      software_avoid_short_loop = TRUE;
       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;
-      software_avoid_all_short_loops = TRUE;
       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;
-      software_avoid_close_loop_end = TRUE;
       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;
-      software_a0_b_retw_interlock = FALSE;
       workaround_b_j_loop_end = FALSE;
-      software_avoid_b_j_loop_end = FALSE;
       workaround_short_loop = FALSE;
-      software_avoid_short_loop = FALSE;
       workaround_all_short_loops = FALSE;
-      software_avoid_all_short_loops = FALSE;
       workaround_close_loop_end = FALSE;
-      software_avoid_close_loop_end = FALSE;
       return 1;
-      
+
     case option_align_targets:
       align_targets = TRUE;
       return 1;
@@ -1010,48 +844,69 @@ md_parse_option (c, arg)
       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;
 
-#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;
 
-    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;
 
-    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;
 
-    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;
 
-    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;
-#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;
-      
+
     default:
       return 0;
     }
@@ -1059,28 +914,65 @@ md_parse_option (c, arg)
 
 
 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
@@ -1101,50 +993,43 @@ state_stackS *directive_state_stack;
 
 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
-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;
@@ -1164,13 +1049,13 @@ directive_push (directive, negated, datum)
   directive_state[directive] = !negated;
 }
 
+
 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;
 
@@ -1193,7 +1078,7 @@ directive_pop (directive, negated, file, line, datum)
 
 
 static void
-directive_balance ()
+directive_balance (void)
 {
   while (directive_state_stack)
     {
@@ -1211,8 +1096,7 @@ directive_balance ()
 
 
 static bfd_boolean
-inside_directive (dir)
-     directiveE dir;
+inside_directive (directiveE dir)
 {
   state_stackS *top = directive_state_stack;
 
@@ -1224,12 +1108,11 @@ inside_directive (dir)
 
 
 static void
-get_directive (directive, negated)
-     directiveE *directive;
-     bfd_boolean *negated;
+get_directive (directiveE *directive, bfd_boolean *negated)
 {
   int len;
   unsigned i;
+  char *directive_string;
 
   if (strncmp (input_line_pointer, "no-", 3) != 0)
     *negated = FALSE;
@@ -1240,16 +1123,33 @@ get_directive (directive, negated)
     }
 
   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)
     {
-      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)
-           as_bad (_("directive %s can't be negated"),
+           as_bad (_("directive %s cannot be negated"),
                    directive_info[i].name);
          return;
        }
@@ -1261,13 +1161,11 @@ get_directive (directive, negated)
 
 
 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;
-  int len;
   lit_state *ls;
 
   get_directive (&directive, &negated);
@@ -1277,18 +1175,32 @@ xtensa_begin_directive (ignore)
       return;
     }
 
+  if (cur_vinsn.inside_bundle)
+    as_bad (_("directives are not valid inside bundles"));
+
   switch (directive)
     {
     case directive_literal:
+      if (!inside_directive (directive_literal))
+       {
+         /* Previous labels go with whatever follows this directive, not with
+            the literal, so save them now.  */
+         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:
+      /* 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.  */
-      if (frag_now->tc_frag_data.is_literal)
+      if (generating_literals)
        {
          as_bad (_("cannot set literal_prefix inside literal fragment"));
          return;
@@ -1300,20 +1212,10 @@ xtensa_begin_directive (ignore)
       assert (ls);
 
       *ls = default_lit_sections;
-
       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.  */
-      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:
@@ -1324,15 +1226,32 @@ xtensa_begin_directive (ignore)
       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:
-      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;
        }
-      /* fall through */
+      xtensa_set_frag_assembly_state (frag_now);
+      directive_push (directive, negated, 0);
+      break;
 
     default:
+      md_flush_pending_output ();
+      xtensa_set_frag_assembly_state (frag_now);
       directive_push (directive, negated, 0);
       break;
     }
@@ -1342,32 +1261,50 @@ xtensa_begin_directive (ignore)
 
 
 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;
+  emit_state **state_ptr;
   lit_state *s;
 
+  if (cur_vinsn.inside_bundle)
+    as_bad (_("directives are not valid inside bundles"));
+
   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;
-    }
 
-  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 ();
-      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,
-                (const void **) &state);
+                (const void **) state_ptr);
 
   if (begin_directive != directive_none)
     {
@@ -1384,25 +1321,33 @@ xtensa_end_directive (ignore)
            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);
-             break;
-
-           case directive_freeregs:
+             if (!inside_directive (directive_literal))
+               {
+                 /* Restore the list of current labels.  */
+                 xtensa_clear_insn_labels ();
+                 insn_labels = saved_insn_labels;
+               }
              break;
 
            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;
 
+           case directive_schedule:
+           case directive_freeregs:
+             break;
+
            default:
+             xtensa_set_frag_assembly_state (frag_now);
              break;
            }
        }
@@ -1415,30 +1360,43 @@ xtensa_end_directive (ignore)
 /* 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"));
-  else if (!use_literal_section)
-    xtensa_mark_literal_pool_location (FALSE);
+  xtensa_mark_literal_pool_location ();
 
   demand_empty_rest_of_line ();
+  xtensa_clear_insn_labels ();
 }
 
 
-/* Support .literal label, value@plt + offset.  */
+/* Support .literal label, expr, ...  */
 
 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;
-  expressionS expP;
   segT dest_seg;
 
+  if (inside_directive (directive_literal))
+    {
+      as_bad (_(".literal not allowed inside .begin literal region"));
+      ignore_rest_of_line ();
+      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;
+  insn_labels = NULL;
+
   /* If we are using text-section literals, then this is the right value... */
   dest_seg = now_seg;
 
@@ -1446,14 +1404,14 @@ xtensa_literal_pseudo (ignored)
 
   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.  */
-  if (use_literal_section)
+  if (use_literal_section || directive_state[directive_absolute_literals])
     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'.  */
@@ -1473,85 +1431,251 @@ xtensa_literal_pseudo (ignored)
 
   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;
+  input_line_pointer++;                /* skip ',' or ':' */
 
-  demand_empty_rest_of_line ();
+  xtensa_elf_cons (4);
 
   xtensa_restore_emit_state (&state);
+
+  /* Restore the list of current labels.  */
+  xtensa_clear_insn_labels ();
+  insn_labels = saved_insn_labels;
 }
 
 
 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);
-
-  strncpy (name, start, len);
+  strncpy (name, input_line_pointer, len);
   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");
-
-  /* Note that retrieve_literal_seg does not create a segment if 
-     it already exists.  */
-  default_lit_sections.lit_seg = NULL; /* retrieved on demand */
+  /* Skip the name in the input line.  */
+  input_line_pointer += len;
 
-  /* Canonicalizing section names allows renaming literal
-     sections to occur correctly.  */
-  default_lit_sections.lit_seg_name =
-    tc_canonicalize_symbol_name (newname);
+  default_lit_sections.lit_prefix = name;
 
-  free (name);
-
-  /* Restore the current section and subsection and set the 
-     generation into the old segment.  */
-  subseg_set (s_now, ss_now);
+  /* Clear cached literal sections, since the prefix has changed.  */
+  default_lit_sections.lit_seg = NULL;
+  default_lit_sections.lit4_seg = NULL;
 }
 
-\f
-/* Parsing and Idiom Translation.  */
 
-static const char *
-expression_end (name)
-     const char *name;
+/* Support ".frequency branch_target_frequency fall_through_frequency".  */
+
+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.  */
+
+/* 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 *
+expression_end (const char *name)
 {
   while (1)
     {
       switch (*name)
        {
+       case '}':
        case ';':
        case '\0':
        case ',':
+       case ':':
          return name;
        case ' ':
        case '\t':
@@ -1567,8 +1691,7 @@ expression_end (name)
 #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;
@@ -1622,45 +1745,62 @@ tc_get_register (prefix)
 }
 
 
-#define PLT_SUFFIX "@PLT"
-#define plt_suffix "@plt"
-
 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);
-      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;
        }
-      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))
        {
-         tok->X_add_symbol->sy_tc.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
     {
-      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 ((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"));
        }
 
@@ -1674,18 +1814,17 @@ expression_maybe_register (opnd, tok)
 /* 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;
+  bfd_boolean saw_colon = FALSE;
   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;
 
@@ -1695,110 +1834,239 @@ tokenize_arguments (args, str)
       switch (*input_line_pointer)
        {
        case '\0':
+       case '}':
          goto fini;
 
+       case ':':
+         input_line_pointer++;
+         if (saw_comma || saw_colon || !saw_arg)
+           goto err;
+         saw_colon = TRUE;
+         break;
+
        case ',':
          input_line_pointer++;
-         if (saw_comma || !saw_arg)
+         if (saw_comma || saw_colon || !saw_arg)
            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;
+
          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;
 
+         if (saw_colon)
+           *arg++ = ':';
          strncpy (arg, input_line_pointer, arg_len);
          arg[arg_len] = '\0';
+
          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:
-  if (saw_comma)
+  if (saw_comma || saw_colon)
     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;
 }
 
 
-/* Parse the arguments to an opcode.  Return true on error.  */
+/* Parse the arguments to an opcode.  Return TRUE on error.  */
 
 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_isa isa = xtensa_default_isa; 
-  int n;
+  xtensa_isa isa = xtensa_default_isa;
+  int n, num_regs = 0;
   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
-    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.  */
-  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++)
-    { 
+    {
       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);
+
+         expression_maybe_register (opcode, opnd_cnt, tok);
+         next_reg = tok->X_add_number + 1;
+
+         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;
 
-      if (actual_operand_count >= opcode_operand_count)
-       { 
-         as_warn (_("too many arguments")); 
-         goto err;
-       } 
-      assert (actual_operand_count < MAX_INSN_ARGS);
+         last_tok = tok;
+         last_opnd_cnt = opnd_cnt;
 
-      opnd = xtensa_get_operand (isa, opcode, actual_operand_count); 
-      expression_maybe_register (opnd, tok);
+         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;
-  had_error = FALSE; 
+  had_error = FALSE;
 
  err:
-  input_line_pointer = old_input_line_pointer; 
+  input_line_pointer = old_input_line_pointer;
   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
-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;
@@ -1816,9 +2084,7 @@ xg_reverse_shift_count (cnt_argp)
    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;
@@ -1838,9 +2104,7 @@ xg_arg_is_constant (arg, valp)
 
 
 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);
@@ -1849,15 +2113,14 @@ xg_replace_opname (popname, newop)
 
 
 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;
 
-  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);
@@ -1881,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
-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;
-  offsetT val;
-  bfd_boolean has_underbar = FALSE;
+  const char *sr_name;
+  int is_user, is_write;
 
   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;
     }
 
-  /* Opname == [rw]ur... */
-
-  if (opname[3] == '\0')
+  sr = xtensa_sysreg_lookup_name (isa, opname + 1);
+  if (sr != XTENSA_UNDEFINED)
+    {
+      /* 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
     {
-      /* If the register is not specified as part of the opcode,
-        then get it from the operand and move it to the opcode.  */
+      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))
+      sr = xtensa_sysreg_lookup (isa, val, 1);
+      if (sr == XTENSA_UNDEFINED)
        {
-         as_bad (_("register number for `%s' is not a constant"), opname);
-         return -1;
-       }
-      if ((unsigned) val > 255)
-       {
-         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;
        }
+    }
 
-      /* 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;
+
+  opname = *popname;
+  assert (opname[0] != '_');
+
+  if (strcmp (opname, old_op) != 0)
+    return 0;
 
-      /* 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;
+  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;
@@ -1941,10 +2308,7 @@ xg_translate_sysreg_op (popname, pnum_args, arg_strings)
    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;
@@ -1957,7 +2321,7 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
 
   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
        {
@@ -1991,9 +2355,14 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
       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
        {
@@ -2011,774 +2380,606 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
       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);
 
+  /* 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
+  /* 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;
 
-     questionable relaxations (widening) from old "tai" idioms:
+      if (xtensa_translate_zero_immed ("beqi", "beqz", popname,
+                                      pnum_args, arg_strings))
+       return -1;
 
-       ADD.N --> ADD
-       BEQZ.N --> BEQZ
-       RET.N --> RET
-       RETW.N --> RETW
-       MOVI.N --> MOVI
-       MOV.N --> MOV
-       NOP.N --> NOP
+      if (xtensa_translate_zero_immed ("bgei", "bgez", 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 ("blti", "bltz", 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.
+  return 0;
+}
 
-     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.  */
+\f
+/* Functions for dealing with the Xtensa ISA.  */
 
-  if (use_generics ()
-      && !has_underbar
-      && density_supported
-      && !code_density_available ())
-    {
-      if (strcmp (opname, "add.n") == 0)
-       xg_replace_opname (popname, "add");
+/* 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, "beqz.n") == 0)
-       xg_replace_opname (popname, "beqz");
+static int
+get_relaxable_immed (xtensa_opcode opcode)
+{
+  int last_immed = -1;
+  int noperands, opi;
 
-      else if (strcmp (opname, "ret.n") == 0)
-       xg_replace_opname (popname, "ret");
+  if (opcode == XTENSA_UNDEFINED)
+    return -1;
 
-      else if (strcmp (opname, "retw.n") == 0)
-       xg_replace_opname (popname, "retw");
-
-      else if (strcmp (opname, "movi.n") == 0)
-       xg_replace_opname (popname, "movi");
-
-      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--)
     {
-      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;
-      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;
 }
 
 
-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;
-  xtensa_opcode opcode;
+  static xtensa_insnbuf slotbuf = NULL;
   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.  */
+#ifdef TENSILICA_DEBUG
 
-static bfd_boolean
-is_loop_opcode (opcode)
-     xtensa_opcode opcode;
-{
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
+/* For debugging, print out the mapping of opcode numbers to opcodes.  */
 
-  return (opcode == xtensa_loop_opcode
-         || opcode == xtensa_loopnez_opcode
-         || opcode == xtensa_loopgtz_opcode);
-}
-
-
-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
-is_conditional_branch_opcode (opcode)
-     xtensa_opcode opcode;
+is_direct_call_opcode (xtensa_opcode opcode)
 {
   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;
 
-  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 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
-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;
 
-  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
-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.  */
 
-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;
-{
-  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);
-}
-
+/* 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 bfd_boolean
-is_negatable_branch (insn)
-     TInsn *insn;
+xg_instruction_matches_option_term (TInsn *insn ATTRIBUTE_UNUSED,
+                                   const ReqOrOption *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++)
+  if (strcmp (option->option_name, "realnop") == 0
+      || strncmp (option->option_name, "IsaUse", 6) == 0)
     {
-      xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
-      char *kind = xtensa_operand_kind (opnd);
-      if (strlen (kind) == 1 && *kind == 'l')
-       return TRUE;
+      /* These conditions were evaluated statically when building the
+        relaxation table.  There's no need to reevaluate them now.  */
+      return TRUE;
     }
-  return FALSE;
-}
-
-\f
-/* Lists for recording various properties of symbols.  */
-
-typedef struct symbol_consS_struct
-{
-  symbolS *first;
-  /* These are used for the target taken.  */
-  int is_loop_target:1;
-  int is_branch_target:1;
-  int is_literal:1;
-  int is_moved:1;
-  struct symbol_consS_struct *rest;
-} symbol_consS;
-
-symbol_consS *defined_symbols = 0;
-symbol_consS *branch_targets = 0;
-
-
-static void
-xtensa_define_label (sym)
-     symbolS *sym;
-{
-  symbol_consS *cons = (symbol_consS *) xmalloc (sizeof (symbol_consS));
-
-  cons->first = sym;
-  cons->is_branch_target = 0;
-  cons->is_loop_target = 0;
-  cons->is_literal = generating_literals ? 1 : 0;
-  cons->is_moved = 0;
-  cons->rest = defined_symbols;
-  defined_symbols = cons;
-}
-
-
-void
-add_target_symbol (sym, is_loop)
-     symbolS *sym;
-     bfd_boolean is_loop;
-{
-  symbol_consS *cons, *sym_e;
-
-  for (sym_e = branch_targets; sym_e; sym_e = sym_e->rest)
+  else
     {
-      if (sym_e->first == sym)
-       {
-         if (is_loop)
-           sym_e->is_loop_target = 1;
-         else
-           sym_e->is_branch_target = 1;
-         return;
-       }
+      as_fatal (_("internal error: unknown option name '%s'"),
+               option->option_name);
     }
-
-  cons = (symbol_consS *) xmalloc (sizeof (symbol_consS));
-  cons->first = sym;
-  cons->is_branch_target = (is_loop ? 0 : 1);
-  cons->is_loop_target = (is_loop ? 1 : 0);
-  cons->rest = branch_targets;
-  branch_targets = cons;
 }
 
 
-/* Find the symbol at a given position.  (Note: 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.)  */
-
-static symbolS *
-xtensa_find_label (fragP, offset, loops_ok)
-     fragS *fragP;
-     offsetT offset;
-     bfd_boolean loops_ok;
+static bfd_boolean
+xg_instruction_matches_or_options (TInsn *insn,
+                                  const ReqOrOptionList *or_option)
 {
-  symbol_consS *consP;
-
-  for (consP = defined_symbols; consP; consP = consP->rest)
+  const ReqOrOption *option;
+  /* Must match each of the AND terms.  */
+  for (option = or_option; option != NULL; option = option->next)
     {
-      symbolS *symP = consP->first;
-
-      if (S_GET_SEGMENT (symP) == now_seg
-         && symbol_get_frag (symP) == fragP
-         && symbol_constant_p (symP)
-         && S_GET_VALUE (symP) == fragP->fr_address + (unsigned) offset
-         && (loops_ok || !is_loop_target_label (symP)))
-       return symP;
+      if (xg_instruction_matches_option_term (insn, option))
+       return TRUE;
     }
-  return NULL;
-}
-
-
-static void
-map_over_defined_symbols (fn)
-     void (*fn) PARAMS ((symbolS *));
-{
-  symbol_consS *sym_cons;
-
-  for (sym_cons = defined_symbols; sym_cons; sym_cons = sym_cons->rest)
-    fn (sym_cons->first);
+  return FALSE;
 }
 
 
 static bfd_boolean
-is_loop_target_label (sym)
-     symbolS *sym;
+xg_instruction_matches_options (TInsn *insn, const ReqOptionList *options)
 {
-  symbol_consS *sym_e;
-
-  for (sym_e = branch_targets; sym_e; sym_e = sym_e->rest)
+  const ReqOption *req_options;
+  /* Must match each of the AND terms.  */
+  for (req_options = options;
+       req_options != NULL;
+       req_options = req_options->next)
     {
-      if (sym_e->first == sym)
-        return sym_e->is_loop_target;
+      /* Must match one of the OR clauses.  */
+      if (!xg_instruction_matches_or_options (insn,
+                                             req_options->or_option_terms))
+       return FALSE;
     }
-  return FALSE;
-} 
+  return TRUE;
+}
 
 
-/* Walk over all of the symbols that are branch target labels and
-   loop target labels.  Mark the associated fragments for these with
-   the appropriate flags.  */
+/* Return the transition rule that matches or NULL if none matches.  */
 
-static void
-xtensa_mark_target_fragments ()
+static bfd_boolean
+xg_instruction_matches_rule (TInsn *insn, TransitionRule *rule)
 {
-  symbol_consS *sym_e;
+  PreconditionList *condition_l;
+
+  if (rule->opcode != insn->opcode)
+    return FALSE;
 
-  for (sym_e = branch_targets; sym_e; sym_e = sym_e->rest)
+  for (condition_l = rule->conditions;
+       condition_l != NULL;
+       condition_l = condition_l->next)
     {
-      symbolS *sym = sym_e->first;
+      expressionS *exp1;
+      expressionS *exp2;
+      Precondition *cond = condition_l->precond;
 
-      if (symbol_get_frag (sym)
-         && symbol_constant_p (sym)
-         && S_GET_VALUE (sym) == 0)
+      switch (cond->typ)
        {
-         if (sym_e->is_branch_target)
-           symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
-         if (sym_e->is_loop_target)
-           symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
-       }
-    }
-}
+       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))
+           {
+             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;
+               }
+           }
+         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;
 
-\f
-/* Various Other Internal Functions.  */
+       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];
 
-static bfd_boolean
-is_unique_insn_expansion (r)
-     TransitionRule *r;
-{
-  if (!r->to_instr || r->to_instr->next != NULL)
-    return FALSE;
-  if (r->to_instr->typ != INSTR_INSTR)
-    return FALSE;
-  return TRUE;
-}
+         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;
+       }
+    }
+  if (!xg_instruction_matches_options (insn, rule->options))
+    return FALSE;
 
-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 TRUE;
 }
 
 
 static int
-xg_get_build_instr_size (insn)
-     BuildInstr *insn;
+transition_rule_cmp (const TransitionRule *a, const TransitionRule *b)
 {
-  assert (insn->typ == INSTR_INSTR);
-  return xtensa_insn_length (xtensa_default_isa, insn->opcode);
-}
+  bfd_boolean a_greater = FALSE;
+  bfd_boolean b_greater = FALSE;
 
+  ReqOptionList *l_a = a->options;
+  ReqOptionList *l_b = b->options;
 
-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);
+  /* We only care if they both are the same except for
+     a const16 vs. an l32r.  */
 
-  for (l = table->table[insn->opcode]; l != NULL; l = l->next)
+  while (l_a && l_b && ((l_a->next == NULL) == (l_b->next == NULL)))
     {
-      TransitionRule *rule = l->rule;
-
-      if (xg_instruction_matches_rule (insn, rule)
-         && is_unique_insn_expansion (rule))
+      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)))
        {
-         /* 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))
+         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;
 }
 
 
-bfd_boolean
-xg_is_single_relaxable_insn (insn)
-     TInsn *insn;
+static TransitionRule *
+xg_instruction_match (TInsn *insn)
 {
-  TransitionTable *table = xg_build_widen_table ();
+  TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
   TransitionList *l;
-  int num_match = 0;
-  assert (insn->insn_type == ITYPE_INSN);
   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)
-         && is_unique_insn_expansion (rule))
-       {
-         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))
-           {
-             num_match++;
-             if (num_match > 1)
-               return FALSE;
-           }
-       }
+      if (xg_instruction_matches_rule (insn, rule))
+       return rule;
     }
-  return (num_match == 1);
+  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;
+}
 
-/* 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.  */
 
-int
-xg_get_max_narrow_insn_size (opcode)
-     xtensa_opcode opcode;
+/* 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;
-  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;
 
-      /* 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.  */
 
-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;
-  int max_size = xtensa_insn_length (xtensa_default_isa, opcode);
+  int max_size = xg_get_single_size (opcode);
 
   assert (opcode < table->num_opcodes);
 
@@ -2802,9 +3003,7 @@ xg_get_max_insn_widen_size (opcode)
            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:
@@ -2821,11 +3020,10 @@ xg_get_max_insn_widen_size (opcode)
 
 /* 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;
 
@@ -2851,7 +3049,7 @@ xg_get_max_insn_widen_literal_size (opcode)
            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:
@@ -2867,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;
-  TransitionTable *table = xg_build_widen_table ();
+  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
   TransitionList *l;
 
   assert (insn->insn_type == ITYPE_INSN);
@@ -2895,7 +3091,7 @@ xg_is_relaxable_insn (insn, lateral_steps)
 
 
 static symbolS *
-get_special_literal_symbol ()
+get_special_literal_symbol (void)
 {
   static symbolS *sym = NULL;
 
@@ -2906,7 +3102,7 @@ get_special_literal_symbol ()
 
 
 static symbolS *
-get_special_label_symbol ()
+get_special_label_symbol (void)
 {
   static symbolS *sym = NULL;
 
@@ -2916,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;
+    }
+}
 
-bfd_boolean
-xg_build_to_insn (targ, insn, bi)
-     TInsn *targ;
-     TInsn *insn;
-     BuildInstr *bi;
+
+/* 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 (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;
 
-  memset (targ, 0, sizeof (TInsn));
+  tinsn_init (targ);
+  targ->linenum = insn->linenum;
   switch (bi->typ)
     {
     case INSTR_INSTR:
@@ -2963,9 +3323,41 @@ xg_build_to_insn (targ, insn, bi)
              sym = get_special_label_symbol ();
              set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
              break;
-           default:
-             /* currently handles:
-                OP_OPERAND_LOW8
+           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
                 OP_OPERAND_HI24S
                 OP_OPERAND_F32MINUS */
              if (xg_has_userdef_op_fn (op->typ))
@@ -3008,6 +3400,9 @@ xg_build_to_insn (targ, insn, bi)
            {
            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:
@@ -3025,7 +3420,7 @@ xg_build_to_insn (targ, insn, bi)
       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;
 
@@ -3037,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)
     {
@@ -3056,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;
-  TransitionTable *table = xg_build_widen_table ();
+  TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
   TransitionList *l;
 
   assert (insn->insn_type == ITYPE_INSN);
@@ -3107,246 +3496,50 @@ xg_expand_to_stack (istack, insn, lateral_steps)
   return FALSE;
 }
 
-
-bfd_boolean
-xg_expand_narrow (targ, insn)
-     TInsn *targ;
-     TInsn *insn;
-{
-  TransitionTable *table = xg_build_widen_table ();
-  TransitionList *l;
-
-  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.  */
+   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.  */
 
-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.  */
+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 */
 {
   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;
@@ -3359,26 +3552,22 @@ xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
       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))
        {
-         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.  */
@@ -3410,8 +3599,7 @@ xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
 
 
 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"
@@ -3420,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
@@ -3433,6 +3621,7 @@ xg_finish_frag (last_insn, state, max_growth, is_insn)
      beginning of the growth area.  */
 
   fragS *old_frag;
+
   xg_force_frag_space (max_growth);
 
   old_frag = frag_now;
@@ -3442,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,
-           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);
 }
 
 
+/* 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
-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;
-  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;
 
-  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++)
     {
-      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;
@@ -3494,7 +3709,7 @@ is_branch_jmp_to_next (insn, fragP)
   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;
 
@@ -3503,182 +3718,89 @@ is_branch_jmp_to_next (insn, fragP)
 
 
 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;
-  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;
-      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)
-         add_target_symbol (insn->tok[i].X_add_symbol, 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;
     }
 
-  /* 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++)
        {
-         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)
-           add_target_symbol (insn->tok[i].X_add_symbol, FALSE);
+           {
+             symbolS *sym = insn->tok[i].X_add_symbol;
+             symbol_get_tc (sym)->is_branch_target = TRUE;
+             if (S_IS_DEFINED (sym))
+               symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
+           }
        }
     }
 }
 
 
-/* Return the transition rule that matches or NULL if none matches.  */
+/* Return FALSE if no error.  */
 
-bfd_boolean
-xg_instruction_matches_rule (insn, rule)
-     TInsn *insn;
-     TransitionRule *rule;
+static bfd_boolean
+xg_build_token_insn (BuildInstr *instr_spec, TInsn *old_insn, TInsn *new_insn)
 {
-  PreconditionList *condition_l;
+  int num_ops = 0;
+  BuildOp *b_op;
 
-  if (rule->opcode != insn->opcode)
-    return FALSE;
+  switch (instr_spec->typ)
+    {
+    case INSTR_INSTR:
+      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;
+      new_insn->linenum = old_insn->linenum;
+      break;
+    case INSTR_LABEL_DEF:
+      as_bad (_("INSTR_LABEL_DEF not supported yet"));
+      break;
+    }
 
-  for (condition_l = rule->conditions;
-       condition_l != NULL;
-       condition_l = condition_l->next)
+  for (b_op = instr_spec->ops; b_op != NULL; b_op = b_op->next)
     {
-      expressionS *exp1;
-      expressionS *exp2;
-      Precondition *cond = condition_l->precond;
+      expressionS *exp;
+      const expressionS *src_exp;
 
-      switch (cond->typ)
+      num_ops++;
+      switch (b_op->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;
-           }
+         assert (b_op->op_num < MAX_INSN_ARGS);
+         exp = &new_insn->tok[b_op->op_num];
+         set_expr_const (exp, b_op->op_data);
          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.  */
-
-bfd_boolean
-xg_build_token_insn (instr_spec, old_insn, new_insn)
-     BuildInstr *instr_spec;
-     TInsn *old_insn;
-     TInsn *new_insn;
-{
-  int num_ops = 0;
-  BuildOp *b_op;
-
-  switch (instr_spec->typ)
-    {
-    case INSTR_INSTR:
-      new_insn->insn_type = ITYPE_INSN;
-      new_insn->opcode = instr_spec->opcode;
-      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;
-      break;
-    case INSTR_LABEL_DEF:
-      as_bad (_("INSTR_LABEL_DEF not supported yet"));
-      break;
-    }
-
-  for (b_op = instr_spec->ops; b_op != NULL; b_op = b_op->next)
-    {
-      expressionS *exp;
-      const expressionS *src_exp;
-
-      num_ops++;
-      switch (b_op->typ)
-       {
-       case OP_CONSTANT:
-         /* The expression must be the constant.  */
-         assert (b_op->op_num < MAX_INSN_ARGS);
-         exp = &new_insn->tok[b_op->op_num];
-         set_expr_const (exp, b_op->op_data);
-         break;
-
-       case OP_OPERAND:
-         assert (b_op->op_num < MAX_INSN_ARGS);
-         assert (b_op->op_data < (unsigned) old_insn->ntok);
-         src_exp = &old_insn->tok[b_op->op_data];
-         exp = &new_insn->tok[b_op->op_num];
-         copy_expr (exp, src_exp);
-         break;
+         assert (b_op->op_num < MAX_INSN_ARGS);
+         assert (b_op->op_data < (unsigned) old_insn->ntok);
+         src_exp = &old_insn->tok[b_op->op_data];
+         exp = &new_insn->tok[b_op->op_num];
+         copy_expr (exp, src_exp);
+         break;
 
        case OP_LITERAL:
        case OP_LABEL:
@@ -3696,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;
+
+  if (old_insn->is_specific_opcode || !density_supported)
+    return FALSE;
+
+  rule = xg_instruction_match (old_insn);
   if (rule == NULL)
     return FALSE;
 
@@ -3723,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
-   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
-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;
-  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);
+
+  /* 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_num_operands (xtensa_default_isa, orig_insn->opcode);
+  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"),
@@ -3759,98 +3881,173 @@ xg_expand_assembly_insn (istack, orig_insn)
             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.  */
-
   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 (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_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
-    {
-      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.  */
 
-symbolS *
-xg_assemble_literal (insn)
-     /* const */ TInsn *insn;
+static symbolS *
+xg_assemble_literal (/* const */ TInsn *insn)
 {
   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;
+  expressionS * emit_val;
+
   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);
 
+  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);
+  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;
-  frag_now->tc_frag_data.is_literal = TRUE;
 
   /* Go back.  */
   xtensa_restore_emit_state (&state);
@@ -3859,492 +4056,203 @@ xg_assemble_literal (insn)
 
 
 static void
-xg_assemble_literal_space (size)
-     /* const */ int size;
+xg_assemble_literal_space (/* const */ int size, int slot)
 {
   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;
 
-  expressionS saved_loc;
-
   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);
+  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);
-  frag_now->tc_frag_data.is_literal = TRUE;
   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);
-  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 *fragSym;
+  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);
-  fragSym = xtensa_create_local_symbol (stdoutput, name, sec, 0, frag_now);
+  reloc = BFD_RELOC_NONE;
 
-  frag->tc_frag_data.is_literal = TRUE;
-  lit_num++;
-  return fragSym;
-}
+  /* First try the special cases for "alternate" relocs.  */
+  if (opcode == xtensa_l32r_opcode)
+    {
+      if (fragP->tc_frag_data.use_absolute_literals)
+       reloc = encode_alt_reloc (slot);
+    }
+  else if (opcode == xtensa_const16_opcode)
+    {
+      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;
+       }
+    }
 
+  if (opnum != get_relaxable_immed (opcode))
+    {
+      as_bad (_("invalid relocation for operand %i of '%s'"),
+             opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
+      return FALSE;
+    }
 
-/* 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.  */
+  /* 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)
+    {
+      as_bad (_("invalid expression for operand %i of '%s'"),
+             opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
+      return FALSE;
+    }
 
-symbolS *
-xtensa_create_local_symbol (abfd, name, sec, value, frag)
-     bfd *abfd;
-     const char *name;
-     segT sec;
-     valueT value;
-     fragS *frag;
-{
-  symbolS *symbolP;
+  /* Next try the generic relocs.  */
+  if (reloc == BFD_RELOC_NONE)
+    reloc = encode_reloc (slot);
+  if (reloc == BFD_RELOC_NONE)
+    {
+      as_bad (_("invalid relocation in instruction slot %i"), slot);
+      return FALSE;
+    }
 
-  if (get_is_linkonce_section (abfd, sec))
+  howto = bfd_reloc_type_lookup (stdoutput, reloc);
+  if (!howto)
     {
-      symbolP = symbol_new (name, sec, value, frag);
-      S_CLEAR_EXTERNAL (symbolP);
-      /* symbolP->local = 1; */
+      as_bad (_("undefined symbol for opcode \"%s\""),
+             xtensa_opcode_name (xtensa_default_isa, opcode));
+      return FALSE;
     }
-  else
-    symbolP = symbol_new (name, sec, value, frag);
 
-  return symbolP;
-}
+  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;
+}
 
-/* 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;
+static bfd_boolean
+xg_emit_insn_to_buf (TInsn *tinsn,
+                    char *buf,
+                    fragS *fragP,
+                    offsetT offset,
+                    bfd_boolean build_fix)
 {
-  flagword flags, link_once_flags;
+  static xtensa_insnbuf insnbuf = NULL;
+  bfd_boolean has_symbolic_immed = FALSE;
+  bfd_boolean ok = TRUE;
 
-  flags = bfd_get_section_flags (abfd, sec);
-  link_once_flags = (flags & SEC_LINK_ONCE);
+  if (!insnbuf)
+    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
 
-  /* Flags might not be set yet.  */
-  if (!link_once_flags)
+  has_symbolic_immed = tinsn_to_insnbuf (tinsn, insnbuf);
+  if (has_symbolic_immed && build_fix)
     {
-      static size_t len = sizeof ".gnu.linkonce.t.";
+      /* Add a fixup.  */
+      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 (strncmp (segment_name (sec), ".gnu.linkonce.t.", len - 1) == 0)
-       link_once_flags = SEC_LINK_ONCE;
+      if (!xg_add_opcode_fix (tinsn, opnum, fmt, slot, exp, fragP, offset))
+       ok = FALSE;
     }
-  return (link_once_flags != 0);
+  fragP->tc_frag_data.is_insn = TRUE;
+  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
+                          (unsigned char *) buf, 0);
+  return ok;
 }
 
 
-/* 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;
+static void
+xg_resolve_literals (TInsn *insn, symbolS *lit_sym)
 {
-  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);
+  symbolS *sym = get_special_literal_symbol ();
+  int i;
+  if (lit_sym == 0)
+    return;
+  assert (insn->insn_type == ITYPE_INSN);
+  for (i = 0; i < insn->ntok; i++)
+    if (insn->tok[i].X_add_symbol == sym)
+      insn->tok[i].X_add_symbol = lit_sym;
 
-  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);
+static void
+xg_resolve_labels (TInsn *insn, symbolS *label_sym)
+{
+  symbolS *sym = get_special_label_symbol ();
+  int i;
+  for (i = 0; i < insn->ntok; i++)
+    if (insn->tok[i].X_add_symbol == sym)
+      insn->tok[i].X_add_symbol = label_sym;
 
-  /* This needs to generate a record that lists the parts that are
-     instructions.  */
-  if (!frag_now->tc_frag_data.is_insn)
-    {
-      /* 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;
-    }
+}
 
-  xtensa_insnbuf_to_chars (isa, insnbuf, f);
 
-  /* Now spit out the opcode fixup.... */
-  if (!has_fixup)
-    return !ok;
-
-  for (i = 0; i < noperands; ++i)
-    {
-      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;
-
-       default:
-         as_bad (_("invalid expression for operand %d on '%s'"),
-                 i, xtensa_opcode_name (isa, opcode));
-         ok = FALSE;
-         break;
-       }
-    }
-
-  return !ok;
-}
-
-
-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;
-{
-  static xtensa_insnbuf insnbuf = NULL;
-  bfd_boolean has_symbolic_immed = FALSE;
-  bfd_boolean ok = TRUE;
-  if (!insnbuf)
-    insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
-  has_symbolic_immed = tinsn_to_insnbuf (t_insn, insnbuf);
-  if (has_symbolic_immed && build_fix)
-    {
-      /* Add a fixup.  */
-      int opnum = get_relaxable_immed (t_insn->opcode);
-      expressionS *exp = &t_insn->tok[opnum];
-
-      if (!xg_add_opcode_fix (t_insn->opcode, 
-                             opnum, exp, fragP, offset))
-       ok = FALSE;
-    }
-  fragP->tc_frag_data.is_insn = TRUE;
-  xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
-  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;
-{
-  symbolS *sym = get_special_literal_symbol ();
-  int i;
-  if (lit_sym == 0)
-    return;
-  assert (insn->insn_type == ITYPE_INSN);
-  for (i = 0; i < insn->ntok; i++)
-    if (insn->tok[i].X_add_symbol == sym)
-      insn->tok[i].X_add_symbol = lit_sym;
-
-}
-
-
-void
-xg_resolve_labels (insn, label_sym)
-     TInsn *insn;
-     symbolS *label_sym;
-{
-  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;
-
-}
-
-
-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.  */
-
-  assert (insn->insn_type == ITYPE_INSN);
-
-  if (!tinsn_has_symbolic_operands (insn))
-    {
-      if (xg_is_narrow_insn (insn) && !insn->is_specific_opcode)
-       {
-         /* 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.  */
+/* 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;
+is_register_writer (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);
+  num_ops = xtensa_opcode_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 == '=')
+      char inout;
+      inout = xtensa_operand_inout (isa, insn->opcode, i);
+      if ((inout == 'o' || inout == 'm')
+         && xtensa_operand_is_register (isa, insn->opcode, i) == 1)
        {
-         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))
@@ -4357,8 +4265,7 @@ is_register_writer (insn, regset, regnum)
 
 
 static bfd_boolean
-is_bad_loopend_opcode (tinsn)
-     const TInsn * tinsn;
+is_bad_loopend_opcode (const TInsn *tinsn)
 {
   xtensa_opcode opcode = tinsn->opcode;
 
@@ -4378,14 +4285,8 @@ is_bad_loopend_opcode (tinsn)
       || 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;
@@ -4397,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"}.  */
 
-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;
@@ -4413,7 +4313,7 @@ is_unaligned_label (sym)
   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'
@@ -4425,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;
 
-  /* 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;
@@ -4443,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);
+
+  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;
+  xtensa_format fmt;
+  int fmt_size;
 
   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;
+  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;
 }
 
 
-/* Return true if the target frag is one of the next non-empty frags.  */
+static int
+next_frag_format_size (const fragS *fragP)
+{
+  const fragS *next_fragP = next_non_empty_frag (fragP);
+  return frag_format_size (next_fragP);
+}
 
-bfd_boolean
-is_next_frag_target (fragP, target)
-     const fragS *fragP;
-     const fragS *target;
+
+/* 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.  */
+
+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;
 }
 
 
@@ -4494,10 +4450,34 @@ is_next_frag_target (fragP, target)
    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 *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)
     {
@@ -4507,16 +4487,16 @@ update_next_frag_nop_state (fragP)
          next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP;
          return;
        }
+
       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)
     {
@@ -4530,10 +4510,9 @@ next_frag_is_branch_target (fragP)
 
 
 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)
     {
@@ -4547,17 +4526,16 @@ next_frag_is_loop_target (fragP)
 
 
 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;
+  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;
 
-  /* 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;
 
@@ -4566,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
-     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;
@@ -4579,58 +4558,73 @@ next_frag_pre_opcode_bytes (fragp)
    placed nearest to their use.  */
 
 static void
-xtensa_mark_literal_pool_location (move_labels)
-     bfd_boolean move_labels;
+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 *label_target = frag_now;
   fragS *pool_location;
-  offsetT label_offset = frag_now_fix ();
 
-  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;
-  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);
-  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);
+  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);
-  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);
+  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);
-  if (move_labels)
-    xtensa_move_labels (label_target, label_offset, frag_now, 0);
+  xtensa_set_frag_assembly_state (frag_now);
 }
 
 
+/* Build a nop of the correct size into tinsn.  */
+
 static void
-xtensa_move_labels (old_frag, old_offset, new_frag, new_offset)
-     fragS *old_frag;
-     valueT old_offset;
-     fragS *new_frag ATTRIBUTE_UNUSED;
-     valueT new_offset;
+build_nop (TInsn *tinsn, int size)
 {
-  symbolS *old_sym;
-
-  /* Repeat until there are no more.... */
-  for (old_sym = xtensa_find_label (old_frag, old_offset, TRUE);
-       old_sym;
-       old_sym = xtensa_find_label (old_frag, old_offset, TRUE))
+  tinsn_init (tinsn);
+  switch (size)
     {
-      S_SET_VALUE (old_sym, (valueT) new_offset);
-      symbol_set_frag (old_sym, frag_now);
+    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)
+       {
+         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);
     }
 }
 
@@ -4638,44 +4632,20 @@ xtensa_move_labels (old_frag, old_offset, new_frag, new_offset)
 /* 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;
-  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);
 }
 
 
@@ -4687,14 +4657,12 @@ assemble_nop (size, buf)
    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.  */
-  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)
@@ -4706,78 +4674,331 @@ 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
-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;
 }
 
-\f
-/* External Functions and Other GAS Hooks.  */
-
-const char *
-xtensa_target_format ()
-{
-  return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
-}
 
+/* Set frag assembly state should be called when a new frag is
+   opened and after a frag has been closed.  */
 
-void
-xtensa_file_arch_init (abfd)
-     bfd *abfd;
+static void
+xtensa_set_frag_assembly_state (fragS *fragP)
 {
-  bfd_set_private_flags (abfd, 0x100 | 0x200);
+  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;
 }
 
 
-void
-md_number_to_chars (buf, val, n)
-     char *buf;
-     valueT val;
-     int n;
+static bfd_boolean
+relaxable_section (asection *sec)
 {
-  if (target_big_endian)
-    number_to_chars_bigendian (buf, val, n);
-  else
-    number_to_chars_littleendian (buf, val, n);
+  return (sec->flags & SEC_DEBUGGING) == 0;
 }
 
 
-/* This function is called once, at assembler startup time.  It should
-   set up all the tables, etc. that the MD part of the assembler will
-   need.  */
-
-void
-md_begin ()
+static void
+xtensa_mark_frags_for_org (void)
 {
-  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().  */
-  isa = xtensa_default_isa;
-#endif
-
-  /* Set  up the .literal, .fini.literal and .init.literal 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;
+  segT *seclist;
 
-  subseg_set (current_section, current_subsec);
+  /* 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 *
+xtensa_target_format (void)
+{
+  return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
+}
+
+
+void
+xtensa_file_arch_init (bfd *abfd)
+{
+  bfd_set_private_flags (abfd, 0x100 | 0x200);
+}
+
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
+}
+
+
+/* This function is called once, at assembler startup time.  It should
+   set up all the tables, etc. that the MD part of the assembler will
+   need.  */
+
+void
+md_begin (void)
+{
+  segT current_section = now_seg;
+  int current_subsec = now_subseg;
+  xtensa_isa isa;
+
+  xtensa_default_isa = xtensa_isa_init (0, 0);
+  isa = xtensa_default_isa;
+
+  linkrelax = 1;
+
+  /* Set up the literal sections.  */
+  memset (&default_lit_sections, 0, sizeof (default_lit_sections));
+
+  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");
@@ -4789,97 +5010,221 @@ 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_const16_opcode = xtensa_opcode_lookup (isa, "const16");
   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_j_opcode = xtensa_opcode_lookup (isa, "j");
   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_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_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");
+
+  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
-xtensa_frob_label (sym)
-     symbolS *sym;
+xtensa_frob_label (symbolS *sym)
 {
-  xtensa_define_label (sym);
-  if (is_loop_target_label (sym) 
-      && (get_last_insn_flags (now_seg, now_subseg)
+  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 (symbol_get_tc (sym)->is_loop_target)
+    {
+      if ((get_last_insn_flags (now_seg, now_subseg)
          & 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 && align_targets
-      && !is_unaligned_label (sym))
-    {
-      fragS *old_frag = frag_now;
-      offsetT old_offset = frag_now_fix ();
-      if (frag_now->tc_frag_data.is_literal)
-       return;
-      /* frag_now->tc_frag_data.is_insn = TRUE; */
-      frag_var (rs_machine_dependent, 4, 4,
+  if (now_seg != absolute_section
+      && do_align_targets ()
+      && !is_unaligned_label (sym)
+      && !generating_literals)
+    {
+      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);
-      xtensa_move_labels (old_frag, old_offset, frag_now, 0);
-      /* Once we know whether or not the label is a branch target
-         We will suppress some of these alignments.  */
+      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);
+}
+
+
+/* tc_unrecognized_line hook */
+
+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
-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);
+      xtensa_set_frag_assembly_state (frag_now);
     }
   frag_now->tc_frag_data.is_insn = FALSE;
+
+  xtensa_clear_insn_labels ();
+}
+
+
+/* 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
-md_assemble (str)
-     char *str;
+md_assemble (char *str)
 {
   xtensa_isa isa = xtensa_default_isa;
-  char *opname;
+  char *opname, *file_name;
   unsigned opnamelen;
   bfd_boolean has_underbar = FALSE;
-  char *arg_strings[MAX_INSN_ARGS]; 
+  char *arg_strings[MAX_INSN_ARGS];
   int num_args;
-  IStack istack;               /* Put instructions into here.  */
   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.  */
@@ -4907,52 +5252,38 @@ md_assemble (str)
 
   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)
     {
-      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"));
+      error_reset_cur_vinsn ();
       return;
     }
 
@@ -4962,179 +5293,105 @@ md_assemble (str)
   while (num_args-- > 0)
     free (arg_strings[num_args]);
 
+  /* Get expressions for invisible operands.  */
+  if (get_invisible_operands (&orig_insn))
+    {
+      error_reset_cur_vinsn ();
+      return;
+    }
+
   /* Check for the right number and type of arguments.  */
   if (tinsn_check_arguments (&orig_insn))
-    return;
+    {
+      error_reset_cur_vinsn ();
+      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);
+  /* 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);
 
   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))
+  /* Check that immediate value for ENTRY is >= 16.  */
+  if (orig_insn.opcode == xtensa_entry_opcode && orig_insn.ntok >= 3)
     {
-      fragS *old_frag = frag_now;
-      offsetT old_offset = frag_now_fix ();
-      symbolS *old_sym = NULL;
-      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);
-
-      /* Repeat until there are no more.  */
-      while ((old_sym = xtensa_find_label (old_frag, old_offset, FALSE)))
-       {
-         S_SET_VALUE (old_sym, (valueT) 0);
-         symbol_set_frag (old_sym, frag_now);
-       }
-    }
-
-  /* Special count for "entry" instruction.  */
-  if (is_entry_opcode (orig_insn.opcode))
-    {
-      /* 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 && is_entry_opcode (orig_insn.opcode))
-    {
-      xtensa_mark_literal_pool_location (TRUE);
-
-      /* Automatically align ENTRY instructions.  */
-      frag_align (2, 0, 0);
+      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"));
     }
 
-  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));
-
-  set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
-                      is_bad_loopend_opcode (&orig_insn));
-
   /* 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.  */
-  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
-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 *
-md_undefined_symbol (name)
-     char *name ATTRIBUTE_UNUSED;
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
 {
   return NULL;
 }
@@ -5143,95 +5400,180 @@ md_undefined_symbol (name)
 /* 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
-md_pcrel_from (fixP)
-     fixS *fixP;
+md_pcrel_from (fixS *fixP)
 {
   char *insn_p;
   static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
   int opnum;
-  xtensa_operand operand;
+  uint32 opnd_value;
   xtensa_opcode opcode;
+  xtensa_format fmt;
+  int slot;
   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)
-    return addr;
+    return 0;
 
   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];
-  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);
 
-  opnum = reloc_to_opnum (fixP->fx_r_type);
+  if (fmt == XTENSA_UNDEFINED)
+    as_fatal (_("bad instruction format"));
 
-  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)
+  if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc) != 0)
+    as_fatal (_("invalid relocation"));
+
+  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));
-      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
-xtensa_symbol_new_hook (symbolP)
-     symbolS *symbolP;
+xtensa_elf_section_change_hook (void)
 {
-  symbolP->sy_tc.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
-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)
@@ -5242,389 +5584,1756 @@ xtensa_fix_adjustable (fixP)
 
 
 void
-md_apply_fix3 (fixP, valP, seg)
-     fixS *fixP;
-     valueT *valP;
-     segT seg ATTRIBUTE_UNUSED;
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 {
-  if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
-    {
-      /* 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;
+  valueT val = 0;
 
-      char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
-      static xtensa_insnbuf insnbuf = NULL;
-      xtensa_opcode opcode;
-      xtensa_isa isa;
+  /* 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)
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_32:
+    case BFD_RELOC_16:
+    case BFD_RELOC_8:
+      if (fixP->fx_subsy)
        {
-       case BFD_RELOC_XTENSA_ASM_EXPAND:
-         fixP->fx_done = 1;
-         break;
-
-       case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
-         as_bad (_("unhandled local relocation fix %s"),
-                 bfd_get_reloc_code_name (fixP->fx_r_type));
-         break;
+         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;
+           }
 
-       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);
+         /* 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;
-         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);
+       }
+      /* fall through */
 
-         xtensa_insnbuf_from_chars (isa, insnbuf, fixpos);
-         opcode = xtensa_decode_insn (isa, insnbuf);
-         if (opcode == XTENSA_UNDEFINED)
-           as_fatal (_("undecodable FIX"));
+    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;
 
-         xtensa_insnbuf_set_immediate_field (opcode, insnbuf, *valP,
-                                             fixP->fx_file, fixP->fx_line);
+    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;
 
-         fixP->fx_frag->tc_frag_data.is_insn = TRUE;
-         xtensa_insnbuf_to_chars (isa, insnbuf, fixpos);
-         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;
+    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));
-       }
+    default:
+      as_bad (_("unhandled local relocation fix %s"),
+             bfd_get_reloc_code_name (fixP->fx_r_type));
     }
 }
 
 
 char *
-md_atof (type, litP, sizeP)
-     int type;
-     char *litP;
-     int *sizeP;
+md_atof (int type, char *litP, int *sizeP)
 {
   int prec;
   LITTLENUM_TYPE words[4];
   char *t;
   int i;
 
-  switch (type)
+  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++)
+    {
+      /* See if the instruction implies an aligned section.  */
+      if (xtensa_opcode_is_loop (isa, vinsn->slots[i].opcode) == 1)
+       record_alignment (now_seg, 2);
+
+      /* Also determine the best line number for debug info.  */
+      best_linenum = vinsn->slots[i].linenum < best_linenum
+       ? vinsn->slots[i].linenum : best_linenum;
+    }
+
+  /* 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)
     {
-    case 'f':
-      prec = 2;
-      break;
+      int max_fill;
 
-    case 'd':
-      prec = 4;
-      break;
+      /* 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;
 
-    default:
-      *sizeP = 0;
-      return "bad call to md_atof";
-    }
+      xtensa_set_frag_assembly_state (frag_now);
+      frag_now->tc_frag_data.is_insn = TRUE;
 
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
+      max_fill = get_text_align_max_fill_size
+       (get_text_align_power (xtensa_fetch_width),
+        TRUE, frag_now->tc_frag_data.is_no_density);
 
-  *sizeP = prec * 2;
+      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);
+    }
 
-  for (i = prec - 1; i >= 0; i--)
+  if (vinsn->slots[0].opcode == xtensa_entry_opcode
+      && !vinsn->slots[0].is_specific_opcode)
     {
-      int idx = i;
-      if (target_big_endian)
-       idx = (prec - 1 - i);
-
-      md_number_to_chars (litP, (valueT) words[idx], 2);
-      litP += 2;
+      xtensa_mark_literal_pool_location ();
+      xtensa_move_labels (frag_now, 0);
+      frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
     }
 
-  return NULL;
-}
+  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));
 
+      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);
 
-int
-md_estimate_size_before_relax (fragP, seg)
-     fragS *fragP;
-     segT seg ATTRIBUTE_UNUSED;
-{
-  return fragP->tc_frag_data.text_expansion;
-}
+  insn_size = xtensa_format_length (isa, vinsn->format);
 
+  extra_space = relaxation_requirements (vinsn, &finish_frag);
 
-/* Translate internal representation of relocation info to BFD target
-   format.  */
+  /* vinsn_to_insnbuf will produce the error.  */
+  if (vinsn->format != XTENSA_UNDEFINED)
+    {
+      f = frag_more (insn_size + extra_space);
+      xtensa_set_frag_assembly_state (frag_now);
+      frag_now->tc_frag_data.is_insn = TRUE;
+    }
 
-arelent *
-tc_gen_reloc (section, fixp)
-     asection *section ATTRIBUTE_UNUSED;
-     fixS *fixp;
-{
-  arelent *reloc;
+  vinsn_to_insnbuf (vinsn, f, frag_now, FALSE);
+  if (vinsn->format == XTENSA_UNDEFINED)
+    return;
 
-  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;
+  xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, (unsigned char *) f, 0);
 
-  /* 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);
+  /* 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);
 
-  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
-  if (reloc->howto == NULL)
+  for (slot = 0; slot < vinsn->num_slots; slot++)
     {
-      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;
+      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);
+
+      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;
+
+      if (tinsn->subtype || tinsn->symbol || tinsn->offset
+         || tinsn->literal_frag || is_jump || is_branch)
+       finish_frag = TRUE;
     }
 
-  if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+  if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+    frag_now->tc_frag_data.is_specific_opcode = TRUE;
+
+  if (finish_frag)
     {
-      as_fatal (_("internal error? cannot generate `%s' relocation"),
-               bfd_get_reloc_code_name (fixp->fx_r_type));
+      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);
     }
-  assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
 
-  reloc->addend = fixp->fx_offset;
+  /* 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.  */
 
-  switch (fixp->fx_r_type)
+  /* "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)
     {
-    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;
+      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);
+       }
 
-    case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
-      as_warn (_("emitting simplification relocation"));
-      break;
+      /* "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);
+       }
+    }
 
-    default:
-      as_warn (_("emitting unknown relocation"));
+  if (use_transform ())
+    {
+      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);
+       }
     }
 
-  return reloc;
+  /* 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);
+    }
+
+  if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+    {
+      frag_wane (frag_now);
+      frag_new (0);
+      xtensa_set_frag_assembly_state (frag_now);
+    }
 }
 
 \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
-xtensa_end ()
+xtensa_end (void)
 {
   directive_balance ();
+  xtensa_flush_pending_output ();
+
+  past_xtensa_end = TRUE;
+
   xtensa_move_literals ();
 
   xtensa_reorder_segments ();
-  xtensa_mark_target_fragments ();
   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 ();
-  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".  */
-  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 ();
 
-  if (software_avoid_short_loop && maybe_has_short_loop)
+  if (workaround_short_loop && maybe_has_short_loop)
     xtensa_fix_short_loop_frags ();
+  if (align_targets)
+    xtensa_mark_narrow_branches ();
+  xtensa_mark_zcl_first_insns ();
 
   xtensa_sanity_check ();
+
+  xtensa_add_config_info ();
 }
 
 
 static void
-xtensa_cleanup_align_frags ()
+xtensa_cleanup_align_frags (void)
 {
   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.
-   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
-xtensa_fix_target_frags ()
+xtensa_fix_target_frags (void)
 {
   frchainS *frchP;
+  asection *s;
 
   /* 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
-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;
 }
 
 
+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.  */
 
+static bfd_boolean next_instrs_are_b_retw (fragS *);
+
 static void
-xtensa_fix_a0_b_retw_frags ()
+xtensa_fix_a0_b_retw_frags (void)
 {
   frchainS *frchP;
+  asection *s;
 
   /* 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);
-           }
-       }
-    }
+             }
+         }
+      }
 }
 
 
-bfd_boolean
-next_instrs_are_b_retw (fragP)
-     fragS * fragP;
+static bfd_boolean
+next_instrs_are_b_retw (fragS *fragP)
 {
   xtensa_opcode opcode;
+  xtensa_format fmt;
   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;
+  int slot;
+  bfd_boolean branch_seen = FALSE;
 
   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.  */
-  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;
 
-  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 (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 FALSE;
 }
 
@@ -5634,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.  */
 
+static bfd_boolean next_instr_is_loop_end (fragS *);
+
 static void
-xtensa_fix_b_j_loop_end_frags ()
+xtensa_fix_b_j_loop_end_frags (void)
 {
   frchainS *frchP;
+  asection *s;
 
   /* 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);
-           }
-       }
-    }
+             }
+         }
+      }
 }
 
 
-bfd_boolean
-next_instr_is_loop_end (fragP)
-     fragS * fragP;
+static bfd_boolean
+next_instr_is_loop_end (fragS *fragP)
 {
   const fragS *next_fragP;
 
@@ -5692,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.  */
 
+static offsetT min_bytes_to_other_loop_end
+  (fragS *, fragS *, offsetT);
+
 static void
-xtensa_fix_close_loop_end_frags ()
+xtensa_fix_close_loop_end_frags (void)
 {
   frchainS *frchP;
+  asection *s;
 
   /* 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);
-
-                 /* Get the current fragment target.  */
-                 if (fragP->fr_symbol)
-                   {
-                     current_target = symbol_get_frag (fragP->fr_symbol);
-                     current_offset = fragP->fr_offset;
-                   }
-               }
-           }
-
-         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;
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+      {
+       fragS *fragP;
 
-#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);
+       fragS *current_target = NULL;
 
-             if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
-               {
-                 while (min_bytes + bytes_added
-                        < REQUIRED_LOOP_DIVIDING_BYTES)
-                   {
-                     int length = 3;
+       /* 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;
 
-                     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);
-           }
-       }
-    }
+#define REQUIRED_LOOP_DIVIDING_BYTES 12
+               /* 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;
@@ -5786,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)
-       return offset + current_offset;
+       return offset;
 
       offset += unrelaxed_frag_min_size (current_fragP);
 
-      if (offset + current_offset >= max_size)
+      if (offset >= 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;
 
@@ -5811,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:
 
@@ -5820,80 +7566,77 @@ unrelaxed_frag_min_size (fragP)
      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.  */
 
+static int count_insns_to_loop_end (fragS *, bfd_boolean, int);
+static bfd_boolean branch_before_loop_end (fragS *);
+
 static void
-xtensa_fix_short_loop_frags ()
+xtensa_fix_short_loop_frags (void)
 {
   frchainS *frchP;
+  asection *s;
 
   /* 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);
-           }
-       }
-    }
+             }
+         }
+      }
 }
 
 
-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;
-  size_t insn_count = 0;
+  int insn_count = 0;
 
   fragP = base_fragP;
 
@@ -5921,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;
 
+  if (!insnbuf)
+    insnbuf = xtensa_insnbuf_alloc (isa);
+
   /* 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;
        }
-      offset += xtensa_insn_length (xtensa_default_isa, opcode);
+      offset += xtensa_format_length (isa, fmt);
       insn_count++;
     }
 
@@ -5948,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;
 
@@ -5965,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;
 
+  if (!insnbuf)
+    insnbuf = xtensa_insnbuf_alloc (isa);
+
   /* 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;
 }
@@ -5994,66 +7759,69 @@ unrelaxed_frag_has_b_j (fragP)
 
 /* 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
-xtensa_sanity_check ()
+xtensa_sanity_check (void)
 {
   char *file_name;
-  int line;
-
+  unsigned line;
   frchainS *frchP;
+  asection *s;
 
   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
 
-/* 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;
@@ -6062,7 +7830,7 @@ is_empty_loop (insn, fragP)
   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)
@@ -6085,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.  */
+
   for (next_fragP = fragP->fr_next;
        next_fragP != NULL;
        next_fragP = next_fragP->fr_next)
@@ -6098,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;
@@ -6110,7 +7877,7 @@ is_local_forward_loop (insn, fragP)
   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)
@@ -6130,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.  */
+
   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;
 }
 
+
+#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.  */
 
-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);
@@ -6172,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)
     {
-      /* 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
     {
-      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;
 }
 
 
+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.  */
 
-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);
@@ -6281,7 +8075,7 @@ get_text_align_nop_count (fill_size, use_no_density)
 
   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;
@@ -6292,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;
 
+  assert (fill_size != 1);     /* Bad argument.  */
+
   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;
@@ -6324,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
-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;
 }
 
 
-/* 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. 
-   
+/* 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) 
+   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.  */
 
-static addressT
-get_widen_aligned_address (fragP, address)
-     fragS *fragP;
-     addressT address;
+static offsetT
+get_aligned_diff (fragS *fragP, addressT address, offsetT *max_diff)
 {
-  addressT align_pow, new_address, loop_insn_offset;
-  fragS *next_frag;
-  int insn_size;
-  xtensa_opcode opcode, next_opcode;
-  static xtensa_insnbuf insnbuf = NULL;
+  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 (!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)
+  assert (fragP->fr_type == rs_machine_dependent);
+  switch (fragP->fr_subtype)
     {
-      switch (fragP->fr_subtype)
-       {
-       case RELAX_DESIRE_ALIGN:
-
-         /* 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.  */
-
-         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;
-
-         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));
-
-         loop_insn_offset = 0;
-         next_frag = next_non_empty_frag (fragP);
-
-         /* 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);
+    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.  */
 
+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
-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 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);
@@ -6562,7 +8294,8 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
     {
     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:
@@ -6572,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);
+      frag_wane (fragP);
       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;
 
@@ -6594,54 +8327,86 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
       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:
+    case RELAX_MAYBE_UNREACHABLE:
+    case RELAX_MAYBE_DESIRE_ALIGN:
       /* 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"));
     }
 
+  /* 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
-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;
 
-  /* 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 +
-                     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.  */
@@ -6653,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.  */
-  fragP->tc_frag_data.text_expansion += growth;
+  fragP->tc_frag_data.text_expansion[0] += 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
-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;
-  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;
-  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)
     {
-      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;
-  frag_wane (fragP);
   return length;
 }
 
 
+static long future_alignment_required (fragS *, 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;
     }
 
-  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.  */
-      if (num_widens > 8)
-       return FALSE;
+      if (*widens >= (int) xtensa_fetch_width)
+       {
+         *fragPP = fragP;
+         return 0;
+       }
       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)
            {
-           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:
-             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:
-             /* 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:
-             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;
     }
 
-  return FALSE;
+  *fragPP = fragP;
+  return 0;
 }
 
 
+static long bytes_to_stretch (fragS *, int, int, int, int);
+
 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;
+  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;
-  int literal_diff, text_diff;
+  int literal_diff, total_text_diff, this_text_diff, first;
 
   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
@@ -6861,7 +8938,7 @@ relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
 
   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)
     {
@@ -6875,26 +8952,40 @@ relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
       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;
-  num_text_bytes = get_num_stack_text_bytes (&istack) - old_size;
   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.  */
-  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)
     {
-      lit_fragP = fragP->tc_frag_data.literal_frag;
+      lit_fragP = fragP->tc_frag_data.literal_frags[slot];
       if (lit_fragP)
        {
          assert (literal_diff == 4);
@@ -6912,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.  */
 
+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
-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;
-  int line;
+  unsigned line;
 
   as_where (&file_name, &line);
   new_logical_line (fragp->fr_file, fragp->fr_line);
@@ -6954,16 +9046,56 @@ md_convert_frag (abfd, sec, fragp)
     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;
 
-    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:
@@ -7001,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.  */
-  size_t i;
-
   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);
@@ -7018,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;
 
-  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;
@@ -7033,15 +9163,25 @@ convert_frag_align_next_opcode (fragp)
 
 
 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;
 
-  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;
@@ -7050,26 +9190,26 @@ convert_frag_narrow (fragP)
 
   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;
-  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;
 
-  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);
@@ -7083,81 +9223,132 @@ convert_frag_narrow (fragP)
 
 
 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;
-  static xtensa_insnbuf insnbuf = NULL;
-  TInsn orig_t_insn;
+  TInsn orig_tinsn;
   bfd_boolean expanded = FALSE;
-  char *fr_opcode = fragP->fr_opcode;
   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.  */
-      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
     {
+      /* 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;
+      int target_offset = 0;
       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.  */
-      frag_offset = fragP->fr_opcode - fragP->fr_literal;
+      frag_offset = fr_opcode - fragP->fr_literal;
       istack_init (&istack);
-      xg_assembly_relax (&istack, &orig_t_insn,
+      xg_assembly_relax (&istack, &orig_tinsn,
                         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.  */
-      total_size = 0;
+      target_offset = 0;
       for (i = 0; i < istack.ninsn; i++)
        {
-         TInsn *t_insn = &istack.insn[i];
-         int size = 0;
+         TInsn *tinsn = &istack.insn[i];
          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.  */
-             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 (t_insn->ntok != 1)
+             if (tinsn->ntok != 1)
                as_bad (_("number of literal tokens != 1"));
 
              /* Set the literal symbol and add a fixup.  */
@@ -7165,53 +9356,109 @@ convert_frag_immed (segP, fragP, min_steps)
              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,
-                                     fragP->fr_opcode - fragP->fr_literal +
-                                     total_size, fragP);
+                                     fr_opcode - fragP->fr_literal
+                                     + target_offset, fragP);
              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;
+      first = TRUE;
+      last_is_jump = FALSE;
       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;
+         bfd_reloc_code_real_type reloc_type;
 
-         switch (t_insn->insn_type)
+         switch (tinsn->insn_type)
            {
            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 (t_insn->ntok == 1);
-             /* add a fixup */
+             assert (tinsn->ntok == 1);
+             /* Add a fixup.  */
              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,
-                                 &t_insn->tok[0], FALSE, BFD_RELOC_32);
+                                 &tinsn->tok[0], FALSE, reloc_type);
              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;
+             total_size += size;
              break;
            }
        }
@@ -7225,20 +9472,17 @@ convert_frag_immed (segP, fragP, min_steps)
       fragP->fr_fix += diff;
     }
 
-  /* Clean it up.  */
-  fragP->fr_var = 0;
-
   /* Check for undefined immediates in LOOP instructions.  */
-  if (is_loop_opcode (orig_t_insn.opcode))
+  if (is_loop)
     {
       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;
        }
-      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));
@@ -7246,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,
-                         &orig_t_insn.tok[0], TRUE,
+                         &orig_tinsn.tok[0], TRUE,
                          BFD_RELOC_XTENSA_ASM_EXPAND);
-
     }
 }
 
@@ -7264,57 +9507,47 @@ convert_frag_immed (segP, fragP, min_steps)
    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;
+
   assert (new_seg != 0);
   subseg_set (new_seg, new_subseg);
 
-  if (r_type == BFD_RELOC_32
-      && exp->X_add_symbol
-      && exp->X_add_symbol->sy_tc.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;
 }
 
 
-/* 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
-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;
@@ -7326,31 +9559,41 @@ convert_frag_immed_finish_loop (segP, fragP, t_insn)
   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.  */
-  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;
 
-  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;
@@ -7361,49 +9604,45 @@ convert_frag_immed_finish_loop (segP, fragP, t_insn)
       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"));
 
-  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);
 
-  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);
-  
+
   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);
-  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
@@ -7420,99 +9659,107 @@ typedef struct subseg_map_struct
 
   /* 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;
 
-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;
 
-  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;
 }
 
-\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
-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
-xtensa_move_seg_list_to_beginning (head)
-     seg_list *head;
+xtensa_move_seg_list_to_beginning (seg_list *head)
 {
   head = head->next;
   while (head)
@@ -7521,16 +9768,20 @@ xtensa_move_seg_list_to_beginning (head)
 
       /* 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;
     }
 }
 
 
-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;
@@ -7539,29 +9790,20 @@ xtensa_move_literals ()
   emit_state state;
   segT dest_seg;
   fixS *fix, *next_fix, **fix_splice;
+  sym_list *lit;
 
-  /* As clunky as this is, we can't rely on frag_var
-     and frag_variant to get called in all situations.  */
-
-  segment = literal_head->next;
-  while (segment)
-    {
-      frchain_from = seg_info (segment->seg)->frchainP;
-      search_frag = frchain_from->frch_root;
-      while (search_frag) 
-       {
-         search_frag->tc_frag_data.is_literal = TRUE;
-         search_frag = search_frag->fr_next;
-       }
-      segment = segment->next;
-    }
+  mark_literal_frags (literal_head->next);
 
   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;
@@ -7584,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);
+      xtensa_set_frag_assembly_state (frag_now);
       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;
 
-         /* 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);
-             /* 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;
+         literal_pool->tc_frag_data.literal_frag = search_frag;
 
          /* Now move any fixups associated with this frag to the
             right section.  */
@@ -7645,45 +9884,45 @@ xtensa_move_literals ()
        }
       frchain_from->fix_tail = NULL;
       xtensa_restore_emit_state (&state);
-      segment = segment->next;
     }
 
-  xtensa_move_frag_symbols ();
+  /* Now fix up the SEGMENT value for all the literal symbols.  */
+  for (lit = literal_syms; lit; lit = lit->next)
+    {
+      symbolS *lit_sym = lit->sym;
+      segT dest_seg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
+      if (dest_seg)
+       S_SET_SEGMENT (lit_sym, dest_seg);
+    }
 }
 
 
-static void
-xtensa_move_frag_symbol (sym)
-     symbolS *sym;
-{
-  fragS *frag = symbol_get_frag (sym); 
-
-  if (frag->tc_frag_data.lit_seg != (segT) 0)
-    S_SET_SEGMENT (sym, frag->tc_frag_data.lit_seg);
-}
-
+/* Walk over all the frags for segments in a list and mark them as
+   containing literals.  As clunky as this is, we can't rely on frag_var
+   and frag_variant to get called in all situations.  */
 
 static void
-xtensa_move_frag_symbols ()
+mark_literal_frags (seg_list *segment)
 {
-  symbolS *symbolP;
+  frchainS *frchain_from;
+  fragS *search_frag;
 
-  /* Although you might think that only one of these lists should be
-     searched, it turns out that the difference of the two sets
-     (either way) is not empty.  They do overlap quite a bit,
-     however.  */
-
-  for (symbolP = symbol_rootP; symbolP; symbolP = symbolP->sy_next)
-    xtensa_move_frag_symbol (symbolP);
-
-  map_over_defined_symbols (xtensa_move_frag_symbol);
+  while (segment)
+    {
+      frchain_from = seg_info (segment->seg)->frchainP;
+      search_frag = frchain_from->frch_root;
+      while (search_frag)
+       {
+         search_frag->tc_frag_data.is_literal = TRUE;
+         search_frag = search_frag->fr_next;
+       }
+      segment = segment->next;
+    }
 }
 
 
 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.  */
@@ -7697,8 +9936,8 @@ xtensa_reorder_seg_list (head, 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;
@@ -7708,23 +9947,23 @@ xtensa_reorder_seg_list (head, after)
 
 /* 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 last_sec;
+  segT last_sec = 0;
   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.  */
-  last_sec = get_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)
@@ -7733,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.  */
 
-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);
-  bfd_boolean is_init = 
+  segT lit_seg;
+  bfd_boolean is_init =
     (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));
-  
 
-  if (pool_location == NULL 
-      && !use_literal_section 
+  if (pool_location == NULL
+      && !use_literal_section
       && !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 (FALSE);
+      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);
+      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_set_frag_assembly_state (frag_now);
     }
-
-  /* Do a 4 byte align here.  */
-  frag_align (2, 0, 0);
 }
 
 
@@ -7827,184 +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. */
 
-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++;
-  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.  */
 
-void
-xtensa_restore_emit_state (state)
-     emit_state *state;
+static void
+xtensa_restore_emit_state (emit_state *state)
 {
   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
-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;
 
+  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;
+    }
 
-/* Return a segment of a given name if it is present.  */
+  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, '.');
 
-static segT
-seg_present (name)
-     const char *name;
-{
-  segT seg;
-  seg = stdoutput->sections;
+      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 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;
 
-  while (seg)
+      name = xmalloc (len + strlen (base_name) + 1);
+      strcpy (name, text_name);
+      strcpy (name + len, base_name);
+    }
+
+  /* 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)
     {
-      if (!strcmp (segment_name (seg), name))
-       return seg;
-      seg = seg->next;
+      flagword flags;
+
+      seg = subseg_force_new (name, 0);
+
+      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);
     }
 
-  return 0;
+  *pcached = seg;
+  subseg_set (current_section, current_subsec);
+  return seg;
 }
 
+\f
+/* Property Tables Stuff.  */
 
-/* Add a segment to a segment list.  */
+#define XTENSA_INSN_SEC_NAME ".xt.insn"
+#define XTENSA_LIT_SEC_NAME ".xt.lit"
+#define XTENSA_PROP_SEC_NAME ".xt.prop"
 
-static void
-add_seg_list (head, seg)
-     seg_list *head;
-     segT seg;
+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)
 {
-  seg_list *n;
-  n = (seg_list *) xmalloc (sizeof (seg_list));
-  assert (n);
+  xtensa_move_seg_list_to_beginning (literal_head);
 
-  n->seg = seg;
-  n->next = head->next;
-  head->next = n;
+  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);
 }
 
-\f
-/* Set up Property Tables after Relaxation.  */
 
-#define XTENSA_INSN_SEC_NAME ".xt.insn"
-#define XTENSA_LIT_SEC_NAME ".xt.lit"
+/* 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);
+
+             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.  */
 
-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);
+             frchainP = seginfo->frchainP;
+             frchainP->frch_root = fragP;
+             frchainP->frch_last = fragP;
 
-  xtensa_create_property_segments (get_frag_is_insn,
-                                  XTENSA_INSN_SEC_NAME,
-                                  xt_literal_sec);
-  if (use_literal_section)
-    xtensa_create_property_segments (get_frag_is_literal,
-                                    XTENSA_LIT_SEC_NAME,
-                                    xt_insn_sec);
-}
+             fixes = (fixS *) xmalloc (sizeof (fixS) * num_recs);
+             memset (fixes, 0, sizeof (fixS) * num_recs);
 
+             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_literal (fragP)
-     const fragS *fragP;
-{
-  assert (fragP != NULL);
-  return (fragP->tc_frag_data.is_literal);
-}
+                 /* 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;
 
-static bfd_boolean
-get_frag_is_insn (fragP)
-     const fragS *fragP;
-{
-  assert (fragP != NULL);
-  return (fragP->tc_frag_data.is_insn);
+                 /* Write the length.  */
+                 md_number_to_chars (&frag_data[4 + 8 * i],
+                                     cur_block->size, 4);
+                 cur_block = cur_block->next;
+               }
+           }
+       }
+    }
 }
 
 
 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.
-     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;
-      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 (stdoutput, 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);
-         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.  */
-         add_xt_block_frags (sec, insn_sec, xt_blocks, property_function);
+         add_xt_prop_frags (sec, insn_sec, xt_blocks, flag_fn);
        }
     }
 
@@ -8017,6 +10432,7 @@ xtensa_create_property_segments (property_function, section_name_base,
       segment_info_type *seginfo;
       xtensa_block_info *block;
       segT sec = *seclist;
+
       seginfo = seg_info (sec);
       block = seginfo->tc_segment_info_data.blocks[sec_type];
 
@@ -8024,28 +10440,29 @@ xtensa_create_property_segments (property_function, section_name_base,
        {
          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++;
 
-         rec_size = num_recs * 8;
+         rec_size = num_recs * (8 + 4);
          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)
            {
-             /* Allocate a fragment and leak it.  */
+             /* Allocate a fragment and (unfortunately) leak it.  */
              fragS *fragP;
-             size_t frag_size;
+             bfd_size_type frag_size;
              fixS *fixes;
              frchainS *frchainP;
-             size_t i;
+             int i;
              char *frag_data;
 
              frag_size = sizeof (fragS) + rec_size;
@@ -8057,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;
-             /* the rest are zeros */
+             /* The rest are zeros.  */
 
              frchainP = seginfo->frchainP;
              frchainP->frch_root = fragP;
@@ -8083,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_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;
@@ -8091,8 +10508,11 @@ xtensa_create_property_segments (property_function, section_name_base,
                  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);
+                 md_number_to_chars (&frag_data[8 + (8+4) * i],
+                                     frag_flags_to_number (&cur_block->flags),
+                                     4);
                  cur_block = cur_block->next;
                }
            }
@@ -8101,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);
@@ -8112,30 +10531,18 @@ retrieve_segment_info (seg)
       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.  */
-#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->frch_seg = seg;
       frchainP->frch_subseg = 0;
       frchainP->fix_root = NULL;
       frchainP->fix_tail = NULL;
@@ -8151,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;
@@ -8187,8 +10587,9 @@ section_has_property (sec, property_function)
     {
       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;
        }
     }
@@ -8198,12 +10599,12 @@ section_has_property (sec, property_function)
 
 /* 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;
@@ -8218,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.  */
 
-  /* 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
-istack_init (stack)
-     IStack *stack;
+istack_init (IStack *stack)
 {
   memset (stack, 0, sizeof (IStack));
   stack->ninsn = 0;
@@ -8266,27 +11017,24 @@ istack_init (stack)
 
 
 bfd_boolean
-istack_empty (stack)
-     IStack *stack;
+istack_empty (IStack *stack)
 {
   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.
-   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 *
-istack_top (stack)
-     IStack *stack;
+istack_top (IStack *stack)
 {
   int rec = stack->ninsn - 1;
   assert (!istack_empty (stack));
@@ -8295,89 +11043,60 @@ istack_top (stack)
 
 
 /* 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
-istack_push (stack, insn)
-     IStack *stack;
-     TInsn *insn;
+istack_push (IStack *stack, TInsn *insn)
 {
   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
-   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 *
-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];
-  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
-   istack_empty () returns true.  */
+   istack_empty () returns TRUE.  */
 
 void
-istack_pop (stack)
-     IStack *stack;
+istack_pop (IStack *stack)
 {
   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
-tinsn_init (dst)
-     TInsn *dst;
+tinsn_init (TInsn *dst)
 {
   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
-tinsn_has_symbolic_operands (insn)
-     const TInsn *insn;
+tinsn_has_symbolic_operands (const TInsn *insn)
 {
   int i;
   int n = insn->ntok;
@@ -8400,9 +11119,9 @@ tinsn_has_symbolic_operands (insn)
 
 
 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;
 
@@ -8415,12 +11134,24 @@ tinsn_has_invalid_symbolic_operands (insn)
        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:
-         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;
@@ -8434,8 +11165,7 @@ tinsn_has_invalid_symbolic_operands (insn)
    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;
@@ -8447,6 +11177,8 @@ tinsn_has_complex_operands (insn)
        case O_register:
        case O_constant:
        case O_symbol:
+       case O_lo16:
+       case O_hi16:
          break;
        default:
          return TRUE;
@@ -8456,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
-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_opcode opcode = t_insn->opcode;
+  xtensa_opcode opcode = tinsn->opcode;
   bfd_boolean has_fixup = FALSE;
-  int noperands = xtensa_num_operands (isa, opcode);
+  int noperands = xtensa_opcode_num_operands (isa, opcode);
   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"));
 
-  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:
-         /* 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;
-         (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:
+         if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+           break;
          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.  */
-         xtensa_insnbuf_set_operand (insnbuf, opcode, operand,
+         xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
                                      expr->X_add_number, file_name, line);
          break;
 
-       case O_symbol:
        default:
          has_fixup = TRUE;
          break;
        }
     }
+
   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;
@@ -8533,13 +11314,13 @@ tinsn_check_arguments (insn)
       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;
     }
 
-  if (xtensa_num_operands (isa, opcode) < insn->ntok)
+  if (xtensa_opcode_num_operands (isa, opcode) < insn->ntok)
     {
       as_bad (_("too many operands"));
       return TRUE;
@@ -8551,31 +11332,38 @@ tinsn_check_arguments (insn)
 /* 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.  */
-  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));
     }
 }
 
@@ -8583,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
-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;
 
-  if (fragP->fr_symbol)
+  if (fragP->tc_frag_data.slot_symbols[slot])
     {
       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
-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++)
     {
-      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
-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++)
     {
-      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;
 }
 
+\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.  */
 
-/* Return true if the expression is an integer constant.  */
+/* Return TRUE if the expression is an integer constant.  */
 
 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.
-   Calling this is illegal if expr_is_const () returns true.  */
+   Calling this is illegal if expr_is_const () returns TRUE.  */
 
 offsetT
-get_expr_const (s)
-     const expressionS *s;
+get_expr_const (const expressionS *s)
 {
   assert (expr_is_const (s));
   return s->X_add_number;
@@ -8661,9 +11623,7 @@ get_expr_const (s)
 /* 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;
@@ -8672,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_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;
@@ -8687,10 +11662,10 @@ set_expr_symbol_offset (s, sym, offset)
 }
 
 
+/* Return TRUE if the two expressions are equal.  */
+
 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;
@@ -8705,17 +11680,13 @@ expr_is_equal (s1, s2)
 
 
 static void
-copy_expr (dst, src)
-     expressionS *dst;
-     const expressionS *src;
+copy_expr (expressionS *dst, const expressionS *src)
 {
   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
 {
@@ -8727,18 +11698,22 @@ struct rename_section_struct
 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;
 
-  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, ':');
@@ -8748,67 +11723,55 @@ build_section_rename (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)
-    if (strcmp (r->old_name, name) == 0)
-      return r->new_name;
+    {
+      if (strcmp (r->old_name, name) == 0)
+       return r->new_name;
+    }
 
   return name;
 }
-
-#endif /* XTENSA_SECTION_RENAME */
This page took 0.311026 seconds and 4 git commands to generate.