[PATCH 2/3] RISC-V: Refactor branch relaxation state encoding for extensibility

Ethan Y. C. Liang ycl669@andestech.com
Wed Nov 26 08:47:54 GMT 2025


The refactoring makes the relaxation logic extensible so that any
branch encoding, whether standard or vendor-defined, can participate
in assembler-side relaxation.

Contributors:
  Ethan Y. C. Liang <ycl669@andestech.com>
  Yu-Qi Liu <yuqiliu@andestech.com>

gas/ChangeLog

	* config/tc-riscv.c: Refactor branch relaxation state encoding.
---
 gas/config/tc-riscv.c | 134 +++++++++++++++++++++++++++++++-----------
 1 file changed, 100 insertions(+), 34 deletions(-)

diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 1893e4ce11d..35deb8dcdae 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -474,16 +474,28 @@ static bool explicit_priv_attr = false;
 static char *expr_parse_end;
 
 /* Macros for encoding relaxation state for RVC branches and far jumps.  */
-#define RELAX_BRANCH_ENCODE(uncond, rvc, length)	\
-  ((relax_substateT) 					\
-   (0xc0000000						\
-    | ((uncond) ? 1 : 0)				\
-    | ((rvc) ? 2 : 0)					\
-    | ((length) << 2)))
+#define RELAX_BRANCH_ENCODE(rvc, length, reloc)	\
+  ((relax_substateT) 				\
+   (0xc0000000					\
+    | ((rvc) ? 1 : 0)				\
+    | ((length) << 1)				\
+    | ((reloc) << 5)))
 #define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
-#define RELAX_BRANCH_LENGTH(i) (((i) >> 2) & 0xF)
-#define RELAX_BRANCH_RVC(i) (((i) & 2) != 0)
-#define RELAX_BRANCH_UNCOND(i) (((i) & 1) != 0)
+#define RELAX_BRANCH_RELOC(i) (((i) >> 5) & 0xFF)
+#define RELAX_BRANCH_LENGTH(i) (((i) >> 1) & 0xF)
+#define RELAX_BRANCH_RVC(i) (((i) & 1) != 0)
+
+enum relax_branch_reloc
+{
+  RELOC_INVALID = 0,
+  RELOC_BRANCH,
+  RELOC_JAL
+};
+
+#define ENUM_RELAX_BRANCH_RELOC(reloc)			\
+  ((reloc) == BFD_RELOC_12_PCREL    ? RELOC_BRANCH	\
+   : (reloc) == BFD_RELOC_RISCV_JMP ? RELOC_JAL		\
+				    : RELOC_INVALID)
 
 /* Is the given value a sign-extended 32-bit value?  */
 #define IS_SEXT_32BIT_NUM(x)						\
@@ -813,17 +825,20 @@ add_relaxed_insn (struct riscv_cl_insn *insn, int max_chars, int var,
 static unsigned
 relaxed_branch_length (fragS *fragp, asection *sec, int update)
 {
-  int jump, rvc, length = 8;
+  int rvc, length = 8;
+  enum relax_branch_reloc reloc;
 
   if (!fragp)
     return length;
 
-  jump = RELAX_BRANCH_UNCOND (fragp->fr_subtype);
   rvc = RELAX_BRANCH_RVC (fragp->fr_subtype);
   length = RELAX_BRANCH_LENGTH (fragp->fr_subtype);
+  reloc = RELAX_BRANCH_RELOC (fragp->fr_subtype);
+
+  gas_assert (reloc != RELOC_INVALID);
 
   /* Assume jumps are in range; the linker will catch any that aren't.  */
-  length = jump ? 4 : 8;
+  length = (reloc == RELOC_JAL) ? 4 : 8;
 
   if (fragp->fr_symbol != NULL
       && S_IS_DEFINED (fragp->fr_symbol)
@@ -831,19 +846,31 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
       && sec == S_GET_SEGMENT (fragp->fr_symbol))
     {
       offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
-      bfd_vma rvc_range = jump ? RVC_JUMP_REACH : RVC_BRANCH_REACH;
       val -= fragp->fr_address + fragp->fr_fix;
 
-      if (rvc && (bfd_vma)(val + rvc_range/2) < rvc_range)
-	length = 2;
-      else if ((bfd_vma)(val + RISCV_BRANCH_REACH/2) < RISCV_BRANCH_REACH)
-	length = 4;
-      else if (!jump && rvc)
-	length = 6;
+      switch (reloc)
+	{
+	case RELOC_BRANCH:
+	  if (rvc && (bfd_vma)(val + RVC_BRANCH_REACH / 2) < RVC_BRANCH_REACH)
+	    length = 2;
+	  else if ((bfd_vma)(val + RISCV_BRANCH_REACH / 2)
+		   < RISCV_BRANCH_REACH)
+	    length = 4;
+	  else if (rvc)
+	    length = 6;
+	  break;
+	case RELOC_JAL:
+	  if (rvc && (bfd_vma)(val + RVC_JUMP_REACH / 2) < RVC_JUMP_REACH)
+	    length = 2;
+	  break;
+	/* Add new branch/jump cases here.  */
+	default:
+	  abort ();
+	}
     }
 
   if (update)
-    fragp->fr_subtype = RELAX_BRANCH_ENCODE (jump, rvc, length);
+    fragp->fr_subtype = RELAX_BRANCH_ENCODE (rvc, length, reloc);
 
   return length;
 }
@@ -2003,7 +2030,6 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
       if (reloc_type == BFD_RELOC_12_PCREL
 	  || reloc_type == BFD_RELOC_RISCV_JMP)
 	{
-	  int j = reloc_type == BFD_RELOC_RISCV_JMP;
 	  int best_case = insn_length (ip);
 	  unsigned worst_case = relaxed_branch_length (NULL, NULL, 0);
 
@@ -2013,10 +2039,11 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
 	      return;
 	    }
 
-	  add_relaxed_insn (ip, worst_case, best_case,
-			    RELAX_BRANCH_ENCODE (j, best_case == 2, worst_case),
-			    address_expr->X_add_symbol,
-			    address_expr->X_add_number);
+	  add_relaxed_insn (
+	      ip, worst_case, best_case,
+	      RELAX_BRANCH_ENCODE (best_case == 2, worst_case,
+				   ENUM_RELAX_BRANCH_RELOC (reloc_type)),
+	      address_expr->X_add_symbol, address_expr->X_add_number);
 	  return;
 	}
       else
@@ -5455,6 +5482,7 @@ md_convert_frag_branch (fragS *fragp)
   fixS *fixp;
   insn_t insn;
   int rs1, reloc;
+  enum relax_branch_reloc fake_reloc;
 
   buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
 
@@ -5462,8 +5490,12 @@ md_convert_frag_branch (fragS *fragp)
   exp.X_add_symbol = fragp->fr_symbol;
   exp.X_add_number = fragp->fr_offset;
 
+  fake_reloc = RELAX_BRANCH_RELOC (fragp->fr_subtype);
+
   gas_assert (fragp->fr_var == RELAX_BRANCH_LENGTH (fragp->fr_subtype));
 
+  gas_assert (fake_reloc != RELOC_INVALID);
+
   if (RELAX_BRANCH_RVC (fragp->fr_subtype))
     {
       switch (RELAX_BRANCH_LENGTH (fragp->fr_subtype))
@@ -5489,16 +5521,33 @@ md_convert_frag_branch (fragS *fragp)
 	  case 6:
 	    /* Invert the branch condition.  Branch over the jump.  */
 	    insn = bfd_getl16 (buf);
-	    insn ^= MATCH_C_BEQZ ^ MATCH_C_BNEZ;
-	    insn |= ENCODE_CBTYPE_IMM (6);
+	    if (((insn & MASK_C_BEQZ) == MATCH_C_BEQZ)
+		|| ((insn & MASK_C_BNEZ) == MATCH_C_BNEZ))
+	      {
+		insn ^= MATCH_C_BEQZ ^ MATCH_C_BNEZ;
+		insn |= ENCODE_CBTYPE_IMM (6);
+	      }
+	    /* Add new rvc branch cases here.  */
+	    else
+	      abort ();
 	    bfd_putl16 (insn, buf);
 	    buf += 2;
 	    goto jump;
 
 	  case 2:
 	    /* Just keep the RVC branch.  */
-	    reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
-		    ? BFD_RELOC_RISCV_RVC_JUMP : BFD_RELOC_RISCV_RVC_BRANCH;
+	    switch (fake_reloc)
+	      {
+	      case RELOC_BRANCH:
+		reloc = BFD_RELOC_RISCV_RVC_BRANCH;
+		break;
+	      case RELOC_JAL:
+		reloc = BFD_RELOC_RISCV_RVC_JUMP;
+		break;
+	      /* Add new rvc branch/jump cases here.  */
+	      default:
+		abort ();
+	      }
 	    /* Maybe it is a vendor-specific relocation in the future.  */
 	    add_vendor_symbol_relocation (
 		fragp, buf - (bfd_byte *)fragp->fr_literal, 2,
@@ -5516,12 +5565,19 @@ md_convert_frag_branch (fragS *fragp)
   switch (RELAX_BRANCH_LENGTH (fragp->fr_subtype))
     {
     case 8:
-      gas_assert (!RELAX_BRANCH_UNCOND (fragp->fr_subtype));
+      gas_assert (fake_reloc != RELOC_JAL);
 
       /* Invert the branch condition.  Branch over the jump.  */
       insn = bfd_getl32 (buf);
-      insn ^= MATCH_BEQ ^ MATCH_BNE;
-      insn |= ENCODE_BTYPE_IMM (8);
+      if (((insn & MASK_BEQ) == MATCH_BEQ)
+	  || ((insn & MASK_BNE) == MATCH_BNE))
+	{
+	  insn ^= MATCH_BEQ ^ MATCH_BNE;
+	  insn |= ENCODE_BTYPE_IMM (8);
+	}
+      /* Add new branch cases here.  */
+      else
+	abort ();
       bfd_putl32 (insn, buf);
       buf += 4;
 
@@ -5534,8 +5590,18 @@ md_convert_frag_branch (fragS *fragp)
       break;
 
     case 4:
-      reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
-	      ? BFD_RELOC_RISCV_JMP : BFD_RELOC_12_PCREL;
+      switch (fake_reloc)
+	{
+	case RELOC_BRANCH:
+	  reloc = BFD_RELOC_12_PCREL;
+	  break;
+	case RELOC_JAL:
+	  reloc = BFD_RELOC_RISCV_JMP;
+	  break;
+	/* Add new branch/jump cases here.  */
+	default:
+	  abort ();
+	}
       /* Maybe it is a vendor-specific relocation in the future.  */
       add_vendor_symbol_relocation (fragp, buf - (bfd_byte *)fragp->fr_literal,
 				    4, riscv_reloc_type_to_vendor_str (reloc));
-- 
2.49.0



More information about the Binutils mailing list