On 28/06/2026 22:55, Richard Sandiford wrote:
Andrew Stubbs <[email protected]> writes:
On 26/06/2026 16:53, Richard Sandiford wrote:
Andrew Stubbs <[email protected]> writes:
x86 gets away with

(define_insn "*<avx512>_load<mode>_mask"
     [(set (match_operand:V48_AVX512VL 0 "register_operand" "=v")
       (vec_merge:V48_AVX512VL
         (unspec:V48_AVX512VL
           [(match_operand:V48_AVX512VL 1 "memory_operand" "m")]
           UNSPEC_MASKLOAD)
         (match_operand:V48_AVX512VL 2 "nonimm_or_0_operand" "0C")
         (match_operand:<avx512fmaskmode> 3 "register_operand" "Yk")))]

so the (unspec ...) is wrapped around the MEM.

I tried that; it just tried to reload the entire unspec. [...]

Out of curiosity, how did that happen?  In the pattern above, the unspec
is a fixed part of the pattern, whereas LRA only reloads match_operands.

You're right; my RTL looked like this, but I had the match_operand
recognize the unspec because I wanted to prevent LRA from separating it
from the MEM.

With a normal consecutive memory operand like the one above, I thought
LRA would only consider reloading the address.

Historically, GCN has always used define_special_memory_constraint, which does not do this. I'm not sure what the original thinking was, but when I try changing it to a regular define_memory_constraint the reloads are completely broken -- it tries to use the stack pointer on an instruction that can't use that register.

I did think that I must just have the hooks setup wrongly, but when I tried to debug it there wasn't an obviously wrong case being tested, so I'm continuing to use the "special" constraint for now.

Ideally, LRA would know how to handle the new vectors of addresses, but I'm struggling to understand that code. (I feel like it's broken for addresses spaces with different sized addresses also, but maybe that's just my lack of understanding.)

With it the way you have it in that example, the unspec is completely
inactive, as far as LRA is concerned.

Yeah.  But it does mean that the equivalent of what you described earlier:

   I have an insn that looks like this:

      (set (reg:V64SI 123)
           (vec_merge:V64SI
              (mem:V64SI (reg:V64DI 456))
              (reg:V64SI 123)
              (reg:DI 789)))

   I.e. a masked load from a vector of addresses in pseudo 456, using a
   mask in pseudo 789.

   LRA sometimes tries to transform this by pulling out the MEM:

      (set (reg:V64SI 248)
           (mem:V64SI (reg:V64DI 456))

      (set (reg:V64SI 123)
           (vec_merge:V64SI
              (reg:V64SI 248)
              (reg:V64SI 123)
              (reg:DI 789)))

shouldn't happen for things that need to be masked loads.  The pattern
would start off with the unspec and (unspec [(reg ...)] UNSPEC_MASKLOAD)
would not be valid.

I assume the failure that you describe above came from providing an insn
that has both register and memory alternatives.  And that's a good thing,
of course.  A masked load can validily implement the vec_merge-of-a-normal-
mem above, since there's no requirement for the pattern to fault on
reads that would be discarded.  It's just that there needs to be another
RTL pattern for cases where the masking is semantically required.  The x86
pattern seems to achieve that, even though (like you say) there is no
perfect representation as things stand.

Yes, with my WIP patch there's one pattern that covers all masked moves, loads, and stores, across 3 different address spaces, and 2 different addressing modes (scalar base and vector base).

Having just unified all the move/gather/scatter insns, I didn't want to break it into multiple insns again, although there's probably not a particularly strong technical reason for that.

Andrew

Reply via email to