On 16/09/2026 13:26, Richard Biener wrote:
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(?)
I'll respond to this in a reply to Richard's suggestion.
+
+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?
How's this?
Define this to return nonzero if the port can handle pointers
with machine mode @var{mode} to address space @var{as}. This hook can
also be used to allow pointers to use vector modes (e.g. to permit
@samp{(mem:V4SI (reg:V4DI))}. This target hook is the same as the
@code{TARGET_VALID_POINTER_MODE} target hook, except that it includes
explicit named address space support, and does not mandate
@code{scalar_int_mode}.
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?)
Yes, this is enough because plus_constant uses gen_int_mode to construct
the constant, which I patched to DTRT thing, above.
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?
OK, I can do that.
+ && 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.
This didn't come up as something I needed, but yes, I suppose that could
be implemented, in future.
+ && !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.
No problem.
Otherwise I think this looks reasonable. But please give Richard/Jeff
the chance to
comment.
I think we just need to agree the wording of the documentation, and I
can apply the two minor code changes,
Thanks for the review.
Andrew
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