static const char default_arch[] = DEFAULT_ARCH;
 
+/* The lowest 4-bit is the bytes of instructions.  */
+#define RELAX_BRANCH_16 0xc0000014
+#define RELAX_BRANCH_21 0xc0000024
+#define RELAX_BRANCH_26 0xc0000048
+
+#define RELAX_BRANCH(x) \
+  (((x) & 0xf0000000) == 0xc0000000)
+#define RELAX_BRANCH_ENCODE(x) \
+  (BFD_RELOC_LARCH_B16 == (x) ? RELAX_BRANCH_16 : RELAX_BRANCH_21)
+
 enum options
 {
   OPTION_IGNORE = OPTION_MD_BASE,
   move_insn (insn, frag_now, f - frag_now->fr_literal);
 }
 
+/* Add instructions based on the worst-case scenario firstly.  */
+static void
+append_relaxed_branch_insn (struct loongarch_cl_insn *insn, int max_chars,
+           int var, relax_substateT subtype, symbolS *symbol, offsetT offset)
+{
+  frag_grow (max_chars);
+  move_insn (insn, frag_now, frag_more (0) - frag_now->fr_literal);
+  frag_var (rs_machine_dependent, max_chars, var,
+           subtype, symbol, offset, NULL);
+}
+
 static void
 append_fixp_and_insn (struct loongarch_cl_insn *ip)
 {
   reloc_howto_type *howto;
-  bfd_reloc_code_real_type reloc_type;
+  bfd_reloc_code_real_type r_type;
   struct reloc_info *reloc_info = ip->reloc_info;
   size_t i;
 
 
   for (i = 0; i < ip->reloc_num; i++)
     {
-      reloc_type = reloc_info[i].type;
-      howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
-      if (howto == NULL)
-       as_fatal (_("no HOWTO loong relocation number %d"), reloc_type);
-
-      ip->fixp[i] =
-       fix_new_exp (ip->frag, ip->where, bfd_get_reloc_size (howto),
-                    &reloc_info[i].value, FALSE, reloc_type);
+      r_type = reloc_info[i].type;
+
+      if (r_type != BFD_RELOC_UNUSED)
+       {
+
+         gas_assert (&(reloc_info[i].value));
+         if (BFD_RELOC_LARCH_B16 == r_type || BFD_RELOC_LARCH_B21 == r_type)
+           {
+             int min_bytes = 4; /* One branch instruction.  */
+             unsigned max_bytes = 8; /* Branch and jump instructions.  */
+
+             if (now_seg == absolute_section)
+               {
+                 as_bad (_("relaxable branches not supported in absolute section"));
+                 return;
+               }
+
+             append_relaxed_branch_insn (ip, max_bytes, min_bytes,
+                                         RELAX_BRANCH_ENCODE (r_type),
+                                         reloc_info[i].value.X_add_symbol,
+                                         reloc_info[i].value.X_add_number);
+             return;
+           }
+         else
+           {
+             howto = bfd_reloc_type_lookup (stdoutput, r_type);
+             if (howto == NULL)
+               as_fatal (_("no HOWTO loong relocation number %d"), r_type);
+
+             ip->fixp[i] = fix_new_exp (ip->frag, ip->where,
+                                        bfd_get_reloc_size (howto),
+                                        &reloc_info[i].value, FALSE, r_type);
+           }
+       }
     }
 
   if (ip->insn_length < ip->relax_max_length)
     }
 }
 
-int
-loongarch_relax_frag (asection *sec ATTRIBUTE_UNUSED,
-                     fragS *fragp ATTRIBUTE_UNUSED,
-                     long stretch ATTRIBUTE_UNUSED)
-{
-  return 0;
-}
-
 int
 md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
                               asection *segtype ATTRIBUTE_UNUSED)
   return reloc;
 }
 
-/* Convert a machine dependent frag.  */
-void
-md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED,
-                fragS *fragp)
-{
-  expressionS exp;
-  exp.X_op = O_symbol;
-  exp.X_add_symbol = fragp->fr_symbol;
-  exp.X_add_number = fragp->fr_offset;
-  bfd_byte *buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
-
-  fixS *fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
-                               4, &exp, false, fragp->fr_subtype);
-  buf += 4;
-
-  fixp->fx_file = fragp->fr_file;
-  fixp->fx_line = fragp->fr_line;
-  fragp->fr_fix += fragp->fr_var;
-
-  gas_assert (fragp->fr_next == NULL
-             || (fragp->fr_next->fr_address - fragp->fr_address
-                 == fragp->fr_fix));
-}
-
 /* Standard calling conventions leave the CFA at SP on entry.  */
 void
 loongarch_cfi_frame_initial_instructions (void)
 {
   elf_elfheader (stdoutput)->e_flags = LARCH_opts.ase_abi;
 }
+
+/* Compute the length of a branch sequence, and adjust the stored length
+   accordingly.  If FRAGP is NULL, the worst-case length is returned.  */
+static unsigned
+loongarch_relaxed_branch_length (fragS *fragp, asection *sec, int update)
+{
+  int length = 4;
+
+  if (!fragp)
+    return 8;
+
+  if (fragp->fr_symbol != NULL
+      && S_IS_DEFINED (fragp->fr_symbol)
+      && !S_IS_WEAK (fragp->fr_symbol)
+      && sec == S_GET_SEGMENT (fragp->fr_symbol))
+    {
+      offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+
+      val -= fragp->fr_address + fragp->fr_fix;
+
+      if (RELAX_BRANCH_16 == fragp->fr_subtype
+         && OUT_OF_RANGE (val, 16, 2))
+       {
+         length = 8;
+         if (update)
+           fragp->fr_subtype = RELAX_BRANCH_26;
+       }
+
+      if (RELAX_BRANCH_21 == fragp->fr_subtype
+         && OUT_OF_RANGE (val, 21, 2))
+       {
+         length = 8;
+         if (update)
+           fragp->fr_subtype = RELAX_BRANCH_26;
+       }
+
+      if (RELAX_BRANCH_26 == fragp->fr_subtype)
+       length = 8;
+    }
+
+  return length;
+}
+
+int
+loongarch_relax_frag (asection *sec ATTRIBUTE_UNUSED,
+                     fragS *fragp ATTRIBUTE_UNUSED,
+                     long stretch ATTRIBUTE_UNUSED)
+{
+  if (RELAX_BRANCH (fragp->fr_subtype))
+    {
+      offsetT old_var = fragp->fr_var;
+      fragp->fr_var = loongarch_relaxed_branch_length (fragp, sec, true);
+      return fragp->fr_var - old_var;
+    }
+  return 0;
+}
+
+/* Expand far branches to multi-instruction sequences.
+   Branch instructions:
+   beq, bne, blt, bgt, bltz, bgtz, ble, bge, blez, bgez
+   bltu, bgtu, bleu, bgeu
+   beqz, bnez, bceqz, bcnez.  */
+
+static void
+loongarch_convert_frag_branch (fragS *fragp)
+{
+  bfd_byte *buf;
+  expressionS exp;
+  fixS *fixp;
+  insn_t insn;
+
+  buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
+
+  exp.X_op = O_symbol;
+  exp.X_add_symbol = fragp->fr_symbol;
+  exp.X_add_number = fragp->fr_offset;
+
+  gas_assert ((fragp->fr_subtype & 0xf) == fragp->fr_var);
+
+  /* blt $t0, $t1, .L1
+     nop
+     change to:
+     bge $t0, $t1, .L2
+     b .L1
+   .L2:
+     nop  */
+  switch (fragp->fr_subtype)
+    {
+    case RELAX_BRANCH_26:
+      insn = bfd_getl32 (buf);
+      /* Invert the branch condition.  */
+      if (LARCH_FLOAT_BRANCH == (insn & LARCH_BRANCH_OPCODE_MASK))
+       insn ^= LARCH_FLOAT_BRANCH_INVERT_BIT;
+      else
+       insn ^= LARCH_BRANCH_INVERT_BIT;
+      insn |= ENCODE_BRANCH16_IMM (8);  /* Set target to PC + 8.  */
+      bfd_putl32 (insn, buf);
+      buf += 4;
+
+      /* Add the B instruction and jump to the original target.  */
+      bfd_putl32 (LARCH_B, buf);
+      fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+                         4, &exp, false, BFD_RELOC_LARCH_B26);
+      buf += 4;
+      break;
+    case RELAX_BRANCH_21:
+      fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+                         4, &exp, false, BFD_RELOC_LARCH_B21);
+      buf += 4;
+      break;
+    case RELAX_BRANCH_16:
+      fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+                         4, &exp, false, BFD_RELOC_LARCH_B16);
+      buf += 4;
+      break;
+
+    default:
+      abort();
+    }
+
+  fixp->fx_file = fragp->fr_file;
+  fixp->fx_line = fragp->fr_line;
+
+  gas_assert (buf == (bfd_byte *)fragp->fr_literal
+             + fragp->fr_fix + fragp->fr_var);
+
+  fragp->fr_fix += fragp->fr_var;
+}
+
+/* Relax a machine dependent frag.  This returns the amount by which
+   the current size of the frag should change.  */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED,
+                fragS *fragp)
+{
+  gas_assert (RELAX_BRANCH (fragp->fr_subtype));
+  loongarch_convert_frag_branch (fragp);
+}
 
--- /dev/null
+#as:
+#objdump: -dr
+
+.*:[    ]+file format .*
+
+
+Disassembly of section .text:
+
+0* <.L1>:
+[      ]+...
+[      ]+48d158:[      ]+5c00098d[     ]+bne[  ]+\$t0, \$t1, 8[        ]+# 48d160 <.L1\+0x48d160>
+[      ]+48d15c:[      ]+532ea7ed[     ]+b[    ]+-4772188[     ]+# 0 <.L1>
+[      ]+48d15c: R_LARCH_B26[  ]+.L1
+[      ]+48d160:[      ]+5800098d[     ]+beq[  ]+\$t0, \$t1, 8[        ]+# 48d168 <.L1\+0x48d168>
+[      ]+48d164:[      ]+532e9fed[     ]+b[    ]+-4772196[     ]+# 0 <.L1>
+[      ]+48d164: R_LARCH_B26[  ]+.L1
+[      ]+48d168:[      ]+6400098d[     ]+bge[  ]+\$t0, \$t1, 8[        ]+# 48d170 <.L1\+0x48d170>
+[      ]+48d16c:[      ]+532e97ed[     ]+b[    ]+-4772204[     ]+# 0 <.L1>
+[      ]+48d16c: R_LARCH_B26[  ]+.L1
+[      ]+48d170:[      ]+640009ac[     ]+bge[  ]+\$t1, \$t0, 8[        ]+# 48d178 <.L1\+0x48d178>
+[      ]+48d174:[      ]+532e8fed[     ]+b[    ]+-4772212[     ]+# 0 <.L1>
+[      ]+48d174: R_LARCH_B26[  ]+.L1
+[      ]+48d178:[      ]+64000980[     ]+bgez[         ]+\$t0, 8[      ]+# 48d180 <.L1\+0x48d180>
+[      ]+48d17c:[      ]+532e87ed[     ]+b[    ]+-4772220[     ]+# 0 <.L1>
+[      ]+48d17c: R_LARCH_B26[  ]+.L1
+[      ]+48d180:[      ]+6400080c[     ]+blez[         ]+\$t0, 8[      ]+# 48d188 <.L1\+0x48d188>
+[      ]+48d184:[      ]+532e7fed[     ]+b[    ]+-4772228[     ]+# 0 <.L1>
+[      ]+48d184: R_LARCH_B26[  ]+.L1
+[      ]+48d188:[      ]+600009ac[     ]+blt[  ]+\$t1, \$t0, 8[        ]+# 48d190 <.L1\+0x48d190>
+[      ]+48d18c:[      ]+532e77ed[     ]+b[    ]+-4772236[     ]+# 0 <.L1>
+[      ]+48d18c: R_LARCH_B26[  ]+.L1
+[      ]+48d190:[      ]+6000098d[     ]+blt[  ]+\$t0, \$t1, 8[        ]+# 48d198 <.L1\+0x48d198>
+[      ]+48d194:[      ]+532e6fed[     ]+b[    ]+-4772244[     ]+# 0 <.L1>
+[      ]+48d194: R_LARCH_B26[  ]+.L1
+[      ]+48d198:[      ]+6000080c[     ]+bgtz[         ]+\$t0, 8[      ]+# 48d1a0 <.L1\+0x48d1a0>
+[      ]+48d19c:[      ]+532e67ed[     ]+b[    ]+-4772252[     ]+# 0 <.L1>
+[      ]+48d19c: R_LARCH_B26[  ]+.L1
+[      ]+48d1a0:[      ]+60000980[     ]+bltz[         ]+\$t0, 8[      ]+# 48d1a8 <.L1\+0x48d1a8>
+[      ]+48d1a4:[      ]+532e5fed[     ]+b[    ]+-4772260[     ]+# 0 <.L1>
+[      ]+48d1a4: R_LARCH_B26[  ]+.L1
+[      ]+48d1a8:[      ]+6c00098d[     ]+bgeu[         ]+\$t0, \$t1, 8[        ]+# 48d1b0 <.L1\+0x48d1b0>
+[      ]+48d1ac:[      ]+532e57ed[     ]+b[    ]+-4772268[     ]+# 0 <.L1>
+[      ]+48d1ac: R_LARCH_B26[  ]+.L1
+[      ]+48d1b0:[      ]+6c0009ac[     ]+bgeu[         ]+\$t1, \$t0, 8[        ]+# 48d1b8 <.L1\+0x48d1b8>
+[      ]+48d1b4:[      ]+532e4fed[     ]+b[    ]+-4772276[     ]+# 0 <.L1>
+[      ]+48d1b4: R_LARCH_B26[  ]+.L1
+[      ]+48d1b8:[      ]+680009ac[     ]+bltu[         ]+\$t1, \$t0, 8[        ]+# 48d1c0 <.L1\+0x48d1c0>
+[      ]+48d1bc:[      ]+532e47ed[     ]+b[    ]+-4772284[     ]+# 0 <.L1>
+[      ]+48d1bc: R_LARCH_B26[  ]+.L1
+[      ]+48d1c0:[      ]+6800098d[     ]+bltu[         ]+\$t0, \$t1, 8[        ]+# 48d1c8 <.L1\+0x48d1c8>
+[      ]+48d1c4:[      ]+532e3fed[     ]+b[    ]+-4772292[     ]+# 0 <.L1>
+[      ]+48d1c4: R_LARCH_B26[  ]+.L1
+[      ]+48d1c8:[      ]+44000980[     ]+bnez[         ]+\$t0, 8[      ]+# 48d1d0 <.L1\+0x48d1d0>
+[      ]+48d1cc:[      ]+532e37ed[     ]+b[    ]+-4772300[     ]+# 0 <.L1>
+[      ]+48d1cc: R_LARCH_B26[  ]+.L1
+[      ]+48d1d0:[      ]+40000980[     ]+beqz[         ]+\$t0, 8[      ]+# 48d1d8 <.L1\+0x48d1d8>
+[      ]+48d1d4:[      ]+532e2fed[     ]+b[    ]+-4772308[     ]+# 0 <.L1>
+[      ]+48d1d4: R_LARCH_B26[  ]+.L1
+[      ]+48d1d8:[      ]+48000900[     ]+bcnez[        ]+\$fcc0, 8[    ]+# 48d1e0 <.L1\+0x48d1e0>
+[      ]+48d1dc:[      ]+532e27ed[     ]+b[    ]+-4772316[     ]+# 0 <.L1>
+[      ]+48d1dc: R_LARCH_B26[  ]+.L1
+[      ]+48d1e0:[      ]+48000800[     ]+bceqz[        ]+\$fcc0, 8[    ]+# 48d1e8 <.L1\+0x48d1e8>
+[      ]+48d1e4:[      ]+532e1fed[     ]+b[    ]+-4772324[     ]+# 0 <.L1>
+[      ]+48d1e4: R_LARCH_B26[  ]+.L1
 
--- /dev/null
+#as:
+#objdump: -dr
+
+.*:[    ]+file format .*
+
+
+Disassembly of section .text:
+
+0* <.L1>:
+[      ]+...
+[      ]+20000:[       ]+5a00018d[     ]+beq[  ]+\$t0, \$t1, -131072[  ]+# 0 <.L1>
+[      ]+20000: R_LARCH_B16[   ]+.L1
+[      ]+20004:[       ]+5c00098d[     ]+bne[  ]+\$t0, \$t1, 8[        ]+# 2000c <.L1\+0x2000c>
+[      ]+20008:[       ]+51fffbff[     ]+b[    ]+-131080[      ]+# 0 <.L1>
+[      ]+20008: R_LARCH_B26[   ]+.L1
+[      ]+2000c:[       ]+5c00098d[     ]+bne[  ]+\$t0, \$t1, 8[        ]+# 20014 <.L1\+0x20014>
+[      ]+20010:[       ]+52000000[     ]+b[    ]+131072[       ]+# 40010 <.L2>
+[      ]+20010: R_LARCH_B26[   ]+.L2
+[      ]+20014:[       ]+59fffd8d[     ]+beq[  ]+\$t0, \$t1, 131068[   ]+# 40010 <.L2>
+[      ]+20014: R_LARCH_B16[   ]+.L2
+[      ]+...
+0*40010 <.L2>:
+[      ]+...
+[      ]+440010:[      ]+40000190[     ]+beqz[         ]+\$t0, -4194304[       ]+# 40010 <.L2>
+[      ]+440010: R_LARCH_B21[  ]+.L2
+[      ]+440014:[      ]+44000980[     ]+bnez[         ]+\$t0, 8[      ]+# 44001c <.L2\+0x40000c>
+[      ]+440018:[      ]+53fffbef[     ]+b[    ]+-4194312[     ]+# 40010 <.L2>
+[      ]+440018: R_LARCH_B26[  ]+.L2
+[      ]+44001c:[      ]+44000980[     ]+bnez[         ]+\$t0, 8[      ]+# 440024 <.L2\+0x400014>
+[      ]+440020:[      ]+50000010[     ]+b[    ]+4194304[      ]+# 840020 <.L3>
+[      ]+440020: R_LARCH_B26[  ]+.L3
+[      ]+440024:[      ]+43fffd8f[     ]+beqz[         ]+\$t0, 4194300[        ]+# 840020 <.L3>
+[      ]+440024: R_LARCH_B21[  ]+.L3
+[      ]+...
+0*840020 <.L3>:
+[      ]+840020:[      ]+5800018d[     ]+beq[  ]+\$t0, \$t1, 0[        ]+# 840020 <.L3>
+[      ]+840020: R_LARCH_B16[  ]+.L4
+0*840024 <.L5>:
+[      ]+840024:[      ]+40000180[     ]+beqz[         ]+\$t0, 0[      ]+# 840024 <.L5>
+[      ]+840024: R_LARCH_B21[  ]+.L5