--- /dev/null
+From 0dbdfb7918d0b0cfcb8883b24c1291574bf5bb7c Mon Sep 17 00:00:00 2001
+From: Max Filippov <jcmvbkbc@gmail.com>
+Date: Tue, 2 Apr 2019 14:32:42 -0700
+Subject: [PATCH] gas: use literals/const16 for xtensa loop relaxation
+
+Loop opcode relaxation that uses addi/addmi doesn't work well with other
+relaxations that may cause code movement. Instead of encoding fixed loop
+end offset in the relaxed sequence use l32r or a pair of const16 to load
+loop end address. This way the address of the loop end gets a relocation
+record and it gets updated appropriately.
+
+gas/
+2019-04-02  Max Filippov  <jcmvbkbc@gmail.com>
+
+       * config/tc-xtensa.c (convert_frag_immed): Drop
+       convert_frag_immed_finish_loop invocation.
+       (convert_frag_immed_finish_loop): Drop declaration and
+       definition.
+       * config/xtensa-relax.c (widen_spec_list): Replace loop
+       widening that uses addi/addmi with widening that uses l32r
+       and const16.
+
+Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
+---
+ gas/config/tc-xtensa.c    | 120 ----------------------------------------------
+ gas/config/xtensa-relax.c |  77 ++++++++++++++++++++---------
+ 2 files changed, 55 insertions(+), 142 deletions(-)
+
+diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
+index 3bdbbc931cfc..0cc06361cf6f 100644
+--- a/gas/config/tc-xtensa.c
++++ b/gas/config/tc-xtensa.c
+@@ -10668,7 +10668,6 @@ convert_frag_fill_nop (fragS *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,
+@@ -10910,9 +10909,6 @@ convert_frag_immed (segT segP,
+       }
+     }
+ 
+-  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_tinsn.opcode))
+     {
+       /* Add an expansion note on the expanded instruction.  */
+@@ -10949,122 +10945,6 @@ fix_new_exp_in_seg (segT new_seg,
+ }
+ 
+ 
+-/* 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 (segT segP, fragS *fragP, TInsn *tinsn)
+-{
+-  TInsn loop_insn;
+-  TInsn addi_insn;
+-  TInsn addmi_insn;
+-  unsigned long target;
+-  static xtensa_insnbuf insnbuf = NULL;
+-  unsigned int loop_length, loop_length_hi, loop_length_lo;
+-  xtensa_isa isa = xtensa_default_isa;
+-  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 (tinsn->opcode);
+-
+-  /* 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);
+-
+-  gas_assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
+-  addi_offset += loop_offset;
+-  addmi_offset += loop_offset;
+-
+-  gas_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;
+-      gas_assert (S_GET_SEGMENT (sym) == segP
+-            || S_GET_SEGMENT (sym) == absolute_section);
+-      target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
+-    }
+-  else
+-    {
+-      as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
+-      target = 0;
+-    }
+-
+-  loop_length = target - (fragP->fr_address + fragP->fr_fix);
+-  loop_length_hi = loop_length & ~0x0ff;
+-  loop_length_lo = loop_length & 0x0ff;
+-  if (loop_length_lo >= 128)
+-    {
+-      loop_length_lo -= 256;
+-      loop_length_hi += 256;
+-    }
+-
+-  /* 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, 0);
+-  gas_assert (addi_insn.opcode == xtensa_addi_opcode);
+-
+-  tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
+-  gas_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, (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, (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.  */
+-
+-  target_count = 0;
+-  for (next_fragP = fragP; next_fragP != NULL;
+-       next_fragP = next_fragP->fr_next)
+-    {
+-      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;
+-    }
+-}
+-
+ \f
+ /* A map that keeps information on a per-subsegment basis.  This is
+    maintained during initial assembly, but is invalid once the
+diff --git a/gas/config/xtensa-relax.c b/gas/config/xtensa-relax.c
+index cb296ed85ed2..daf15d52c259 100644
+--- a/gas/config/xtensa-relax.c
++++ b/gas/config/xtensa-relax.c
+@@ -87,13 +87,7 @@
+    when the first and second operands are not the same as specified
+    by the "| %at!=%as" precondition clause.
+    {"l32i %at,%as,%imm | %at!=%as",
+-   "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l32i %at,%at,0"}
+-
+-   There is special case for loop instructions here, but because we do
+-   not currently have the ability to represent the difference of two
+-   symbols, the conversion requires special code in the assembler to
+-   write the operands of the addi/addmi pair representing the
+-   difference of the old and new loop end label.  */
++   "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l32i %at,%at,0"}  */
+ 
+ #include "as.h"
+ #include "xtensa-isa.h"
+@@ -306,44 +300,83 @@ static string_pattern_pair widen_spec_list[] =
+   {"l32i %at,%as,%imm | %at!=%as ? IsaUseConst16",
+    "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l32i %at,%at,0"},
+ 
+-  /* This is only PART of the loop instruction.  In addition,
+-     hardcoded into its use is a modification of the final operand in
+-     the instruction in bytes 9 and 12.  */
+-  {"loop %as,%label | %as!=1 ? IsaUseLoops",
++  /* Widening loops with literals.  */
++  {"loop %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "loop %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "beqz    %as,%label;"
++   "bltz    %as,%label;"
++   "loopgtz %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++  {"loopnez %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "beqz     %as,%label;"
++   "loopnez %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++
++  /* Widening loops with const16.  */
++  {"loop %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "loop %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+-  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops",
++  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "beqz    %as,%label;"
+    "bltz    %as,%label;"
+    "loopgtz %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+-  {"loopnez %as,%label | %as!=1 ? IsaUseLoops",
++  {"loopnez %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "beqz     %as,%label;"
+    "loopnez %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+ 
+   /* Relaxing to wide branches.  Order is important here.  With wide
+-- 
+2.11.0
+
 
--- /dev/null
+From 0dbdfb7918d0b0cfcb8883b24c1291574bf5bb7c Mon Sep 17 00:00:00 2001
+From: Max Filippov <jcmvbkbc@gmail.com>
+Date: Tue, 2 Apr 2019 14:32:42 -0700
+Subject: [PATCH] gas: use literals/const16 for xtensa loop relaxation
+
+Loop opcode relaxation that uses addi/addmi doesn't work well with other
+relaxations that may cause code movement. Instead of encoding fixed loop
+end offset in the relaxed sequence use l32r or a pair of const16 to load
+loop end address. This way the address of the loop end gets a relocation
+record and it gets updated appropriately.
+
+gas/
+2019-04-02  Max Filippov  <jcmvbkbc@gmail.com>
+
+       * config/tc-xtensa.c (convert_frag_immed): Drop
+       convert_frag_immed_finish_loop invocation.
+       (convert_frag_immed_finish_loop): Drop declaration and
+       definition.
+       * config/xtensa-relax.c (widen_spec_list): Replace loop
+       widening that uses addi/addmi with widening that uses l32r
+       and const16.
+
+Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
+---
+ gas/config/tc-xtensa.c    | 120 ----------------------------------------------
+ gas/config/xtensa-relax.c |  77 ++++++++++++++++++++---------
+ 2 files changed, 55 insertions(+), 142 deletions(-)
+
+diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
+index 3bdbbc931cfc..0cc06361cf6f 100644
+--- a/gas/config/tc-xtensa.c
++++ b/gas/config/tc-xtensa.c
+@@ -10668,7 +10668,6 @@ convert_frag_fill_nop (fragS *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,
+@@ -10910,9 +10909,6 @@ convert_frag_immed (segT segP,
+       }
+     }
+ 
+-  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_tinsn.opcode))
+     {
+       /* Add an expansion note on the expanded instruction.  */
+@@ -10949,122 +10945,6 @@ fix_new_exp_in_seg (segT new_seg,
+ }
+ 
+ 
+-/* 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 (segT segP, fragS *fragP, TInsn *tinsn)
+-{
+-  TInsn loop_insn;
+-  TInsn addi_insn;
+-  TInsn addmi_insn;
+-  unsigned long target;
+-  static xtensa_insnbuf insnbuf = NULL;
+-  unsigned int loop_length, loop_length_hi, loop_length_lo;
+-  xtensa_isa isa = xtensa_default_isa;
+-  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 (tinsn->opcode);
+-
+-  /* 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);
+-
+-  gas_assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
+-  addi_offset += loop_offset;
+-  addmi_offset += loop_offset;
+-
+-  gas_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;
+-      gas_assert (S_GET_SEGMENT (sym) == segP
+-            || S_GET_SEGMENT (sym) == absolute_section);
+-      target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
+-    }
+-  else
+-    {
+-      as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
+-      target = 0;
+-    }
+-
+-  loop_length = target - (fragP->fr_address + fragP->fr_fix);
+-  loop_length_hi = loop_length & ~0x0ff;
+-  loop_length_lo = loop_length & 0x0ff;
+-  if (loop_length_lo >= 128)
+-    {
+-      loop_length_lo -= 256;
+-      loop_length_hi += 256;
+-    }
+-
+-  /* 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, 0);
+-  gas_assert (addi_insn.opcode == xtensa_addi_opcode);
+-
+-  tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
+-  gas_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, (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, (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.  */
+-
+-  target_count = 0;
+-  for (next_fragP = fragP; next_fragP != NULL;
+-       next_fragP = next_fragP->fr_next)
+-    {
+-      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;
+-    }
+-}
+-
+ \f
+ /* A map that keeps information on a per-subsegment basis.  This is
+    maintained during initial assembly, but is invalid once the
+diff --git a/gas/config/xtensa-relax.c b/gas/config/xtensa-relax.c
+index cb296ed85ed2..daf15d52c259 100644
+--- a/gas/config/xtensa-relax.c
++++ b/gas/config/xtensa-relax.c
+@@ -87,13 +87,7 @@
+    when the first and second operands are not the same as specified
+    by the "| %at!=%as" precondition clause.
+    {"l32i %at,%as,%imm | %at!=%as",
+-   "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l32i %at,%at,0"}
+-
+-   There is special case for loop instructions here, but because we do
+-   not currently have the ability to represent the difference of two
+-   symbols, the conversion requires special code in the assembler to
+-   write the operands of the addi/addmi pair representing the
+-   difference of the old and new loop end label.  */
++   "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l32i %at,%at,0"}  */
+ 
+ #include "as.h"
+ #include "xtensa-isa.h"
+@@ -306,44 +300,83 @@ static string_pattern_pair widen_spec_list[] =
+   {"l32i %at,%as,%imm | %at!=%as ? IsaUseConst16",
+    "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l32i %at,%at,0"},
+ 
+-  /* This is only PART of the loop instruction.  In addition,
+-     hardcoded into its use is a modification of the final operand in
+-     the instruction in bytes 9 and 12.  */
+-  {"loop %as,%label | %as!=1 ? IsaUseLoops",
++  /* Widening loops with literals.  */
++  {"loop %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "loop %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "beqz    %as,%label;"
++   "bltz    %as,%label;"
++   "loopgtz %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++  {"loopnez %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "beqz     %as,%label;"
++   "loopnez %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++
++  /* Widening loops with const16.  */
++  {"loop %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "loop %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+-  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops",
++  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "beqz    %as,%label;"
+    "bltz    %as,%label;"
+    "loopgtz %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+-  {"loopnez %as,%label | %as!=1 ? IsaUseLoops",
++  {"loopnez %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "beqz     %as,%label;"
+    "loopnez %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+ 
+   /* Relaxing to wide branches.  Order is important here.  With wide
+-- 
+2.11.0
+
 
--- /dev/null
+From 0dbdfb7918d0b0cfcb8883b24c1291574bf5bb7c Mon Sep 17 00:00:00 2001
+From: Max Filippov <jcmvbkbc@gmail.com>
+Date: Tue, 2 Apr 2019 14:32:42 -0700
+Subject: [PATCH] gas: use literals/const16 for xtensa loop relaxation
+
+Loop opcode relaxation that uses addi/addmi doesn't work well with other
+relaxations that may cause code movement. Instead of encoding fixed loop
+end offset in the relaxed sequence use l32r or a pair of const16 to load
+loop end address. This way the address of the loop end gets a relocation
+record and it gets updated appropriately.
+
+gas/
+2019-04-02  Max Filippov  <jcmvbkbc@gmail.com>
+
+       * config/tc-xtensa.c (convert_frag_immed): Drop
+       convert_frag_immed_finish_loop invocation.
+       (convert_frag_immed_finish_loop): Drop declaration and
+       definition.
+       * config/xtensa-relax.c (widen_spec_list): Replace loop
+       widening that uses addi/addmi with widening that uses l32r
+       and const16.
+
+Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
+---
+ gas/config/tc-xtensa.c    | 120 ----------------------------------------------
+ gas/config/xtensa-relax.c |  77 ++++++++++++++++++++---------
+ 2 files changed, 55 insertions(+), 142 deletions(-)
+
+diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
+index 3bdbbc931cfc..0cc06361cf6f 100644
+--- a/gas/config/tc-xtensa.c
++++ b/gas/config/tc-xtensa.c
+@@ -10668,7 +10668,6 @@ convert_frag_fill_nop (fragS *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,
+@@ -10910,9 +10909,6 @@ convert_frag_immed (segT segP,
+       }
+     }
+ 
+-  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_tinsn.opcode))
+     {
+       /* Add an expansion note on the expanded instruction.  */
+@@ -10949,122 +10945,6 @@ fix_new_exp_in_seg (segT new_seg,
+ }
+ 
+ 
+-/* 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 (segT segP, fragS *fragP, TInsn *tinsn)
+-{
+-  TInsn loop_insn;
+-  TInsn addi_insn;
+-  TInsn addmi_insn;
+-  unsigned long target;
+-  static xtensa_insnbuf insnbuf = NULL;
+-  unsigned int loop_length, loop_length_hi, loop_length_lo;
+-  xtensa_isa isa = xtensa_default_isa;
+-  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 (tinsn->opcode);
+-
+-  /* 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);
+-
+-  gas_assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
+-  addi_offset += loop_offset;
+-  addmi_offset += loop_offset;
+-
+-  gas_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;
+-      gas_assert (S_GET_SEGMENT (sym) == segP
+-            || S_GET_SEGMENT (sym) == absolute_section);
+-      target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
+-    }
+-  else
+-    {
+-      as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
+-      target = 0;
+-    }
+-
+-  loop_length = target - (fragP->fr_address + fragP->fr_fix);
+-  loop_length_hi = loop_length & ~0x0ff;
+-  loop_length_lo = loop_length & 0x0ff;
+-  if (loop_length_lo >= 128)
+-    {
+-      loop_length_lo -= 256;
+-      loop_length_hi += 256;
+-    }
+-
+-  /* 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, 0);
+-  gas_assert (addi_insn.opcode == xtensa_addi_opcode);
+-
+-  tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
+-  gas_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, (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, (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.  */
+-
+-  target_count = 0;
+-  for (next_fragP = fragP; next_fragP != NULL;
+-       next_fragP = next_fragP->fr_next)
+-    {
+-      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;
+-    }
+-}
+-
+ \f
+ /* A map that keeps information on a per-subsegment basis.  This is
+    maintained during initial assembly, but is invalid once the
+diff --git a/gas/config/xtensa-relax.c b/gas/config/xtensa-relax.c
+index cb296ed85ed2..daf15d52c259 100644
+--- a/gas/config/xtensa-relax.c
++++ b/gas/config/xtensa-relax.c
+@@ -87,13 +87,7 @@
+    when the first and second operands are not the same as specified
+    by the "| %at!=%as" precondition clause.
+    {"l32i %at,%as,%imm | %at!=%as",
+-   "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l32i %at,%at,0"}
+-
+-   There is special case for loop instructions here, but because we do
+-   not currently have the ability to represent the difference of two
+-   symbols, the conversion requires special code in the assembler to
+-   write the operands of the addi/addmi pair representing the
+-   difference of the old and new loop end label.  */
++   "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l32i %at,%at,0"}  */
+ 
+ #include "as.h"
+ #include "xtensa-isa.h"
+@@ -306,44 +300,83 @@ static string_pattern_pair widen_spec_list[] =
+   {"l32i %at,%as,%imm | %at!=%as ? IsaUseConst16",
+    "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l32i %at,%at,0"},
+ 
+-  /* This is only PART of the loop instruction.  In addition,
+-     hardcoded into its use is a modification of the final operand in
+-     the instruction in bytes 9 and 12.  */
+-  {"loop %as,%label | %as!=1 ? IsaUseLoops",
++  /* Widening loops with literals.  */
++  {"loop %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "loop %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "beqz    %as,%label;"
++   "bltz    %as,%label;"
++   "loopgtz %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++  {"loopnez %as,%label | %as!=1 ? IsaUseLoops ? IsaUseL32R",
++   "beqz     %as,%label;"
++   "loopnez %as,%LABEL;"
++   "rsr.lend    %as;"         /* LEND */
++   "wsr.lbeg    %as;"         /* LBEG */
++   "LITERAL     %label;"
++   "l32r        %as, %LITERAL;"
++   "nop;"
++   "wsr.lend    %as;"
++   "isync;"
++   "rsr.lcount    %as;"               /* LCOUNT */
++   "addi    %as, %as, 1;"
++   "LABEL"},
++
++  /* Widening loops with const16.  */
++  {"loop %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "loop %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+-  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops",
++  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "beqz    %as,%label;"
+    "bltz    %as,%label;"
+    "loopgtz %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+-  {"loopnez %as,%label | %as!=1 ? IsaUseLoops",
++  {"loopnez %as,%label | %as!=1 ? IsaUseLoops ? IsaUseConst16",
+    "beqz     %as,%label;"
+    "loopnez %as,%LABEL;"
+    "rsr.lend    %as;"         /* LEND */
+    "wsr.lbeg    %as;"         /* LBEG */
+-   "addi    %as, %as, 0;"     /* lo8(%label-%LABEL1) */
+-   "addmi   %as, %as, 0;"     /* mid8(%label-%LABEL1) */
++   "const16     %as,HI16U(%label);"
++   "const16     %as,LOW16U(%label);"
+    "wsr.lend    %as;"
+    "isync;"
+    "rsr.lcount    %as;"               /* LCOUNT */
+-   "addi    %as, %as, 1;"     /* density -> addi.n %as, %as, 1 */
++   "addi    %as, %as, 1;"
+    "LABEL"},
+ 
+   /* Relaxing to wide branches.  Order is important here.  With wide
+-- 
+2.11.0
+