[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