On 17/07/2026 21:40, Robin Dapp wrote:
It's essentially vectorization in the backend, for code with specific
attributes. I definitely do not want to do it at gimple level because that
would have the exact same problems that the existing vectorizer has.

For example, in most cases I can simply transform

    (set (reg:SI) (plus:SI (reg:SI) (const_int)))

into

    (set (reg:V64SI) (plus:V64SI (reg:V64SI) (const_vector:V64SI)))

which makes total sense and Just Works on AMD GCN for almost all
instructions.

So, I want to do the same thing for a MEM:

    (set (reg:SI) (mem:SI (reg:DI)))

becomes

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

except that the MEM is not valid because the address "isn't valid".
There's no reason it shouldn't work on the architecture -- the two RTL
patterns here will emit the *exact same* assembler code (obviously I set
the execution mask differently) -- it's just an old assumption built
into the compiler.

I'm not sure I follow 100% so let me just spell it out for myself.  You're
probably trying to "just vectorize everything", like in a GPU/SIMT programming
model?  And obviously this clashes with the vectorizer which doesn't if-convert
everything, bails, costs etc.?

Yes, this is for the GPU, and yes, SIMT is the way to think of it. The vectorizer would not be enabled for this code. That would not go well!

But I don't see how vectorization, even simple one, is easier at RTL level.
What about divergence, calls, aliasing, loops?  Or is your target much
narrower?

Divergence is a high-level concept that does not really exist at the assembler level (GCN has fork/join instructions, but then says not to use them in all but "irreducible cases" -- they simply loop until all the mask variations are exhausted).

Calls are not an issue in themselves (provided all parameters fit in registers).

Aliasing is no more of an issue than it would be in the original scalar code because the threads are required to be "independent".

Loops work just fine, as long as the masks are managed correctly.

Even if narrow, for a proper gimple solution you'd need a "second vectorizer"
with limited scope and predicated gimple (because our usual IFNs aren't
comprehensive enough)?  It would "just" eat all conditions turning them into
masks.  That's a long-term goal for the vectorizer as well BTW :)

Some things are easier at gimple level, and some are easier at RTL. The RTL level requires fewer changes that end up in discussions like this one. ;-)

Then, however, you'd still have the representational problem post expand :/
A vec_predicate would be helpful there, but only if it supported moves, loads
and stores, which my initial RFC skipped for the known reasons ("they are
special").

I guess it's time to start thinking more about them but that's obviously the
opposite of a quick solution...  so nothing here will actually help you right
away :/

It might be that a "vec_predicate" would be better than "vec_merge"? But if
the register allocator can pull the MEM out of context then we didn't gain
anything for the specific issues I discussed in the gcc@ thread.

Yeah, one of the main reasons for vec_predicate as a "first-class" citizen is
that we should not be able use its constituents context free.  Under that model
a vec_merge is already an atomic operation, a predicated reg-move.  I wouldn't
re-use vec_merge of course but we'd have something like
   (set (reg...) (vec_predicate [(reg ...)] vmask velse ...))

So, it would be another way to write vec_merge, but with different behaviours/restrictions in certain passes?

Reply via email to