On Thu, Sep 3, 2026 at 3:48 PM Andrew Stubbs <[email protected]> wrote:
>
> This patch makes the middle-end adjustments needed to allow a MEM to take a
> vector of addresses in order to read/write a non-contiguous vector of data.
> Mostly the changes don't have to do anything besides letting the vectors pass.
>
> There's a new MEM flag: MEM_INDEPENDENT_P that describes the store semantics
> of vectors with conflicting addresses.  Nothing uses it, yet, but it will
> allow future optimizations that rely on left-to-right semantics to do the 
> right
> thing.  GCN does not have such a guarantee, so it sets the flag.
>
> These changes, alone, are not enough to actually allow such MEMs, since it's
> unlikely that any back-end reports the vector modes "legitimate", so there
> should be no change of behaviour on any existing target (unconfirmed).
>
> I've modified the type of only the TARGET_ADDR_SPACE_VALID_POINTER_MODE hook,
> which is currently not being used by any target (amdgcn will use it in the
> following patch), and left the TARGET_VALID_POINTER_MODE alone so as not to
> have to touch all the other back-ends.  If another back-end wants to use
> vectors of addresses then I propose that either TARGET_VALID_POINTER_MODE can
> be fixed up at that time, or that backend simply switch to using
> TARGET_ADDR_SPACE_VALID_POINTER_MODE.
>
> I have not made any changes to LRA code.  I believe this is not necessary as
> long as the target uses "define_special_memory_constraint".  I failed to
> figure out how to make the regular memory constraints work, and as it's not
> necessary for my use case I'm loath to invest any more time into that.
>
> Hopefully the documentation patches should describe the intended semantics.
> I've tried to keep it very simple.
>
> There's one "TODO" in the code; I'm intentionally applying the principle of
> "don't add it if you don't need it" here (not least because I don't know how
> to test it).  There's an assert to make sure it never bites anyone silently.
>
> gcc/ChangeLog:
>
>         * doc/rtl.texi: Document MEM_INDEPENDENT_P.
>         Document new MEM semantics.
>         * doc/tm.texi: Likewise.
>         * emit-rtl.cc (gen_int_mode): Extend to DTRT for vector modes.
>         (adjust_address_1): Permit vectors of addresses.
>         * explow.cc (convert_memory_address_addr_space_1): Permit vectors of
>         addresses, for no-op conversions only.
>         * recog.cc (address_operand): Permit vectors of addresses.
>         * rtl.h (MEM_INDEPENDENT_P): New macro.
>         (get_address_mode): Change return type to machine_mode.
>         * rtlanal.cc (get_address_mode): Likewise.
>         * simplify-rtx.cc (simplify_context::simplify_subreg): Handle vectors
>         of addresses.
>         * target.def (pointer_mode): Document vectors of addresses.
>         (address_mode): Likewise.
>         (valid_pointer_mode): Change scalar_int_mode to machine_mode.
>         * targhooks.cc (default_addr_space_valid_pointer_mode): Change
>         scalar_int_mode to machine_mode.
>         * targhooks.h (default_addr_space_valid_pointer_mode): Likewise.
> ---
>  gcc/doc/rtl.texi    | 23 +++++++++++++++++++++++
>  gcc/doc/tm.texi     | 13 ++++++++++++-
>  gcc/emit-rtl.cc     | 30 ++++++++++++++++++++++++------
>  gcc/explow.cc       | 12 ++++++++++--
>  gcc/recog.cc        |  4 +++-
>  gcc/rtl.h           |  7 ++++++-
>  gcc/rtlanal.cc      |  4 ++--
>  gcc/simplify-rtx.cc |  5 ++++-
>  gcc/target.def      | 13 ++++++++++---
>  gcc/targhooks.cc    |  4 ++--
>  gcc/targhooks.h     |  3 +--
>  11 files changed, 97 insertions(+), 21 deletions(-)
>
> diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi
> index f606b97389f..a2af57233f4 100644
> --- a/gcc/doc/rtl.texi
> +++ b/gcc/doc/rtl.texi
> @@ -703,6 +703,21 @@ application, this memory will never be subsequently 
> modified.
>
>  Stored in the @code{unchanging} field and printed as @samp{/u}.
>
> +@findex MEM_INDEPENDENT_P
> +@cindex @code{mem} and @samp{/i}
> +@item MEM_INDEPENDENT_P (@var{x})
> +Nonzero in a @code{mem}, if the elements in a vector are written to memory as
> +independent operations, not necessarily completing in left-to-right order.  
> In
> +such a store, the result of two vector elements writing to the same address, 
> or
> +overlapping addresses, is undefined.  Otherwise, when the flag is zero, the
> +compiler can assume that the vector elements are known to write predictably 
> and
> +in order.
> +
> +This applies primarily when the write address is a vector of addresses, e.g.
> +@samp{(mem:V4SI/i (reg:V4DI r123))}.

I'd fold this into the above and "if the elements in a vector are
written to memory
specified as a vector of addresses as independent operations", because otherwise
it doesn't make sense(?)

> +
> +Stored in the @code{return_val} field and printed as @samp{/i}.
> +
>  @findex PREFETCH_SCHEDULE_BARRIER_P
>  @cindex @code{prefetch} and @samp{/v}
>  @cindex @code{volatile}, in @code{prefetch}
> @@ -2369,6 +2384,14 @@ a unit of memory is accessed.  @var{alias} specifies 
> an alias set for the
>  reference.  In general two items are in different alias sets if they cannot
>  reference the same memory address.
>
> +The @var{addr} expression can represent either a scalar base address, or a
> +vector of addresses (using an appropriate vector mode).  In the vector case,
> +the number of lanes in the address mode must match the number of lanes in the
> +data mode @var{m}.  Each vector lane can be viewed as an independent, 
> parallel
> +memory access of the corresponding scalar mode.  If multiple lanes represent
> +the same, or overlapping, memory location then the effect is undefined.  All
> +lanes are assumed to have the same attributes (address space, volatile, 
> etc.).
> +
>  The construct @code{(mem:BLK (scratch))} is considered to alias all
>  other memories.  Thus it may be used as a memory barrier in epilogue
>  stack deallocation patterns.
> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> index 62df9533769..60be632c497 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -11508,16 +11508,27 @@ c_register_addr_space ("__ea", ADDR_SPACE_EA);
>  @deftypefn {Target Hook} scalar_int_mode TARGET_ADDR_SPACE_POINTER_MODE 
> (addr_space_t @var{address_space})
>  Define this to return the machine mode to use for pointers to
>  @var{address_space} if the target supports named address spaces.
> +
> +Targets that support vectors of pointers should return only the scalar base
> +mode here, but permit the vector modes in
> +@code{TARGET_ADDR_SPACE_VALID_POINTER_MODE}.
> +
>  The default version of this hook returns @code{ptr_mode}.
>  @end deftypefn
>
>  @deftypefn {Target Hook} scalar_int_mode TARGET_ADDR_SPACE_ADDRESS_MODE 
> (addr_space_t @var{address_space})
>  Define this to return the machine mode to use for addresses in
>  @var{address_space} if the target supports named address spaces.
> +
> +Targets that support vectors of addresses should return only the scalar base
> +mode here, but permit the vector modes in
> +@code{TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P} and
> +@code{TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS}.
> +
>  The default version of this hook returns @code{Pmode}.
>  @end deftypefn
>
> -@deftypefn {Target Hook} bool TARGET_ADDR_SPACE_VALID_POINTER_MODE 
> (scalar_int_mode @var{mode}, addr_space_t @var{as})
> +@deftypefn {Target Hook} bool TARGET_ADDR_SPACE_VALID_POINTER_MODE 
> (machine_mode @var{mode}, addr_space_t @var{as})
>  Define this to return nonzero if the port can handle pointers
>  with machine mode @var{mode} to address space @var{as}.  This target
>  hook is the same as the @code{TARGET_VALID_POINTER_MODE} target hook,

can you amend this to say when address modes can be pointer modes this is
the hook to implement?

> diff --git a/gcc/emit-rtl.cc b/gcc/emit-rtl.cc
> index 4a23eaefe02..ea4d757bed3 100644
> --- a/gcc/emit-rtl.cc
> +++ b/gcc/emit-rtl.cc
> @@ -540,14 +540,29 @@ gen_rtx_CONST_INT (machine_mode mode ATTRIBUTE_UNUSED, 
> HOST_WIDE_INT arg)
>    return *slot;
>  }
>
> +/* Return a CONST_INT, CONST_POLY_INT, or CONST_VECTOR containing the given
> +   value.  In the case of a vector, the value will be duplicated.  */
> +
>  rtx
>  gen_int_mode (poly_int64 c, machine_mode mode)

heh, I wonder why this wasn't scalar_int_mode ...

>  {
> -  c = trunc_int_for_mode (c, mode);
> +  machine_mode s_mode = (VECTOR_MODE_P (mode) ? GET_MODE_INNER (mode) : 
> mode);
> +  rtx val;
> +
> +  c = trunc_int_for_mode (c, s_mode);
>    if (c.is_constant ())
> -    return GEN_INT (c.coeffs[0]);
> -  unsigned int prec = GET_MODE_PRECISION (as_a <scalar_mode> (mode));
> -  return immed_wide_int_const (poly_wide_int::from (c, prec, SIGNED), mode);
> +    val = GEN_INT (c.coeffs[0]);
> +  else
> +    {
> +      unsigned int prec = GET_MODE_PRECISION (as_a <scalar_mode> (s_mode));
> +      val = immed_wide_int_const (poly_wide_int::from (c, prec, SIGNED),
> +                                 s_mode);
> +    }
> +
> +  if (VECTOR_MODE_P (mode))
> +    val = gen_const_vec_duplicate (mode, val);
> +
> +  return val;
>  }
>
>  /* CONST_DOUBLEs might be created from pairs of integers, or from
> @@ -2381,7 +2396,7 @@ adjust_address_1 (rtx memref, machine_mode mode, 
> poly_int64 offset,
>  {
>    rtx addr = XEXP (memref, 0);
>    rtx new_rtx;
> -  scalar_int_mode address_mode;
> +  machine_mode address_mode;
>    class mem_attrs attrs (*get_mem_attrs (memref)), *defattrs;
>    unsigned HOST_WIDE_INT max_align;
>  #ifdef POINTERS_EXTEND_UNSIGNED
> @@ -2415,7 +2430,10 @@ adjust_address_1 (rtx memref, machine_mode mode, 
> poly_int64 offset,
>    /* Convert a possibly large offset to a signed value within the
>       range of the target address space.  */
>    address_mode = get_address_mode (memref);
> -  offset = trunc_int_for_mode (offset, address_mode);
> +  if (!VECTOR_MODE_P (address_mode))
> +    offset = trunc_int_for_mode (offset, address_mode);
> +  else
> +    offset = trunc_int_for_mode (offset, GET_MODE_INNER (address_mode));
>

But is this really enough?  Won't we get mismatched address/offset
mode when later adding offset
via plus_constant?  I think this at least needs some defenses (do you
only ever see zero offset?)

>    if (adjust_address)
>      {
> diff --git a/gcc/explow.cc b/gcc/explow.cc
> index ec2bc9bc9b6..9c7c834aaab 100644
> --- a/gcc/explow.cc
> +++ b/gcc/explow.cc
> @@ -298,8 +298,13 @@ convert_memory_address_addr_space_1 (scalar_int_mode 
> to_mode ATTRIBUTE_UNUSED,
>                                      bool in_const ATTRIBUTE_UNUSED,
>                                      bool no_emit ATTRIBUTE_UNUSED)
>  {
> +  machine_mode xmode = GET_MODE (x);
> +  bool vecaddr_p = VECTOR_MODE_P (GET_MODE (x));
> +  if (vecaddr_p)
> +    xmode = GET_MODE_INNER (xmode);
> +
>  #ifndef POINTERS_EXTEND_UNSIGNED
> -  gcc_assert (GET_MODE (x) == to_mode || GET_MODE (x) == VOIDmode);
> +  gcc_assert (xmode == to_mode || xmode == VOIDmode);
>    return x;
>  #else /* defined(POINTERS_EXTEND_UNSIGNED) */
>    scalar_int_mode pointer_mode, address_mode, from_mode;
> @@ -307,9 +312,12 @@ convert_memory_address_addr_space_1 (scalar_int_mode 
> to_mode ATTRIBUTE_UNUSED,
>    enum rtx_code code;
>
>    /* If X already has the right mode, just return it.  */
> -  if (GET_MODE (x) == to_mode)
> +  if (xmode == to_mode)
>      return x;
>
> +  /* TODO: support vector conversions.  */
> +  gcc_assert (!vecaddr_p);
> +
>    pointer_mode = targetm.addr_space.pointer_mode (as);
>    address_mode = targetm.addr_space.address_mode (as);
>    from_mode = to_mode == pointer_mode ? address_mode : pointer_mode;
> diff --git a/gcc/recog.cc b/gcc/recog.cc
> index f9cff68e457..f0553fdfd05 100644
> --- a/gcc/recog.cc
> +++ b/gcc/recog.cc
> @@ -1632,7 +1632,9 @@ address_operand (rtx op, machine_mode mode)
>  {
>    /* Wrong mode for an address expr.  */
>    if (GET_MODE (op) != VOIDmode
> -      && ! SCALAR_INT_MODE_P (GET_MODE (op)))
> +      && !(SCALAR_INT_MODE_P (GET_MODE (op))
> +          || (VECTOR_MODE_P (GET_MODE (op))

GET_MODE_CLASS (GET_MODE (op)) == VECTOR_INT_MODE?

I don't think we have vector-of-partial-int-modes?

> +              && SCALAR_INT_MODE_P (GET_MODE_INNER (GET_MODE (op))))))
>      return false;
>
>    return memory_address_p (mode, op);
> diff --git a/gcc/rtl.h b/gcc/rtl.h
> index 77a71e5bbec..e523517884d 100644
> --- a/gcc/rtl.h
> +++ b/gcc/rtl.h
> @@ -2653,6 +2653,11 @@ do {                                                   
>                   \
>  #define MEM_READONLY_P(RTX) \
>    (RTL_FLAG_CHECK1 ("MEM_READONLY_P", (RTX), MEM)->unchanging)
>
> +/* 1 if RTX is a mem whose vector elements are written to memory
> +   independently, not necessarily left-to-right.  */
> +#define MEM_INDEPENDENT_P(RTX) \
> +  (RTL_FLAG_CHECK1 ("MEM_INDEPENDENT_P", (RTX), MEM)->return_val)
> +
>  /* 1 if RTX is a mem and we should keep the alias set for this mem
>     unchanged when we access a component.  Set to 1, or example, when we
>     are already in a non-addressable component of an aggregate.  */
> @@ -3698,7 +3703,7 @@ inline rtx single_set (const rtx_insn *insn)
>    return single_set_2 (insn, PATTERN (insn));
>  }
>
> -extern scalar_int_mode get_address_mode (rtx mem);
> +extern machine_mode get_address_mode (rtx mem);
>  extern bool rtx_addr_can_trap_p (const_rtx);
>  extern bool nonzero_address_p (const_rtx);
>  extern bool rtx_unstable_p (const_rtx);
> diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
> index d897dc400ea..3d954f291e8 100644
> --- a/gcc/rtlanal.cc
> +++ b/gcc/rtlanal.cc
> @@ -6295,7 +6295,7 @@ low_bitmask_len (machine_mode mode, unsigned 
> HOST_WIDE_INT m)
>
>  /* Return the mode of MEM's address.  */
>
> -scalar_int_mode
> +machine_mode
>  get_address_mode (rtx mem)
>  {
>    machine_mode mode;
> @@ -6303,7 +6303,7 @@ get_address_mode (rtx mem)
>    gcc_assert (MEM_P (mem));
>    mode = GET_MODE (XEXP (mem, 0));
>    if (mode != VOIDmode)
> -    return as_a <scalar_int_mode> (mode);
> +    return mode;
>    return targetm.addr_space.address_mode (MEM_ADDR_SPACE (mem));
>  }
>
> diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
> index 38c8abfe3e7..dadcd82d0c7 100644
> --- a/gcc/simplify-rtx.cc
> +++ b/gcc/simplify-rtx.cc
> @@ -8777,7 +8777,10 @@ simplify_context::simplify_subreg (machine_mode 
> outermode, rtx op,
>        && (! MEM_VOLATILE_P (op)
>           || ! have_insn_for (SET, innermode))
>        && !(STRICT_ALIGNMENT && MEM_ALIGN (op) < GET_MODE_ALIGNMENT 
> (outermode))
> -      && known_le (outersize, innersize))
> +      && known_le (outersize, innersize)
> +      /* You can't get the Nth element of a vector of addresses by adding
> +        offsets.  */

Nth element of a gather/scatter (a MEM with an address vector)

But you could simplify it to the MEM of the subreg of the _address_, no?
(subreg (mem x) byte) -> (mem (subreg x byte)) at least if the element
size matches the address element size or with further massaging if the
subreg selects a subset of elements.  Of course that's a separate case.

> +      && !VECTOR_MODE_P (GET_MODE (XEXP (op, 0))))
>      return adjust_address_nv (op, outermode, byte);
>
>    /* Handle complex or vector values represented as CONCAT or VEC_CONCAT
> diff --git a/gcc/target.def b/gcc/target.def
> index 66c20a9c9d2..becab55cd62 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -3451,7 +3451,10 @@ HOOK_VECTOR (TARGET_ADDR_SPACE_HOOKS, addr_space)
>  DEFHOOK
>  (pointer_mode,
>   "Define this to return the machine mode to use for pointers to\n\
> -@var{address_space} if the target supports named address spaces.\n\
> +@var{address_space} if the target supports named address spaces.\n\n\
> +Targets that support vectors of pointers should return only the scalar 
> base\n\
> +mode here, but permit the vector modes in\n\
> +@code{TARGET_ADDR_SPACE_VALID_POINTER_MODE}.\n\n\
>  The default version of this hook returns @code{ptr_mode}.",
>   scalar_int_mode, (addr_space_t address_space),
>   default_addr_space_pointer_mode)
> @@ -3460,7 +3463,11 @@ The default version of this hook returns 
> @code{ptr_mode}.",
>  DEFHOOK
>  (address_mode,
>   "Define this to return the machine mode to use for addresses in\n\
> -@var{address_space} if the target supports named address spaces.\n\
> +@var{address_space} if the target supports named address spaces.\n\n\
> +Targets that support vectors of addresses should return only the scalar 
> base\n\
> +mode here, but permit the vector modes in\n\
> +@code{TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P} and\n\
> +@code{TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS}.\n\n\
>  The default version of this hook returns @code{Pmode}.",
>   scalar_int_mode, (addr_space_t address_space),
>   default_addr_space_address_mode)
> @@ -3476,7 +3483,7 @@ except that it includes explicit named address space 
> support.  The default\n\
>  version of this hook returns true for the modes returned by either the\n\
>  @code{TARGET_ADDR_SPACE_POINTER_MODE} or 
> @code{TARGET_ADDR_SPACE_ADDRESS_MODE}\n\
>  target hooks for the given address space.",
> - bool, (scalar_int_mode mode, addr_space_t as),
> + bool, (machine_mode mode, addr_space_t as),
>   default_addr_space_valid_pointer_mode)
>
>  /* True if an address is a valid memory address to a given named address
> diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
> index 388f696c8aa..e89e9feb79e 100644
> --- a/gcc/targhooks.cc
> +++ b/gcc/targhooks.cc
> @@ -1728,10 +1728,10 @@ default_addr_space_address_mode (addr_space_t 
> addrspace ATTRIBUTE_UNUSED)
>     To match the above, the same modes apply to all address spaces.  */
>
>  bool
> -default_addr_space_valid_pointer_mode (scalar_int_mode mode,
> +default_addr_space_valid_pointer_mode (machine_mode mode,
>                                        addr_space_t as ATTRIBUTE_UNUSED)
>  {
> -  return targetm.valid_pointer_mode (mode);
> +  return targetm.valid_pointer_mode (as_a <scalar_int_mode> (mode));

Please make this more defensive by returning false if mode isn't a
scalar_int_mode.

Otherwise I think this looks reasonable.  But please give Richard/Jeff
the chance to
comment.

Thanks,
Richard.

>  }
>
>  /* Some places still assume that all pointer or address modes are the
> diff --git a/gcc/targhooks.h b/gcc/targhooks.h
> index 86de6ef8a69..798a84d0e13 100644
> --- a/gcc/targhooks.h
> +++ b/gcc/targhooks.h
> @@ -206,8 +206,7 @@ extern bool default_valid_pointer_mode (scalar_int_mode);
>  extern bool default_ref_may_alias_errno (class ao_ref *);
>  extern scalar_int_mode default_addr_space_pointer_mode (addr_space_t);
>  extern scalar_int_mode default_addr_space_address_mode (addr_space_t);
> -extern bool default_addr_space_valid_pointer_mode (scalar_int_mode,
> -                                                  addr_space_t);
> +extern bool default_addr_space_valid_pointer_mode (machine_mode, 
> addr_space_t);
>  extern bool default_addr_space_legitimate_address_p (machine_mode, rtx, bool,
>                                                      addr_space_t, 
> code_helper);
>  extern rtx default_addr_space_legitimize_address (rtx, rtx, machine_mode,
> --
> 2.54.0
>

Reply via email to