Richard Biener <[email protected]> writes:
> On Mon, Aug 10, 2026 at 7:48 PM Richard Sandiford
> <[email protected]> wrote:
>>
>> "Robin Dapp" <[email protected]> writes:
>> >>> > (define_insn "pred_add"
>> >>> > [(set (match_operand:V 0 "register_operand" "=vd")
>> >>> > (vec_predicate:V plus
>> >>> > [(match_operand:V 3 "register_operand" " v")
>> >>> > (match_operand:V 4 "register_operand" " v")]
>> >>
>> >> So there's not actually the predicated RTL operation, but just
>> >> the code and a vector of ... operands? I'd have expected either
>> >
>> > Yeah I don't particularly like it either, in particular that we need
>> > to replicate canonicalization for these "unstructured" operands in
>> > simplify-rtx. It's not a big deal but feels redundant.
>> >
>> >> (vec_predicate
>> >> (plus:<mode> (...) (...))
>> >
>> > This would still be my preferred way just by the way it reads and re-uses
>> > existing code. Richard's objection here was, though:
>> >
>> >> This again avoids contextual interpretation. A predicated plus is not
>> >> equivalent to taking an existing unpredicated plus and predicating it,
>> >> and vice versa.
>> >
>> > I didn't realize cond_exec is similar and that we need to avoid the
>> > "hoisting"
>> > situation already. So maybe the above wouldn't be too bad?
>>
>> cond_exec is a top-level code though. It wraps sets, rather than occurring
>> within sets. Contextual interpretation is a given there, since e.g.
>> XEXP (..., 0) on a SET or a CLOBBER needs to be interpreted as an lvalue
>> rather than an rvalue.
>>
>> That is, you can't interpret a top-level RTX code by evaluating its
>> operands first and then applying the operator to them. But you can do
>> that for all existing rvalue codes that I'm aware of.
>>
>> So it depends on what kind of code we want. Do we want a top-level code or
>> an rvalue code? A "vector predicate version of cond_exec" could indeed...
>>
>> > Introducing a vec_pred_set would avoid a vec_copy... RTX code. I'm not
>> > looking forward to adding combine/etc. handling for vec_predicate +
>> > vec_pred_set combinations, though :)
>>
>> ...be like this, although perhaps keeping the set and the vec_predicate
>> separate (the former within the latter).
>>
>> But the thing about cond_exec is that it's all or nothing. The action
>> that it wraps either happens or doesn't happen. The natural way of
>> extending that to predicates would be to say that the wrapped operation
>> either happens for a lane or does not happen for a lane. In other words,
>> it would be lane-level cond_exec rather than the existing vector-level
>> cond_exec.
>>
>> That would be a natural way of describing merge predication and
>> predicated stores, where the destination is partially modified and
>> partially preserved. Doing that sounds good to me if it's what we want.
>>
>> But I thought we wanted more than that from the new RTL code. I thought
>> we wanted to use it to describe an operation that is performed on some
>> lanes to produce a full vector result, with the values of other lanes
>> being taken from elsewhere. E.g. SVE patterns "need" at least merge
>> with zero, merge with first operand, merge with second operand, and
>> merge with third operand.
>>
>> If we took the cond_exec approach, we'd presumably require the wrapped
>> set operation to have a certain form and use information in the
>> vec_predicate wrapper to "imagine" what the corresponding operation
>> for inactive lanes would be.
>
> Maybe
>
> (vec_cond_exec
> ((pred:X) (set ...)
> (set ...)))
>
> with the 2nd (optional) set being executed for inactive lanes? That
> would basically be an "else" operation rather than an "else" value
> that we have now in the IFNs.
>
>> (In theory, we could avoid the "imagination" by having an explicit
>> rtx that describes the operation for inactive lanes, but that would
>> require duplicating the destination, which sounds worse.)
>
> Does it?
Does to me :) At the moment, it's not possible for an instruction
to set the same destination twice. (IMO call clobbers don't count,
since they happen during the call, rather than in the write phase
of the instruction. For example, df-scan.cc has:
else if (callee_abi.clobbers_full_reg_p (i)
/* no clobbers for regs that are the result of the call */
&& !TEST_HARD_REG_BIT (defs_generated, i)
where explicit sets prevail over call clobbers.)
That means that consumers of DF can iterate through DF_INSN_DEFS
and assume that each DF_REF_REG is independent. They might be
different subregs of the same pseudo, but that still counts.
If we had the "else" operand above, I think we'd need to include
it in the DF lists, otherwise consumers wouldn't get to see all the
relevant DF_REF_LOCs. But the same DF_REF_REG would then appear twice.
We'd have to audit DF users to see whether they care.
There's also the question of how it would be reloaded. In md terms,
I suppose the "else" operand's destination would be a match_dup
of the "then" operand's destination, but one destination match_duping
another would itself be new ground.
For reasons like that, it seems like it would be an odd exception
that many pieces of code would need to be aware of. I guess it's
doable though.
>> Even with that, the cond_exec approach still seems a bit inflexible.
>> It wouldn't help if we wanted the predicated operation to be nested
>> within another (unconditional) operation. E.g. it's feasible that an
>> instruction could perform zero predication followed by some form of
>> permutation. IIRC SVE LD1RQ would be like that.
>
> True, so I guess that rules out a toplevel RTX as a generic enough vehicle,
> but then at least zero predication can be handled fine already
How though? SVE generally uses unspecs if zero predication can suppress
side-effects, but I thought the idea was to avoid unspecs.
> and combine splitting that out wouldn't be harmful (if recognizable).
>
>> Having vec_predicate be an rvalue that occurs within the set would
>> avoid that inflexibility. But vec_predicate would then be completely
>> unlike cond_exec. Both the vec_predicate rtx itself and its operands
>> would be subject to combination, cse, forward progatation, etc.
>>
>> Allowing:
>>
>> (vec_predicate
>> predicate
>> (plus op op1)
>> ...)
>>
>> would mean that vec_predicate could not be interpreted by evaluating its
>> operands ("predicate" and "(plus op0 op1)"), then applying the vec_predicate
>> operator to the result. vec_predicate could only be interpreted by
>> treating RTX_CODE and RTX_CODE (XEXP (x, 1)) as a compound operation
>> whose operands are formed from XEXP (x, 0), XEXP (XEXP (x, 1), 0),
>> XEXP (XEXP (x, 1), 1) and whatever is in "...". AFAIK that would
>> make it unique among rtxes that can occur as or within a SET_SRC.
>>
>> I realise that the form above would make the initial implementation easier.
>> But I fear that's only because we'd have to abandon any realistic hope of
>> identifying which pieces of code need to be taught about this new exception
>> and instead wait for users and fuzzers to find them experimentally.
>>
>> That said, I realise that the alternatives aren't particularly appealing
>> either, even if they seem semantically cleaner (to me).
>
> I think at this point having a "cheat sheet" of how the various uses
> (masked store, masked load, masked operation) look like with the
> different proposed ways would be nice to have. Possibly the wiki
> can host a table of those?
Yeah, sounds good, if someone's willing.
Richard