[06/21] Update branch relaxation

Message ID 20250402121759.1962001-7-jovan.dmitrovic@htecgroup.com
State New
Headers
Series Integrate MIPS-Specific Support |

Commit Message

Jovan Dmitrovic April 2, 2025, 12:18 p.m. UTC
  From: Matthew Fortune <matthew.fortune@imgtec.com>

Modification of opcode_descriptor Structure:
The dst_opcode field has been added, allowing the specification of a
target opcode for instructions undergoing relaxation. This enhances
the instruction relaxation capabilities by enabling the relaxation to
result in different target opcodes, rather than being limited to a
binary mask.

Addition of New Opcode Descriptions:
New constants, such as micromips_bc_PC26 and micromips_bc_PC21, have
been defined to include opcodes for instructions like bc, beqzc, and
bnezc. These instructions now also include a target opcode
(e.g., 0xcc00 for bc), enabling relaxation with optimization in mind.

Expansion of Relaxation Logic:
The logic in the _bfd_mips_elf_relax_section function has been extended
to support new instructions and opcode relaxation based on the values
in dst_opcode. This allows transitions from more complex opcodes (such
as 32-bit opcodes) to simpler versions (e.g., 16-bit opcodes) wherever
feasible.

Benefits:
Reduces instruction size through relaxation.
Provides better control over relaxation within the microMIPS
architecture. Adding dst_opcode enhances the ability to define
relaxations for various groups of instructions.

Cherry-picked eddaf9c
from https://github.com/MIPS/binutils-gdb

Signed-off-by: Matthew Fortune <matthew.fortune@mips.com>
Signed-off-by: Faraz Shahbazker <fshahbazker@wavecomp.com>
Signed-off-by: Milica Matic <milica.matic@htecgroup.com>
---
 bfd/elfxx-mips.c | 192 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 134 insertions(+), 58 deletions(-)
  

Patch

diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 1dfd877b210..6345e782776 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -13865,6 +13865,7 @@  mips_elf_relax_delete_bytes (bfd *abfd,
 struct opcode_descriptor {
   unsigned long match;
   unsigned long mask;
+  unsigned long dst_opcode;	/* target opcode.  */
 };
 
 /* The $ra register aka $31.  */
@@ -13884,29 +13885,40 @@  struct opcode_descriptor {
 
 /* 32-bit and 16-bit branches.  */
 
+static const struct opcode_descriptor micromips_bc_PC26[] = {
+  { 0x94000000, 0xfc000000, 0xcc00 }, /* bc */
+  { 0, 0, 0 }  /* End marker for find_match().  */
+};
+
+static const struct opcode_descriptor micromips_bc_PC21[] = {
+  { 0x80000000, 0xfc000000, 0x8c00 }, /* beqzc */
+  { 0xa0000000, 0xfc000000, 0xac00 }, /* bnezc */
+  { 0, 0, 0 }  /* End marker for find_match().  */
+};
+
 static const struct opcode_descriptor b_insns_32[] = {
-  { /* "b",	"p",		*/ 0x40400000, 0xffff0000 }, /* bgez 0 */
-  { /* "b",	"p",		*/ 0x94000000, 0xffff0000 }, /* beq 0, 0 */
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "b",	"p",		*/ 0x40400000, 0xffff0000, 0 }, /* bgez 0 */
+  { /* "b",	"p",		*/ 0x94000000, 0xffff0000, 0 }, /* beq 0, 0 */
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 static const struct opcode_descriptor bc_insn_32 =
-  { /* "bc(1|2)(ft)", "N,p",	*/ 0x42800000, 0xfec30000 };
+  { /* "bc(1|2)(ft)", "N,p",	*/ 0x42800000, 0xfec30000, 0 };
 
 static const struct opcode_descriptor bz_insn_32 =
-  { /* "b(g|l)(e|t)z", "s,p",	*/ 0x40000000, 0xff200000 };
+  { /* "b(g|l)(e|t)z", "s,p",	*/ 0x40000000, 0xff200000, 0 };
 
 static const struct opcode_descriptor bzal_insn_32 =
-  { /* "b(ge|lt)zal", "s,p",	*/ 0x40200000, 0xffa00000 };
+  { /* "b(ge|lt)zal", "s,p",	*/ 0x40200000, 0xffa00000, 0 };
 
 static const struct opcode_descriptor beq_insn_32 =
-  { /* "b(eq|ne)", "s,t,p",	*/ 0x94000000, 0xdc000000 };
+  { /* "b(eq|ne)", "s,t,p",	*/ 0x94000000, 0xdc000000, 0 };
 
 static const struct opcode_descriptor b_insn_16 =
-  { /* "b",	"mD",		*/ 0xcc00,     0xfc00 };
+  { /* "b",	"mD",		*/ 0xcc00,     0xfc00, 0 };
 
 static const struct opcode_descriptor bz_insn_16 =
-  { /* "b(eq|ne)z", "md,mE",	*/ 0x8c00,     0xdc00 };
+  { /* "b(eq|ne)z", "md,mE",	*/ 0x8c00,     0xdc00, 0 };
 
 
 /* 32-bit and 16-bit branch EQ and NE zero.  */
@@ -13918,27 +13930,27 @@  static const struct opcode_descriptor bz_insn_16 =
 #define BZC32_REG_FIELD(r) (((r) & 0x1f) << 16)
 
 static const struct opcode_descriptor bz_rs_insns_32[] = {
-  { /* "beqz",	"s,p",		*/ 0x94000000, 0xffe00000 },
-  { /* "bnez",	"s,p",		*/ 0xb4000000, 0xffe00000 },
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "beqz",	"s,p",		*/ 0x94000000, 0xffe00000, 0 },
+  { /* "bnez",	"s,p",		*/ 0xb4000000, 0xffe00000, 0 },
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 static const struct opcode_descriptor bz_rt_insns_32[] = {
-  { /* "beqz",	"t,p",		*/ 0x94000000, 0xfc01f000 },
-  { /* "bnez",	"t,p",		*/ 0xb4000000, 0xfc01f000 },
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "beqz",	"t,p",		*/ 0x94000000, 0xfc01f000, 0 },
+  { /* "bnez",	"t,p",		*/ 0xb4000000, 0xfc01f000, 0 },
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 static const struct opcode_descriptor bzc_insns_32[] = {
-  { /* "beqzc",	"s,p",		*/ 0x40e00000, 0xffe00000 },
-  { /* "bnezc",	"s,p",		*/ 0x40a00000, 0xffe00000 },
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "beqzc",	"s,p",		*/ 0x40e00000, 0xffe00000, 0 },
+  { /* "bnezc",	"s,p",		*/ 0x40a00000, 0xffe00000, 0 },
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 static const struct opcode_descriptor bz_insns_16[] = {
-  { /* "beqz",	"md,mE",	*/ 0x8c00,     0xfc00 },
-  { /* "bnez",	"md,mE",	*/ 0xac00,     0xfc00 },
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "beqz",	"md,mE",	*/ 0x8c00,     0xfc00, 0 },
+  { /* "bnez",	"md,mE",	*/ 0xac00,     0xfc00, 0 },
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 /* Switch between a 5-bit register index and its 3-bit shorthand.  */
@@ -13950,83 +13962,83 @@  static const struct opcode_descriptor bz_insns_16[] = {
 /* 32-bit instructions with a delay slot.  */
 
 static const struct opcode_descriptor jal_insn_32_bd16 =
-  { /* "jals",	"a",		*/ 0x74000000, 0xfc000000 };
+  { /* "jals",	"a",		*/ 0x74000000, 0xfc000000, 0 };
 
 static const struct opcode_descriptor jal_insn_32_bd32 =
-  { /* "jal",	"a",		*/ 0xf4000000, 0xfc000000 };
+  { /* "jal",	"a",		*/ 0xf4000000, 0xfc000000, 0 };
 
 static const struct opcode_descriptor jal_x_insn_32_bd32 =
-  { /* "jal[x]", "a",		*/ 0xf0000000, 0xf8000000 };
+  { /* "jal[x]", "a",		*/ 0xf0000000, 0xf8000000, 0 };
 
 static const struct opcode_descriptor j_insn_32 =
-  { /* "j",	"a",		*/ 0xd4000000, 0xfc000000 };
+  { /* "j",	"a",		*/ 0xd4000000, 0xfc000000, 0 };
 
 static const struct opcode_descriptor jalr_insn_32 =
-  { /* "jalr[.hb]", "t,s",	*/ 0x00000f3c, 0xfc00efff };
+  { /* "jalr[.hb]", "t,s",	*/ 0x00000f3c, 0xfc00efff, 0 };
 
 /* This table can be compacted, because no opcode replacement is made.  */
 
 static const struct opcode_descriptor ds_insns_32_bd16[] = {
-  { /* "jals",	"a",		*/ 0x74000000, 0xfc000000 },
+  { /* "jals",	"a",		*/ 0x74000000, 0xfc000000, 0 },
 
-  { /* "jalrs[.hb]", "t,s",	*/ 0x00004f3c, 0xfc00efff },
-  { /* "b(ge|lt)zals", "s,p",	*/ 0x42200000, 0xffa00000 },
+  { /* "jalrs[.hb]", "t,s",	*/ 0x00004f3c, 0xfc00efff, 0 },
+  { /* "b(ge|lt)zals", "s,p",	*/ 0x42200000, 0xffa00000, 0 },
 
-  { /* "b(g|l)(e|t)z", "s,p",	*/ 0x40000000, 0xff200000 },
-  { /* "b(eq|ne)", "s,t,p",	*/ 0x94000000, 0xdc000000 },
-  { /* "j",	"a",		*/ 0xd4000000, 0xfc000000 },
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "b(g|l)(e|t)z", "s,p",	*/ 0x40000000, 0xff200000, 0 },
+  { /* "b(eq|ne)", "s,t,p",	*/ 0x94000000, 0xdc000000, 0 },
+  { /* "j",	"a",		*/ 0xd4000000, 0xfc000000, 0 },
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 /* This table can be compacted, because no opcode replacement is made.  */
 
 static const struct opcode_descriptor ds_insns_32_bd32[] = {
-  { /* "jal[x]", "a",		*/ 0xf0000000, 0xf8000000 },
+  { /* "jal[x]", "a",		*/ 0xf0000000, 0xf8000000, 0 },
 
-  { /* "jalr[.hb]", "t,s",	*/ 0x00000f3c, 0xfc00efff },
-  { /* "b(ge|lt)zal", "s,p",	*/ 0x40200000, 0xffa00000 },
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "jalr[.hb]", "t,s",	*/ 0x00000f3c, 0xfc00efff, 0 },
+  { /* "b(ge|lt)zal", "s,p",	*/ 0x40200000, 0xffa00000, 0 },
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 
 /* 16-bit instructions with a delay slot.  */
 
 static const struct opcode_descriptor jalr_insn_16_bd16 =
-  { /* "jalrs",	"my,mj",	*/ 0x45e0,     0xffe0 };
+  { /* "jalrs",	"my,mj",	*/ 0x45e0,     0xffe0, 0 };
 
 static const struct opcode_descriptor jalr_insn_16_bd32 =
-  { /* "jalr",	"my,mj",	*/ 0x45c0,     0xffe0 };
+  { /* "jalr",	"my,mj",	*/ 0x45c0,     0xffe0, 0 };
 
 static const struct opcode_descriptor jr_insn_16 =
-  { /* "jr",	"mj",		*/ 0x4580,     0xffe0 };
+  { /* "jr",	"mj",		*/ 0x4580,     0xffe0, 0 };
 
 #define JR16_REG(opcode) ((opcode) & 0x1f)
 
 /* This table can be compacted, because no opcode replacement is made.  */
 
 static const struct opcode_descriptor ds_insns_16_bd16[] = {
-  { /* "jalrs",	"my,mj",	*/ 0x45e0,     0xffe0 },
+  { /* "jalrs",	"my,mj",	*/ 0x45e0,     0xffe0, 0 },
 
-  { /* "b",	"mD",		*/ 0xcc00,     0xfc00 },
-  { /* "b(eq|ne)z", "md,mE",	*/ 0x8c00,     0xdc00 },
-  { /* "jr",	"mj",		*/ 0x4580,     0xffe0 },
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "b",	"mD",		*/ 0xcc00,     0xfc00, 0 },
+  { /* "b(eq|ne)z", "md,mE",	*/ 0x8c00,     0xdc00, 0 },
+  { /* "jr",	"mj",		*/ 0x4580,     0xffe0, 0 },
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 
 /* LUI instruction.  */
 
 static const struct opcode_descriptor lui_insn =
- { /* "lui",	"s,u",		*/ 0x41a00000, 0xffe00000 };
+ { /* "lui",	"s,u",		*/ 0x41a00000, 0xffe00000, 0 };
 
 
 /* ADDIU instruction.  */
 
 static const struct opcode_descriptor addiu_insn =
-  { /* "addiu",	"t,r,j",	*/ 0x30000000, 0xfc000000 };
+  { /* "addiu",	"t,r,j",	*/ 0x30000000, 0xfc000000, 0 };
 
 static const struct opcode_descriptor addiupc_insn =
-  { /* "addiu",	"mb,$pc,mQ",	*/ 0x78000000, 0xfc000000 };
+  { /* "addiu",	"mb,$pc,mQ",	*/ 0x78000000, 0xfc000000, 0 };
 
 #define ADDIUPC_REG_FIELD(r) \
   (((2 <= (r) && (r) <= 7) ? (r) : ((r) - 16)) << 23)
@@ -14043,22 +14055,22 @@  static const struct opcode_descriptor addiupc_insn =
 #define MOVE16_RS_FIELD(r) (((r) & 0x1f)     )
 
 static const struct opcode_descriptor move_insns_32[] = {
-  { /* "move",	"d,s",		*/ 0x00000290, 0xffe007ff }, /* or   d,s,$0 */
-  { /* "move",	"d,s",		*/ 0x00000150, 0xffe007ff }, /* addu d,s,$0 */
-  { 0, 0 }  /* End marker for find_match().  */
+  { /* "move",	"d,s",		*/ 0x00000290, 0xffe007ff, 0 }, /* or   d,s,$0 */
+  { /* "move",	"d,s",		*/ 0x00000150, 0xffe007ff, 0 }, /* addu d,s,$0 */
+  { 0, 0, 0 }  /* End marker for find_match().  */
 };
 
 static const struct opcode_descriptor move_insn_16 =
-  { /* "move",	"mp,mj",	*/ 0x0c00,     0xfc00 };
+  { /* "move",	"mp,mj",	*/ 0x0c00,     0xfc00, 0 };
 
 
 /* NOP instructions.  */
 
 static const struct opcode_descriptor nop_insn_32 =
-  { /* "nop",	"",		*/ 0x00000000, 0xffffffff };
+  { /* "nop",	"",		*/ 0x00000000, 0xffffffff, 0 };
 
 static const struct opcode_descriptor nop_insn_16 =
-  { /* "nop",	"",		*/ 0x0c00,     0xffff };
+  { /* "nop",	"",		*/ 0x0c00,     0xffff, 0 };
 
 
 /* Instruction match support.  */
@@ -14265,12 +14277,16 @@  _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
 	 to delete these bytes starting at irel->r_offset.  */
       int delcnt = 0;
       int deloff = 0;
+      int opcidx = 0;
 
       /* If this isn't something that can be relaxed, then ignore
 	 this reloc.  */
       if (r_type != R_MICROMIPS_HI16
 	  && r_type != R_MICROMIPS_PC16_S1
-	  && r_type != R_MICROMIPS_26_S1)
+	  && r_type != R_MICROMIPS_26_S1
+	  && r_type != R_MICROMIPS_PC26_S1
+	  && r_type != R_MICROMIPS_PC21_S1
+	  && r_type != R_MICROMIPS_LO16)
 	continue;
 
       /* Get the section contents if we haven't done so already.  */
@@ -14363,10 +14379,70 @@  _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
 		- (sec->output_section->vma + sec->output_offset)
 		- irel->r_offset);
 
-      /* R_MICROMIPS_HI16 / LUI relaxation to nil, performing relaxation
-	 of corresponding R_MICROMIPS_LO16 to R_MICROMIPS_HI0_LO16 or
-	 R_MICROMIPS_PC23_S2.  The R_MICROMIPS_PC23_S2 condition is
+      /* R_MICROMIPS_PC26_S1 relaxation to R_MICROMIPS_PC10_S1.  */
+      if (!insn32
+	  && r_type == R_MICROMIPS_PC26_S1
+	  && (opcidx = find_match (opcode, micromips_bc_PC26)) >= 0)
+	{
+	  bfd_vma addend = opcode & 0x03ffffff;
+	  unsigned int new_opcode;
+
+	  addend = _bfd_mips_elf_sign_extend (addend << 1, 27);
+	  new_opcode = micromips_bc_PC26[opcidx].dst_opcode;
+
+	  if (! mips_elf_overflow_p ((pcrval + addend), 11))
+	    {
+	      /* Calculate new addend.  */
+	      addend = (addend + 4 - 2) >> 1;
+
+	      /* Fix the relocation's type.  */
+	      irel->r_info = ELF32_R_INFO (r_symndx, R_MICROMIPS_PC10_S1);
+
+	      /* Replace the 32-bit bc with a 16-bit bc16.  */
+	      bfd_put_16 (abfd, new_opcode | (addend & 0x3ff), ptr);
+
+	      /* Delete 2 bytes from irel->r_offset + 2.  */
+	      delcnt = 2;
+	      deloff = 2;
+	    }
+	}
+
+      /* R_MICROMIPS_PC21_S1 relaxation to R_MICROMIPS_PC7_S1.  */
+      if (!insn32
+	  && r_type == R_MICROMIPS_PC21_S1
+	  && (opcidx = find_match (opcode, micromips_bc_PC21)) >= 0)
+	{
+	  unsigned int reg, new_opcode;
+	  bfd_vma addend = opcode & 0x001fffff;
+
+	  reg = OP32_TREG (opcode);
+	  addend = _bfd_mips_elf_sign_extend (addend << 1, 22);
+	  new_opcode = micromips_bc_PC21[opcidx].dst_opcode;
+
+	  if (! mips_elf_overflow_p ((pcrval + addend), 8)
+	      && ((reg >= 2 && reg <= 7) || reg == 16 || reg == 17))
+	    {
+	      /* Calculate new addend.  */
+	      addend = (addend + 4 - 2) >> 1;
+
+	      /* Fix the relocation's type.  */
+	      irel->r_info = ELF32_R_INFO (r_symndx, R_MICROMIPS_PC7_S1);
 
+	      /* Replace the 32-bit bc with a 16-bit bc16.  */
+	      bfd_put_16 (abfd,
+			  (new_opcode
+			   | BZ16_REG_FIELD (reg)
+			   | (addend & 0x7f)), ptr);
+
+	      /* Delete 2 bytes from irel->r_offset + 2.  */
+	      delcnt = 2;
+	      deloff = 2;
+	    }
+	}
+
+      /* R_MICROMIPS_HI16 / LUI relaxation to nil, performing relaxation
+         of corresponding R_MICROMIPS_LO16 to R_MICROMIPS_HI0_LO16 or
+         R_MICROMIPS_PC23_S2.  The R_MICROMIPS_PC23_S2 condition is
 	   (symval % 4 == 0 && IS_BITSIZE (pcrval, 25))
 
 	 where pcrval has first to be adjusted to apply against the LO16