[PATCH] RISCV-V: Add new relocation type for global array accesses with non-constant indices

Jan Beulich jbeulich@suse.com
Thu Jul 9 06:32:20 GMT 2026


On 08.07.2026 13:17, 翁丽琴 wrote:
> --- a/bfd/ChangeLog
> +++ b/bfd/ChangeLog
> @@ -1,3 +1,16 @@
> +2026-06-22  wengliqin  <liqin.weng@spacemit.com>
> +
> +        * reloc.c (BFD_RELOC_RISCV_BASE_IDX_LO12_I, BFD_RELOC_RISCV_BASE_IDX_LO12_S,
> +        BFD_RELOC_RISCV_BASE_IDX_ADD): New
> +        relocation types.
> +        * bfd-in2.h: Regenerate.
> +        * libbfd.h: Regenerate.
> +        * elfnn-riscv.c (perform_relocation): Add support for new REGREL
> +        relocations.
> +        (riscv_elf_relocate_section): Handle REGREL relocations for global
> +        array accesses with non-constant indices.
> +        * elfxx-riscv.c: Define relocation information for new REGREL types.
> +
>  2025-09-16  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
>  
>          * config.bfd <powerpc-*-solaris2*>: Remove.

Btw, as you can see from the adjacent date: ChangeLog files aren't meant to be
patched directly anymore. ChangeLog entries, if you want to have them, are now
supposed to be part of the commit message.

There also looks to be a problem with your mailing of patches: The entry in
context really uses tab indentation (as it is supposed to be), yet throughout
the entire patch there's no single hard tab (when elsewhere they ought to be
used for indentation as well).

> --- a/bfd/elfxx-riscv.c
> +++ b/bfd/elfxx-riscv.c
> @@ -876,7 +876,63 @@ static const reloc_howto_type howto_table[] =
>           false,                                /* partial_inplace */
>           0,                                /* src_mask */
>           ENCODE_ITYPE_IMM (-1U),        /* dst_mask */
> -         false)                         /* pcrel_offset */
> +         false),                        /* pcrel_offset */
> +
> +  /* Reserved slots 66-76.  */
> +  EMPTY_HOWTO (66),
> +  EMPTY_HOWTO (67),
> +  EMPTY_HOWTO (68),
> +  EMPTY_HOWTO (69),
> +  EMPTY_HOWTO (70),
> +  EMPTY_HOWTO (71),
> +  EMPTY_HOWTO (72),
> +  EMPTY_HOWTO (73),
> +  EMPTY_HOWTO (74),
> +  EMPTY_HOWTO (75),
> +  EMPTY_HOWTO (76),
> +
> +  /* Global array accesses with non-constant subscript (base + index).  */
> +  HOWTO (R_RISCV_BASE_IDX_LO12_I,   /* type */
> +         0,                            /* rightshift */
> +         4,                            /* size */
> +         32,                            /* bitsize */

I previously commented on this (and that comment applies to all new entries).

Also the comments on the right all want to have their /* align with one
another (to aid legibility).

> --- a/gas/config/tc-riscv.c
> +++ b/gas/config/tc-riscv.c
> @@ -1958,6 +1958,12 @@ riscv_apply_const_reloc (bfd_reloc_code_real_type reloc_type, bfd_vma value)
>      case BFD_RELOC_RISCV_LO12_I:
>        return ENCODE_ITYPE_IMM (value);
>  
> +    case BFD_RELOC_RISCV_BASE_IDX_LO12_S:
> +      return ENCODE_STYPE_IMM (value);
> +
> +    case BFD_RELOC_RISCV_BASE_IDX_LO12_I:
> +      return ENCODE_ITYPE_IMM (value);

I'm not a maintainer of this file, but I find this odd: Why can't all case
labels wanting the same treatment be grouped together, to limit redundancy?

> @@ -2022,7 +2028,8 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
>    if (reloc_type == BFD_RELOC_RISCV_HI20
>        || reloc_type == BFD_RELOC_RISCV_PCREL_HI20
>        || reloc_type == BFD_RELOC_RISCV_TPREL_HI20
> -      || reloc_type == BFD_RELOC_RISCV_TPREL_ADD)
> +      || reloc_type == BFD_RELOC_RISCV_TPREL_ADD
> +      || reloc_type == BFD_RELOC_RISCV_BASE_IDX_ADD)
>      {
>        frag_wane (frag_now);
>        frag_new (0);
> @@ -2452,30 +2459,27 @@ static const struct percent_op_match percent_op_utype[] =
>    {0, 0}
>  };
>  
> -static const struct percent_op_match percent_op_itype[] =
> -{
> -  {"lo", BFD_RELOC_RISCV_LO12_I},
> -  {"tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_I},
> -  {"pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_I},
> -  {"tlsdesc_load_lo", BFD_RELOC_RISCV_TLSDESC_LOAD_LO12},
> -  {"tlsdesc_add_lo", BFD_RELOC_RISCV_TLSDESC_ADD_LO12},
> -  {0, 0}
> -};
> -
> -static const struct percent_op_match percent_op_stype[] =
> -{
> -  {"lo", BFD_RELOC_RISCV_LO12_S},
> -  {"tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_S},
> -  {"pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_S},
> -  {0, 0}
> -};
> -
> -static const struct percent_op_match percent_op_relax_only[] =
> -{
> -  {"tlsdesc_call", BFD_RELOC_RISCV_TLSDESC_CALL},
> -  {"tprel_add", BFD_RELOC_RISCV_TPREL_ADD},
> -  {0, 0}
> -};
> +static const struct percent_op_match percent_op_itype[]
> +  = {{"lo", BFD_RELOC_RISCV_LO12_I},
> +     {"base_idx_lo", BFD_RELOC_RISCV_BASE_IDX_LO12_I},
> +     {"tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_I},
> +     {"pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_I},
> +     {"tlsdesc_load_lo", BFD_RELOC_RISCV_TLSDESC_LOAD_LO12},
> +     {"tlsdesc_add_lo", BFD_RELOC_RISCV_TLSDESC_ADD_LO12},
> +     {0, 0}};
> +
> +static const struct percent_op_match percent_op_stype[]
> +  = {{"lo", BFD_RELOC_RISCV_LO12_S},
> +     {"base_idx_lo", BFD_RELOC_RISCV_BASE_IDX_LO12_S},
> +     {"tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_S},
> +     {"pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_S},
> +     {0, 0}};
> +
> +static const struct percent_op_match percent_op_relax_only[]
> +  = {{"tlsdesc_call", BFD_RELOC_RISCV_TLSDESC_CALL},
> +     {"tprel_add", BFD_RELOC_RISCV_TPREL_ADD},
> +     {"base_idx_add", BFD_RELOC_RISCV_BASE_IDX_ADD},
> +     {0, 0}};
>  
>  static const struct percent_op_match percent_op_null[] =
>  {

There's far more churn here than necessary. Why don't you simply add the
new lines you need, without otherwise altering what's there?

> @@ -3633,15 +3637,24 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
>  
>              case 'j': /* Sign-extended immediate.  */
>                p = percent_op_itype;
> -              *imm_reloc = BFD_RELOC_RISCV_LO12_I;
> +              if (strncmp (asarg, "%base_idx_lo", 9) == 0)
> +                *imm_reloc = BFD_RELOC_RISCV_BASE_IDX_LO12_I;
> +              else
> +                *imm_reloc = BFD_RELOC_RISCV_LO12_I;
>                goto alu_op;
>              case 'q': /* Store displacement.  */
>                p = percent_op_stype;
> -              *imm_reloc = BFD_RELOC_RISCV_LO12_S;
> +              if (strncmp (asarg, "%base_idx_lo", 9) == 0)
> +                *imm_reloc = BFD_RELOC_RISCV_BASE_IDX_LO12_S;
> +              else
> +                *imm_reloc = BFD_RELOC_RISCV_LO12_S;
>                goto load_store;
>              case 'o': /* Load displacement.  */
>                p = percent_op_itype;
> -              *imm_reloc = BFD_RELOC_RISCV_LO12_I;
> +              if (strncmp (asarg, "%base_idx_lo", 9) == 0)
> +                *imm_reloc = BFD_RELOC_RISCV_BASE_IDX_LO12_I;
> +              else
> +                *imm_reloc = BFD_RELOC_RISCV_LO12_I;
>                goto load_store;
>              case '1':
>                /* This is used for TLS relocations that acts as relaxation

Why are these changes needed? parse_relocation() recognizes and stores the
correct reloc type when %base_idx_lo() is found, doesn't it? The pre-filling
of the pointed-to variable here is to cover the case of no relocation, aiui.

> --- /dev/null
> +++ b/gas/testsuite/gas/riscv/base-idx-add.l
> @@ -0,0 +1,10 @@
> +.*: Assembler messages:
> +.*: Error: illegal operands `amoadd.w x8,x9,%base_idx_add\(sym\)\(x10\)'
> +.*: Error: illegal operands `add a5,a5,a0,0'
> +.*: Error: illegal operands `sh1add a0,a0,a5,0'
> +.*: Error: illegal operands `sh1add.uw a0,a0,a5,0'
> +.*: Error: illegal operands `sh2add a0,a0,a5,0'
> +.*: Error: illegal operands `sh2add.uw a0,a0,a5,0'
> +.*: Error: illegal operands `sh3add a0,a0,a5,0'
> +.*: Error: illegal operands `sh3add.uw a0,a0,a5,0'
> +.*: Error: illegal operands `add.uw a0,a0,a5,0'
> --- /dev/null
> +++ b/gas/testsuite/gas/riscv/base-idx-add.s
> @@ -0,0 +1,19 @@
> +.option arch, +a
> +    # Don't allow base_idx_add in amoadd.
> +        amoadd.w x8,x9,%base_idx_add(sym)(x10)
> +        # Do require base_idx_add in 4-operand add.
> +        add         a5,a5,a0,0
> +        sh1add            a0,a0,a5,0
> +        sh1add.uw        a0,a0,a5,0
> +        sh2add           a0,a0,a5,0
> +        sh2add.uw        a0,a0,a5,0
> +        sh3add            a0,a0,a5,0
> +        sh3add.uw        a0,a0,a5,0
> +        add.uw      a0,a0,a5,0
> +        .globl        sym
> +        .section        .tbss,"awT",@nobits
> +        .align        2
> +        .type        sym, @object
> +        .size        sym, 4
> +sym:
> +        .zero        4

Please help readability here by (again) padding such that (first) operands
align with one another.

Also shouldn't there be another testcase actually testing correct behavior
with proper input?

> --- a/opcodes/riscv-opc.c
> +++ b/opcodes/riscv-opc.c
> @@ -1378,16 +1378,23 @@ const struct riscv_opcode riscv_opcodes[] =
>  {"rorw",      64, INSN_CLASS_ZBB_OR_ZBKB,  "d,s,<", MATCH_RORIW, MASK_RORIW, match_opcode, INSN_ALIAS },
>  
>  /* Zba instructions.  */
> -{"sh1add",     0, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH1ADD, MASK_SH1ADD, match_opcode, 0 },
> -{"sh2add",     0, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH2ADD, MASK_SH2ADD, match_opcode, 0 },
> -{"sh3add",     0, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH3ADD, MASK_SH3ADD, match_opcode, 0 },
> -{"sh1add.uw", 64, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH1ADD_UW, MASK_SH1ADD_UW, match_opcode, 0 },
> -{"sh2add.uw", 64, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH2ADD_UW, MASK_SH2ADD_UW, match_opcode, 0 },
> -{"sh3add.uw", 64, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH3ADD_UW, MASK_SH3ADD_UW, match_opcode, 0 },
> +{"sh1add",     0, INSN_CLASS_ZBA,  "d,s,t",   MATCH_SH1ADD, MASK_SH1ADD, match_opcode, 0 },
> +{"sh1add",     0, INSN_CLASS_ZBA,  "d,s,t,1", MATCH_SH1ADD, MASK_SH1ADD, match_opcode, 0 },
> +{"sh2add",     0, INSN_CLASS_ZBA,  "d,s,t",   MATCH_SH2ADD, MASK_SH2ADD, match_opcode, 0 },
> +{"sh2add",     0, INSN_CLASS_ZBA,  "d,s,t,1", MATCH_SH2ADD, MASK_SH2ADD, match_opcode, 0 },
> +{"sh3add",     0, INSN_CLASS_ZBA,  "d,s,t",   MATCH_SH3ADD, MASK_SH3ADD, match_opcode, 0 },
> +{"sh3add",     0, INSN_CLASS_ZBA,  "d,s,t,1", MATCH_SH3ADD, MASK_SH3ADD, match_opcode, 0 },
> +{"sh1add.uw", 64, INSN_CLASS_ZBA,  "d,s,t",   MATCH_SH1ADD_UW, MASK_SH1ADD_UW, match_opcode, 0 },
> +{"sh1add.uw", 64, INSN_CLASS_ZBA,  "d,s,t,1",   MATCH_SH1ADD_UW, MASK_SH1ADD_UW, match_opcode, 0 },
> +{"sh2add.uw", 64, INSN_CLASS_ZBA,  "d,s,t",   MATCH_SH2ADD_UW, MASK_SH2ADD_UW, match_opcode, 0 },
> +{"sh2add.uw", 64, INSN_CLASS_ZBA,  "d,s,t,1",   MATCH_SH2ADD_UW, MASK_SH2ADD_UW, match_opcode, 0 },
> +{"sh3add.uw", 64, INSN_CLASS_ZBA,  "d,s,t",   MATCH_SH3ADD_UW, MASK_SH3ADD_UW, match_opcode, 0 },
> +{"sh3add.uw", 64, INSN_CLASS_ZBA,  "d,s,t,1",   MATCH_SH3ADD_UW, MASK_SH3ADD_UW, match_opcode, 0 },

Same remark again regarding suitable padding ahead of MATCH_*. (Maybe this
becomes irrepevant though with the comment at the bottom.)

>  {"zext.w",    64, INSN_CLASS_ZCB_AND_ZBA,  "Cs,Cw", MATCH_C_ZEXT_W, MASK_C_ZEXT_W, match_opcode, INSN_ALIAS },
>  {"zext.w",    64, INSN_CLASS_ZBA,  "d,s",   MATCH_ADD_UW, MASK_ADD_UW | MASK_RS2, match_opcode, INSN_ALIAS },
>  {"zext.w",    64, INSN_CLASS_I, "d,s",       0, (int) M_ZEXTW, NULL, INSN_MACRO },
>  {"add.uw",    64, INSN_CLASS_ZBA,  "d,s,t", MATCH_ADD_UW, MASK_ADD_UW, match_opcode, 0 },
> +{"add.uw",    64, INSN_CLASS_ZBA,  "d,s,t,1", MATCH_ADD_UW, MASK_ADD_UW, match_opcode, 0 },
>  {"slli.uw",   64, INSN_CLASS_ZBA,  "d,s,>", MATCH_SLLI_UW, MASK_SLLI_UW, match_opcode, 0 },

The "...,1" forms of the insns aren't (at least for the time being) part of
the Zba spec. Their presence here wants commenting upon, so readers aren't
tempted to strip them out again as being against the spec.

I further wonder whether we really need two entries for each of these insns.
Can't the ",1" be made optional just like Vm is, and just like [1] arranges
to have the floating point rounding modes as well?

Jan

[1] https://sourceware.org/pipermail/binutils/2026-June/149695.html


More information about the Binutils mailing list