On 16/09/2026 18:35, Richard Sandiford wrote:
Andrew Stubbs <[email protected]> writes:
On 16/09/2026 14:44, Richard Sandiford wrote:
Richard Biener <[email protected]> writes:
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(?)

Yeah.  And IMO the flag should be about the addresses rather than
the operation.  I.e. something like:

--------------------------------------------------------------------------
Nonzero in a @code{mem}, if the memory address is a vector and if that
vector must specify independent, non-overlapping memory locations.
If the vector fails to do this, the behavior is undefined.

When writing to a @code{mem} that has a vector address, an instruction
must write to the elements as if in left-to-right order.
@code{MEM_INDEPENDENT_P} indicates that left-to-right order is
equivalent to any other order and so an instruction can write to each
element independently.
--------------------------------------------------------------------------

I'm struggling to understand what this implies for code transformations,
etc., and what happens when the flag is unset. How about this version?

-------------------------------------------------------------------------
In a @code{mem}, nonzero if the memory address is a vector and if that
vector must specify independent, non-overlapping memory locations, to be
used safely in a store. If the vector fails to do this, the behavior is
undefined.

In general, when writing to a @code{mem} that has a vector address, an
instruction must write to the elements as if in left-to-right order.
@code{MEM_INDEPENDENT_P} indicates that left-to-right order is
equivalent to any other order and so an instruction can safely write to
each element independently.
--------------------------------------------------------------------------

So I suppose the differences are:

(1) adding "to be used safely in a store".  But to me that implies that
     !MEM_INDEPENDENT_P stores are unsafe, whereas they would be perfectly
     safe on a target like AVX or SVE.

I thought that was the point. Without the flag, writes are safe and well defined because the behaviour of the instructions matches the left-to-right semantics of the the RTL, and all is well with the world.

With the flag set, machine instructions that do not operate strictly left-to-right can still be used because whatever generated the MEM is promising that the addresses are safe and "independent".

(Right now, GCN can assume that such operations are safe where the sources of the address vectors are gcn_expand_scalar_to_vector_address, that generates a series of contiguous addresses from a base address and mode, or the new SIMT vectorizer where loop iterations are declared "independent" by the use of OpenMP direfctives. In theory, the SIMD gather/scatter is not safe, but we have been making the assumption that it is since forever, so .... hmmmm.)

The thought strikes me that I should probably be adding a MEM_INDEPENDENT_P check to my memory constraints.

>      I think the flag also makes logical sense in non-store contexts,
>      as optimisation information.
>
(2) Adding "in general", which implies there are exceptions.  What I was
     trying to say was that the RTL semantics of storing to a mem are the
     same for all targets, but that this flag allows a different
     implementation of those semantics.

I wanted to add that because it wasn't clear to me whether the second paragraph was logically linked to the first one, or not. Anyway ....

How about:

--------------------------------------------------------------------------
This flag only applies to @code{mem}s that have vector addresses (that is,
if the address itself has a vector mode).  The flag is nonzero if the
address vector is known to specify independent, non-overlapping memory
locations, so that the @code{mem} never refers to the same byte twice.

Here, ``known to'' and ``never'' mean that the behavior of the program
is undefined if the addresses do overlap, rather than that overlap is
numerically impossible.

This information can be useful when writing to a @code{mem} that has
a vector address.  RTL semantics require such writes to behave
@emph{as if} they processed the vector in left-to-right order,
with later writes winning over earlier writes.  However, when
@code{MEM_INDEPENDENT_P} is true, the order of the writes does
not matter for RTL semantics and so an instruction could write
to the elements in any order, or in parallel.
--------------------------------------------------------------------------

Is that better in terms of code transformations etc?  The idea is that
the flag is an assertion about the address values, rather than a
description of how the mem is used.

I like the word "assertion" but it does not appear in the text.

  The flag is nonzero if the creator asserts that the address vector
  specifies independent, non-overlapping memory locations, ....

This means that we don't need to define "known to" and "never". WDYT?

The last paragraph looks good to me.

I suppose this means that, when MEM_INDEPENDENT_P is true:

   local var;

   if (cond)
     write to (mem (vector referring to var))

cannot be speculatively moved outside of the "if" unless we can prove that
the addresses really are independent, since otherwise we're speculating UB.
But I think that's true however we define this.

How can memory writes be done speculatively anyway? Anyway, doing anything with vectors speculatively is likely to be expensive.

[...]
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.  */

I think this should be updated along the lines of the above.

How about this?

---------------------------------------------------------------------
/* 1 is RTX is a mem whose address vector elements all point to
     independent memory locations, without overlapping each other.  */
---------------------------------------------------------------------

SGTM.

Thanks,
Richard

Reply via email to