From 2bd49b44dfe938623456d4abfef7f0c5f5b3b81f Mon Sep 17 00:00:00 2001
From: Lulu Cai <cailulu@loongson.cn>
Date: Wed, 24 Jan 2024 17:43:20 +0800
Subject: [PATCH 069/123] LoongArch: Delete extra instructions when TLS type
 transition

This modification mainly changes the timing of type transition,
adds relaxation to the old LE instruction sequence, and fixes
bugs in extreme code models.

We strictly distinguish between type transition and relaxation.
Type transition is from one type to another, while relaxation
is the removal of instructions under the same TLS type. Detailed
instructions are as follows:

1. For type transition, only the normal code model of DESC/IE
does type transition, and each relocation is accompanied by a
RELAX relocation. Neither abs nor extreme will do type transition,
and no RELAX relocation will be generated.
The extra instructions when DESC transitions to other TLS types
will be deleted during the type transition.

2. Implemented relaxation for the old LE instruction sequence.
The first two instructions of LE's 32-bit and 64-bit models
use the same relocations and cannot be distinguished based on
relocations. Therefore, for LE's instruction sequence, any code
model will try to relax.

3. Some function names have been adjusted to facilitate understanding,
parameters have been adjusted, and unused macros have been deleted.
---
 bfd/elfnn-loongarch.c     | 420 +++++++++++++++++++++++---------------
 gas/config/tc-loongarch.c |  31 ++-
 2 files changed, 279 insertions(+), 172 deletions(-)

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index 1693ad7e..eea1839f 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -145,16 +145,20 @@ struct loongarch_elf_link_hash_table
 #define elf_backend_rela_normal 1
 #define elf_backend_default_execstack 0
 
-#define IS_LOONGARCH_TLS_DESC_RELOC(R_TYPE)    \
-  ((R_TYPE) == R_LARCH_TLS_DESC_PC_HI20	\
-   || (R_TYPE) == R_LARCH_TLS_DESC_PC_LO12  \
-   || (R_TYPE) == R_LARCH_TLS_DESC_LD \
-   || (R_TYPE) == R_LARCH_TLS_DESC_CALL)
-
-#define IS_LOONGARCH_TLS_IE_RELOC(R_TYPE) \
-  ((R_TYPE) == R_LARCH_TLS_IE_PC_HI20 \
+#define IS_LOONGARCH_TLS_TRANS_RELOC(R_TYPE)  \
+  ((R_TYPE) == R_LARCH_TLS_DESC_PC_HI20	      \
+   || (R_TYPE) == R_LARCH_TLS_DESC_PC_LO12    \
+   || (R_TYPE) == R_LARCH_TLS_DESC_LD	      \
+   || (R_TYPE) == R_LARCH_TLS_DESC_CALL	      \
+   || (R_TYPE) == R_LARCH_TLS_IE_PC_HI20      \
    || (R_TYPE) == R_LARCH_TLS_IE_PC_LO12)
 
+#define IS_OUTDATED_TLS_LE_RELOC(R_TYPE)  \
+  ((R_TYPE) == R_LARCH_TLS_LE_HI20	  \
+   || (R_TYPE) == R_LARCH_TLS_LE_LO12	  \
+   || (R_TYPE) == R_LARCH_TLS_LE64_LO20	  \
+   || (R_TYPE) == R_LARCH_TLS_LE64_HI12)
+
 /* Generate a PLT header.  */
 
 static bool
@@ -642,15 +646,18 @@ loongarch_reloc_got_type (unsigned int r_type)
 
 /* Return true if tls type transition can be performed.  */
 static bool
-loongarch_can_relax_tls (struct bfd_link_info *info, unsigned int r_type,
-			 struct elf_link_hash_entry *h, bfd *input_bfd,
-			 unsigned long r_symndx)
+loongarch_can_trans_tls (bfd *input_bfd,
+			 struct bfd_link_info *info,
+			 struct elf_link_hash_entry *h,
+			 unsigned int r_symndx,
+			 unsigned int r_type)
 {
   char symbol_tls_type;
   unsigned int reloc_got_type;
 
-  if (! (IS_LOONGARCH_TLS_DESC_RELOC (r_type)
-	 || IS_LOONGARCH_TLS_IE_RELOC (r_type)))
+  /* Only TLS DESC/IE in normal code mode will perform type
+     transition.  */
+  if (! IS_LOONGARCH_TLS_TRANS_RELOC (r_type))
     return false;
 
   symbol_tls_type = _bfd_loongarch_elf_tls_type (input_bfd, h, r_symndx);
@@ -707,11 +714,13 @@ loongarch_tls_transition_without_check (struct bfd_link_info *info,
 }
 
 static unsigned int
-loongarch_tls_transition (struct bfd_link_info *info, unsigned int r_type,
-			  struct elf_link_hash_entry *h, bfd *input_bfd,
-			  unsigned long r_symndx)
+loongarch_tls_transition (bfd *input_bfd,
+			  struct bfd_link_info *info,
+			  struct elf_link_hash_entry *h,
+			  unsigned int r_symndx,
+			  unsigned int r_type)
 {
-  if (! loongarch_can_relax_tls (info, r_type, h, input_bfd,r_symndx))
+  if (! loongarch_can_trans_tls (input_bfd, info, h, r_symndx, r_type))
     return r_type;
 
   return loongarch_tls_transition_without_check (info, r_type, h);
@@ -818,7 +827,11 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       int need_dynreloc = 0;
       int only_need_pcrel = 0;
 
-      r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
+      /* Type transitions are only possible with relocations accompanied
+	 by R_LARCH_RELAX.  */
+      if (rel + 1 != relocs + sec->reloc_count
+	  && ELFNN_R_TYPE (rel[1].r_info) == R_LARCH_RELAX)
+	r_type = loongarch_tls_transition (abfd, info, h, r_symndx, r_type);
       switch (r_type)
 	{
 	case R_LARCH_GOT_PC_HI20:
@@ -2536,95 +2549,6 @@ loongarch_reloc_is_fatal (struct bfd_link_info *info,
       relocation += 0x100000000;			\
   })
 
-/* Transition instruction sequence to relax instruction sequence.  */
-static bool
-loongarch_tls_relax (bfd *abfd, asection *sec, Elf_Internal_Rela *rel,
-		    int r_type, struct elf_link_hash_entry *h,
-		    struct bfd_link_info *info)
-{
-  bool local_exec = bfd_link_executable (info)
-		    && SYMBOL_REFERENCES_LOCAL (info, h);
-  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
-  unsigned long insn;
-
-  switch (r_type)
-    {
-      case R_LARCH_TLS_DESC_PC_HI20:
-	if (local_exec)
-	    /* DESC -> LE relaxation:
-	       pcalalau12i $a0,%desc_pc_hi20(var) =>
-	       lu12i.w $a0,%le_hi20(var)
-	    */
-	    bfd_put (32, abfd, LARCH_LU12I_W | LARCH_RD_A0,
-		     contents + rel->r_offset);
-
-	/* DESC -> IE relaxation:
-	   pcalalau12i $a0,%desc_pc_hi20(var) =>
-	   pcalalau12i $a0,%ie_pc_hi20(var)
-	*/
-	return true;
-
-      case R_LARCH_TLS_DESC_PC_LO12:
-	if (local_exec)
-	  {
-	    /* DESC -> LE relaxation:
-	       addi.d $a0,$a0,%desc_pc_lo12(var) =>
-	       ori  $a0,$a0,le_lo12(var)
-	    */
-	    insn = LARCH_ORI | LARCH_RD_RJ_A0;
-	    bfd_put (32, abfd, LARCH_ORI | LARCH_RD_RJ_A0,
-		     contents + rel->r_offset);
-	  }
-	else
-	  {
-	    /* DESC -> IE relaxation:
-	       addi.d $a0,$a0,%desc_pc_lo12(var) =>
-	       ld.d $a0,$a0,%%ie_pc_lo12
-	    */
-	    bfd_put (32, abfd, LARCH_LD_D | LARCH_RD_RJ_A0,
-		     contents + rel->r_offset);
-	  }
-	return true;
-
-      case R_LARCH_TLS_DESC_LD:
-      case R_LARCH_TLS_DESC_CALL:
-	/* DESC -> LE/IE relaxation:
-	   ld.d $ra,$a0,%desc_ld(var) => NOP
-	   jirl $ra,$ra,%desc_call(var) => NOP
-	*/
-	bfd_put (32, abfd, LARCH_NOP, contents + rel->r_offset);
-	return true;
-
-      case R_LARCH_TLS_IE_PC_HI20:
-	if (local_exec)
-	  {
-	    /* IE -> LE relaxation:
-	       pcalalau12i $rd,%ie_pc_hi20(var) =>
-	       lu12i.w $rd,%le_hi20(var)
-	    */
-	    insn = bfd_getl32 (contents + rel->r_offset);
-	    bfd_put (32, abfd, LARCH_LU12I_W | (insn & 0x1f),
-		     contents + rel->r_offset);
-	  }
-	return true;
-
-      case R_LARCH_TLS_IE_PC_LO12:
-	if (local_exec)
-	  {
-	    /* IE -> LE relaxation:
-	       ld.d $rd,$rj,%%ie_pc_lo12 =>
-	       ori  $rd,$rj,le_lo12(var)
-	    */
-	    insn = bfd_getl32 (contents + rel->r_offset);
-	    bfd_put (32, abfd, LARCH_ORI | (insn & 0x3ff),
-		     contents + rel->r_offset);
-	  }
-	return true;
-    }
-
-  return false;
-}
-
 
 static int
 loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
@@ -2659,7 +2583,6 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
       const char *name;
       bfd_reloc_status_type r = bfd_reloc_ok;
       bool is_ie, is_desc, is_undefweak, unresolved_reloc, defined_local;
-      unsigned int relaxed_r_type;
       bool resolved_local, resolved_dynly, resolved_to_const;
       char tls_type;
       bfd_vma relocation, off, ie_off, desc_off;
@@ -2791,16 +2714,6 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
       BFD_ASSERT (!resolved_local || defined_local);
 
-      relaxed_r_type = loongarch_tls_transition (info, r_type, h, input_bfd, r_symndx);
-      if (relaxed_r_type != r_type)
-      {
-	howto = loongarch_elf_rtype_to_howto (input_bfd, relaxed_r_type);
-	BFD_ASSERT (howto != NULL);
-
-	if (loongarch_tls_relax (input_bfd, input_section, rel, r_type, h, info))
-	  r_type = relaxed_r_type;
-      }
-
       is_desc = false;
       is_ie = false;
       switch (r_type)
@@ -4112,6 +4025,116 @@ loongarch_relax_delete_bytes (bfd *abfd,
 
   return true;
 }
+
+/* Start perform TLS type transition.
+   Currently there are three cases of relocation handled here:
+   DESC -> IE, DEC -> LE and IE -> LE.  */
+static bool
+loongarch_tls_perform_trans (bfd *abfd, asection *sec,
+			   Elf_Internal_Rela *rel,
+			   struct elf_link_hash_entry *h,
+			   struct bfd_link_info *info)
+{
+  unsigned long insn;
+  bool local_exec = bfd_link_executable (info)
+		      && SYMBOL_REFERENCES_LOCAL (info, h);
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+  unsigned long r_type = ELFNN_R_TYPE (rel->r_info);
+  unsigned long r_symndx = ELFNN_R_SYM (rel->r_info);
+
+  switch (r_type)
+    {
+      case R_LARCH_TLS_DESC_PC_HI20:
+	if (local_exec)
+	  {
+	    /* DESC -> LE relaxation:
+	       pcalalau12i $a0,%desc_pc_hi20(var) =>
+	       lu12i.w $a0,%le_hi20(var)
+	    */
+	    bfd_put (32, abfd, LARCH_LU12I_W | LARCH_RD_A0,
+		contents + rel->r_offset);
+	    rel->r_info = ELFNN_R_INFO (r_symndx, R_LARCH_TLS_LE_HI20);
+	  }
+	else
+	  {
+	    /* DESC -> IE relaxation:
+	       pcalalau12i $a0,%desc_pc_hi20(var) =>
+	       pcalalau12i $a0,%ie_pc_hi20(var)
+	    */
+	    rel->r_info = ELFNN_R_INFO (r_symndx, R_LARCH_TLS_IE_PC_HI20);
+	  }
+	return true;
+
+      case R_LARCH_TLS_DESC_PC_LO12:
+	if (local_exec)
+	  {
+	    /* DESC -> LE relaxation:
+	       addi.d $a0,$a0,%desc_pc_lo12(var) =>
+	       ori  $a0,$a0,le_lo12(var)
+	    */
+	    insn = LARCH_ORI | LARCH_RD_RJ_A0;
+	    bfd_put (32, abfd, LARCH_ORI | LARCH_RD_RJ_A0,
+		contents + rel->r_offset);
+	    rel->r_info = ELFNN_R_INFO (r_symndx, R_LARCH_TLS_LE_LO12);
+	  }
+	else
+	  {
+	    /* DESC -> IE relaxation:
+	       addi.d $a0,$a0,%desc_pc_lo12(var) =>
+	       ld.d $a0,$a0,%ie_pc_lo12(var)
+	    */
+	    bfd_put (32, abfd, LARCH_LD_D | LARCH_RD_RJ_A0,
+		contents + rel->r_offset);
+	    rel->r_info = ELFNN_R_INFO (r_symndx, R_LARCH_TLS_IE_PC_LO12);
+	  }
+	return true;
+
+      case R_LARCH_TLS_DESC_LD:
+      case R_LARCH_TLS_DESC_CALL:
+	/* DESC -> LE/IE relaxation:
+	   ld.d $ra,$a0,%desc_ld(var) => NOP
+	   jirl $ra,$ra,%desc_call(var) => NOP
+	*/
+	rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
+	bfd_put (32, abfd, LARCH_NOP, contents + rel->r_offset);
+	/* link with -relax option will delete NOP.  */
+	if (!info->disable_target_specific_optimizations)
+	  loongarch_relax_delete_bytes (abfd, sec, rel->r_offset, 4, info);
+	return true;
+
+      case R_LARCH_TLS_IE_PC_HI20:
+	if (local_exec)
+	  {
+	    /* IE -> LE relaxation:
+	       pcalalau12i $rd,%ie_pc_hi20(var) =>
+	       lu12i.w $rd,%le_hi20(var)
+	    */
+	    insn = bfd_getl32 (contents + rel->r_offset);
+	    bfd_put (32, abfd, LARCH_LU12I_W | (insn & 0x1f),
+		contents + rel->r_offset);
+	    rel->r_info = ELFNN_R_INFO (r_symndx, R_LARCH_TLS_LE_HI20);
+	  }
+	return true;
+
+      case R_LARCH_TLS_IE_PC_LO12:
+	if (local_exec)
+	  {
+	    /* IE -> LE relaxation:
+	       ld.d $rd,$rj,%%ie_pc_lo12(var) =>
+	       ori  $rd,$rj,le_lo12(var)
+	    */
+	    insn = bfd_getl32 (contents + rel->r_offset);
+	    bfd_put (32, abfd, LARCH_ORI | (insn & 0x3ff),
+		contents + rel->r_offset);
+	    rel->r_info = ELFNN_R_INFO (r_symndx, R_LARCH_TLS_LE_LO12);
+	  }
+	return true;
+    }
+
+  return false;
+}
+
+
 /*  Relax tls le, mainly relax the process of getting TLS le symbolic addresses.
   there are three situations in which an assembly instruction sequence needs to
   be relaxed:
@@ -4148,6 +4171,21 @@ loongarch_relax_delete_bytes (bfd *abfd,
   lu12i.w    $rd,%le_hi20_r (sym)	 ==> (instruction deleted)
   add.{w/d}  $rd,$rd,$tp,%le_add_r (sym) ==> (instruction deleted)
   addi.{w/d} $rs,$rd,%le_lo12_r (sym)    ==> addi.{w/d} $rs,$tp,%le_lo12_r (sym)
+
+
+  For relocation of all old LE instruction sequences, whether it is
+  a normal code model or an extreme code model, relaxation will be
+  performed when the relaxation conditions are met.
+
+  nomal code model:
+  lu12i.w   $rd,%le_hi20(sym)	    => (deleted)
+  ori	    $rd,$rd,le_lo12(sym)    => ori  $rd,$zero,le_lo12(sym)
+
+  extreme code model:
+  lu12i.w   $rd,%le_hi20(sym)	    => (deleted)
+  ori	    $rd,$rd,%le_lo12(sym)   => ori  $rd,$zero,le_lo12(sym)
+  lu32i.d   $rd,%le64_lo20(sym)	    => (deleted)
+  lu52i.d   $rd,$rd,%le64_hi12(sym) => (deleted)
 */
 static bool
 loongarch_relax_tls_le (bfd *abfd, asection *sec,
@@ -4159,31 +4197,56 @@ loongarch_relax_tls_le (bfd *abfd, asection *sec,
   uint32_t insn = bfd_get (32, abfd, contents + rel->r_offset);
   static uint32_t insn_rj,insn_rd;
   symval = symval - elf_hash_table (link_info)->tls_sec->vma;
-  /* Whether the symbol offset is in the interval (offset < 0x800).  */
-  if (ELFNN_R_TYPE ((rel + 1)->r_info) == R_LARCH_RELAX && (symval < 0x800))
+  /* The old LE instruction sequence can be relaxed when the symbol offset
+     is smaller than the 12-bit range.  */
+  if (ELFNN_R_TYPE ((rel + 1)->r_info) == R_LARCH_RELAX && (symval <= 0xfff))
     {
       switch (ELFNN_R_TYPE (rel->r_info))
 	{
-	case R_LARCH_TLS_LE_HI20_R:
-	case R_LARCH_TLS_LE_ADD_R:
-	  /* delete insn.  */
-	  rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
-	  loongarch_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info);
-	  break;
-	case R_LARCH_TLS_LE_LO12_R:
-	  /* Change rj to $tp.  */
-	  insn_rj = 0x2 << 5;
-	  /* Get rd register.  */
-	  insn_rd = insn & 0x1f;
-	  /* Write symbol offset.  */
-	  symval <<= 10;
-	  /* Writes the modified instruction.  */
-	  insn = insn & 0xffc00000;
-	  insn = insn | symval | insn_rj | insn_rd;
-	  bfd_put (32, abfd, insn, contents + rel->r_offset);
-	  break;
-	default:
-	  break;
+	  /*if offset < 0x800, then perform the new le instruction
+	    sequence relax.  */
+	  case R_LARCH_TLS_LE_HI20_R:
+	  case R_LARCH_TLS_LE_ADD_R:
+	    /* delete insn.  */
+	    if (symval < 0x800)
+	      {
+		rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
+		loongarch_relax_delete_bytes (abfd, sec, rel->r_offset,
+		    4, link_info);
+	      }
+	    break;
+
+	  case R_LARCH_TLS_LE_LO12_R:
+	    if (symval < 0x800)
+	      {
+		/* Change rj to $tp.  */
+		insn_rj = 0x2 << 5;
+		/* Get rd register.  */
+		insn_rd = insn & 0x1f;
+		/* Write symbol offset.  */
+		symval <<= 10;
+		/* Writes the modified instruction.  */
+		insn = insn & 0xffc00000;
+		insn = insn | symval | insn_rj | insn_rd;
+		bfd_put (32, abfd, insn, contents + rel->r_offset);
+	      }
+	    break;
+
+	  case R_LARCH_TLS_LE_HI20:
+	  case R_LARCH_TLS_LE64_LO20:
+	  case R_LARCH_TLS_LE64_HI12:
+	    rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
+	    loongarch_relax_delete_bytes (abfd, sec, rel->r_offset,
+					  4, link_info);
+	    break;
+
+	  case R_LARCH_TLS_LE_LO12:
+	    bfd_put (32, abfd, LARCH_ORI | (insn & 0x1f),
+		    contents + rel->r_offset);
+	    break;
+
+	  default:
+	    break;
 	}
     }
   return true;
@@ -4534,7 +4597,7 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	 R_LARCH_CALL36: symval is the symbol address for local symbols,
 	 or the PLT entry address of the symbol. (Todo)
 	 R_LARCHL_TLS_LD/GD/DESC_PC_HI20: symval is the GOT entry address
-	 of the symbol.  */
+	 of the symbol if transition is not possible.  */
       if (r_symndx < symtab_hdr->sh_info)
 	{
 	  Elf_Internal_Sym *sym = (Elf_Internal_Sym *)symtab_hdr->contents
@@ -4542,22 +4605,24 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	  if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
 	    continue;
 
+	  /* Only TLS instruction sequences that are accompanied by
+	     R_LARCH_RELAX and cannot perform type transition can be
+	     relaxed.  */
 	  if (R_LARCH_TLS_LD_PC_HI20 == r_type
 	      || R_LARCH_TLS_GD_PC_HI20 == r_type
-	      || R_LARCH_TLS_DESC_PC_HI20 == r_type)
+	      || (R_LARCH_TLS_DESC_PC_HI20 == r_type
+		  && (i + 1 != sec->reloc_count)
+		  && ELFNN_R_TYPE (rel[1].r_info) == R_LARCH_RELAX
+		  && ! loongarch_can_trans_tls (abfd, info, h,
+						  r_symndx, r_type)))
 	    {
-	      if (loongarch_can_relax_tls (info, r_type, h, abfd, r_symndx))
-		continue;
-	      else
-		{
-		  sym_sec = htab->elf.sgot;
-		  symval = elf_local_got_offsets (abfd)[r_symndx];
-		  char tls_type = _bfd_loongarch_elf_tls_type (abfd, h,
-								r_symndx);
-		  if (R_LARCH_TLS_DESC_PC_HI20 == r_type
-			&& GOT_TLS_GD_BOTH_P (tls_type))
-		    symval += 2 * GOT_ENTRY_SIZE;
-		}
+	      sym_sec = htab->elf.sgot;
+	      symval = elf_local_got_offsets (abfd)[r_symndx];
+	      char tls_type = _bfd_loongarch_elf_tls_type (abfd, h,
+							    r_symndx);
+	      if (R_LARCH_TLS_DESC_PC_HI20 == r_type
+		    && GOT_TLS_GD_BOTH_P (tls_type))
+		symval += 2 * GOT_ENTRY_SIZE;
 	    }
 	  else if (sym->st_shndx == SHN_UNDEF || R_LARCH_ALIGN == r_type)
 	    {
@@ -4588,20 +4653,19 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	     shared object.  */
 	  if (R_LARCH_TLS_LD_PC_HI20 == r_type
 	      || R_LARCH_TLS_GD_PC_HI20 == r_type
-	      || R_LARCH_TLS_DESC_PC_HI20 == r_type)
+	      || (R_LARCH_TLS_DESC_PC_HI20 == r_type
+		  && (i + 1 != sec->reloc_count)
+		  && ELFNN_R_TYPE (rel[1].r_info) == R_LARCH_RELAX
+		  && !loongarch_can_trans_tls (abfd, info, h,
+						r_symndx, r_type)))
 	    {
-	      if (loongarch_can_relax_tls (info, r_type, h, abfd, r_symndx))
-		continue;
-	      else
-		{
-		  sym_sec = htab->elf.sgot;
-		  symval = h->got.offset;
-		  char tls_type = _bfd_loongarch_elf_tls_type (abfd, h,
-								r_symndx);
-		  if (R_LARCH_TLS_DESC_PC_HI20 == r_type
-			&& GOT_TLS_GD_BOTH_P (tls_type))
-		    symval += 2 * GOT_ENTRY_SIZE;
-		}
+	      sym_sec = htab->elf.sgot;
+	      symval = h->got.offset;
+	      char tls_type = _bfd_loongarch_elf_tls_type (abfd, h,
+							    r_symndx);
+	      if (R_LARCH_TLS_DESC_PC_HI20 == r_type
+		    && GOT_TLS_GD_BOTH_P (tls_type))
+		symval += 2 * GOT_ENTRY_SIZE;
 	    }
 	  else if ((h->root.type == bfd_link_hash_defined
 		  || h->root.type == bfd_link_hash_defweak)
@@ -4646,6 +4710,24 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 
       symval += sec_addr (sym_sec);
 
+      /* If the conditions for tls type transition are met, type
+	 transition is performed instead of relax.
+	 During the transition from DESC->IE/LE, there are 2 situations
+	 depending on the different configurations of the relax/norelax
+	 option.
+	 If the -relax option is used, the extra nops will be removed,
+	 and this transition is performed in pass 0.
+	 If the --no-relax option is used, nop will be retained, and
+	 this transition is performed in pass 1.  */
+      if (IS_LOONGARCH_TLS_TRANS_RELOC (r_type)
+	  && (i + 1 != sec->reloc_count)
+	  && ELFNN_R_TYPE (rel[1].r_info) == R_LARCH_RELAX
+	  && loongarch_can_trans_tls (abfd, info, h, r_symndx, r_type))
+	{
+	  loongarch_tls_perform_trans (abfd, sec, rel, h, info);
+	  r_type = ELFNN_R_TYPE (rel->r_info);
+	}
+
       switch (r_type)
 	{
 	case R_LARCH_ALIGN:
@@ -4664,6 +4746,10 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	case R_LARCH_TLS_LE_HI20_R:
 	case R_LARCH_TLS_LE_LO12_R:
 	case R_LARCH_TLS_LE_ADD_R:
+	case R_LARCH_TLS_LE_HI20:
+	case R_LARCH_TLS_LE_LO12:
+	case R_LARCH_TLS_LE64_LO20:
+	case R_LARCH_TLS_LE64_HI12:
 	  if (0 == info->relax_pass && (i + 2) <= sec->reloc_count)
 	    loongarch_relax_tls_le (abfd, sec, rel, info, symval);
 	  break;
diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c
index e6da4e1e..b510d228 100644
--- a/gas/config/tc-loongarch.c
+++ b/gas/config/tc-loongarch.c
@@ -716,7 +716,11 @@ loongarch_args_parser_can_match_arg_helper (char esc_ch1, char esc_ch2,
 
 	      if (LARCH_opts.relax
 		    && (BFD_RELOC_LARCH_TLS_LE_HI20_R == reloc_type
-			|| BFD_RELOC_LARCH_TLS_LE_LO12_R == reloc_type))
+			|| BFD_RELOC_LARCH_TLS_LE_LO12_R == reloc_type
+			|| BFD_RELOC_LARCH_TLS_LE_HI20 == reloc_type
+			|| BFD_RELOC_LARCH_TLS_LE_LO12 == reloc_type
+			|| BFD_RELOC_LARCH_TLS_LE64_LO20 == reloc_type
+			|| BFD_RELOC_LARCH_TLS_LE64_HI12 == reloc_type))
 		{
 		  ip->reloc_info[ip->reloc_num].type = BFD_RELOC_LARCH_RELAX;
 		  ip->reloc_info[ip->reloc_num].value = const_0;
@@ -724,8 +728,12 @@ loongarch_args_parser_can_match_arg_helper (char esc_ch1, char esc_ch2,
 		}
 
 	      /* Only one register macros (used in normal code model)
-		 emit R_LARCH_RELAX.  */
+		 emit R_LARCH_RELAX.
+		 LARCH_opts.ase_labs and LARCH_opts.ase_gabs are used
+		 to generate the code model of absolute addresses, and
+		 we do not relax this code model.  */
 	      if (LARCH_opts.relax && (ip->expand_from_macro & 1)
+		    && !(LARCH_opts.ase_labs | LARCH_opts.ase_gabs)
 		    && (BFD_RELOC_LARCH_PCALA_HI20 == reloc_type
 			|| BFD_RELOC_LARCH_PCALA_LO12 == reloc_type
 			|| BFD_RELOC_LARCH_GOT_PC_HI20 == reloc_type
@@ -733,7 +741,11 @@ loongarch_args_parser_can_match_arg_helper (char esc_ch1, char esc_ch2,
 			|| BFD_RELOC_LARCH_TLS_LD_PC_HI20 == reloc_type
 			|| BFD_RELOC_LARCH_TLS_GD_PC_HI20 == reloc_type
 			|| BFD_RELOC_LARCH_TLS_DESC_PC_HI20 == reloc_type
-			|| BFD_RELOC_LARCH_TLS_DESC_PC_LO12 == reloc_type))
+			|| BFD_RELOC_LARCH_TLS_DESC_PC_LO12 == reloc_type
+			|| BFD_RELOC_LARCH_TLS_DESC_LD == reloc_type
+			|| BFD_RELOC_LARCH_TLS_DESC_CALL == reloc_type
+			|| BFD_RELOC_LARCH_TLS_IE_PC_HI20 == reloc_type
+			|| BFD_RELOC_LARCH_TLS_IE_PC_LO12 == reloc_type))
 		{
 		  ip->reloc_info[ip->reloc_num].type = BFD_RELOC_LARCH_RELAX;
 		  ip->reloc_info[ip->reloc_num].value = const_0;
@@ -1080,7 +1092,11 @@ append_fixp_and_insn (struct loongarch_cl_insn *ip)
      if (symbol_get_frag (to) == symbol_get_frag (from)))
 
      For macro instructions, only the first instruction expanded from macro
-     need to start a new frag.  */
+     need to start a new frag.
+     Since the relocations of the normal code model and the extreme code model
+     of the old LE instruction sequence are the same, it is impossible to
+     distinguish which code model it is based on relocation alone, so the
+     extreme code model has to be relaxed.  */
   if (LARCH_opts.relax
       && (BFD_RELOC_LARCH_PCALA_HI20 == reloc_info[0].type
 	  || BFD_RELOC_LARCH_GOT_PC_HI20 == reloc_info[0].type
@@ -1088,7 +1104,12 @@ append_fixp_and_insn (struct loongarch_cl_insn *ip)
 	  || BFD_RELOC_LARCH_TLS_LE_ADD_R == reloc_info[0].type
 	  || BFD_RELOC_LARCH_TLS_LD_PC_HI20 == reloc_info[0].type
 	  || BFD_RELOC_LARCH_TLS_GD_PC_HI20 == reloc_info[0].type
-	  || BFD_RELOC_LARCH_TLS_DESC_PC_HI20 == reloc_info[0].type))
+	  || BFD_RELOC_LARCH_TLS_DESC_PC_HI20 == reloc_info[0].type
+	  || BFD_RELOC_LARCH_TLS_IE_PC_HI20 == reloc_info[0].type
+	  || BFD_RELOC_LARCH_TLS_LE_HI20 == reloc_info[0].type
+	  || BFD_RELOC_LARCH_TLS_LE_LO12 == reloc_info[0].type
+	  || BFD_RELOC_LARCH_TLS_LE64_LO20 == reloc_info[0].type
+	  || BFD_RELOC_LARCH_TLS_LE64_HI12 == reloc_info[0].type))
     {
       frag_wane (frag_now);
       frag_new (0);
-- 
2.33.0

