On Fri, 17 Jul 2026, Jeffrey Law wrote:
>
>
> On 5/19/2026 6:14 AM, Robin Dapp wrote:
> > Hi,
> >
> > I have been playing around with a vec_predicate RTX locally, moving parts of
> > the RVV backend over to it. Its main purpose is to lay out common operands
> > in
> > a fixed order. This helps rtl passes to reason about the predicate as a
> > whole,
> > as well as backends which needed to carry index information if they wanted
> > to
> > access particular fields.
> And just to be clear, the ability to find the predicate and interpret is a
> huge part of the motivation here.
>
> >
> > The staging I have in mind is roughly, each at least one patch:
> >
> > (1) Add basic vec_predicate plumbing for non-"move" insns.
> > (2) Handle vec_predicate in simplify-rtx to allow canonicalization,
> > merging,
> > and elision.
> > (3) Convert a self-contained subset of RVV instructions to the new format.
> >
> > A vec_predicate would look like this:
> >
> > (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
(vec_predicate
(plus:<mode> (...) (...))
or
(cond_plus:<mode>
(vec_predicate
...)
(...) (...))
where the latter is probably "safer" combine/split wise. The latter
would mimic .COND_* ifns on GIMPLE, but of course we'd explode the
RTX code somewhat unless we maybe stage it like
(cond_op:<mode> PLUS
(vec_predicate ...)
(...) (...))
and that's where you come from? Having (vec_predicate ..) as
nested RTX would allow that to be compile-time shortened for
targets that do not need all of the below ...
> > (match_operand:V 2 "else_operand" " velse")
> > (match_operand:<VM> 1 "mask_operand" " vm")
> > (match_operand 5 "vector_length_operand" " vl")
> > (const_int 0) # Bias
> > (match_operand 6 "const_int_operand" " i") # Length else
> > policy
> > (match_operand 7 "const_int_operand" " i")))]) # Mask else
> > policy
> So at a high level this seems to broadly match what the RISC-V backend is
> doing. That shouldn't be a big surprise to anyone.
... like x86 can do with mask_operand and else_operand.
> >
> > As AVX and SVE don't need operands 5-7, and most autovec patterns might not
> > even need the else/mask, I added default-fill parsing in read-rtl: If a
> > vec_predicate ends prematurely, the remaining operands are filled with
> > "autovec" defaults like zero-fill policies, no bias, full length, etc.
> > One issue with default filling is the mode of else and mask. I hope that
> > can work if we use dedicated sentinels like VEC_UNDEF and VEC_ALL_TRUE
> > (which target predicates would need to allow)?
> I think as long as we can avoid the need for the other architectures to worry
> about this stuff with any kind of regularity, then we're going to be in good
> shape. Obviously the RTL optimizers will need awareness, but the target files
> shouldn't. If that means we need a few well known sentinels, that's OK by me.
>
> >
> > What's not yet covered but should be in the future:
> >
> > - Predicated vector "copy" insns (register copy, load, store).
> > During initial brainstorming we couldn't come up with a good way to
> > represent predicated vector stores.
> > What could perhaps work is something like
> > [(set (reg:V) (vec_predicate:V vec_copy [reg:V] ...))]
> > and
> > [(set (reg:V) (vec_predicate:V vec_copy [mem:V] ...))]
> > [(set (mem:V) (vec_predicate:V vec_copy [reg:V] ...))]
> > where the mem attributes could still be stored in dest or src.
> > This obviously would not inherit any regular "move" handling we have.
> > I'm not sure this is a good thing :) But at least it would be a clear
> > cut.
> I can probably live with moves being special. Exposing the predicates on the
> operations seems like the big gain to me, even if we have a few stragglers
> like moves.
>
> >
> > Once we have predicated loads, the question of vectorizer interaction needs
> > to
> > come up, which brings us to another work or staging item:
> >
> > (4?) Add handling for the new shape to ifn and optabs. So far, I couldn't
> > think of a nicer way of identifying "vec_predicate"-style
> > optabs/insns
> > than adding a field to idata and have it be populated by genoutput
> > and
> > friends. Then, we could do something like
> > bool
> > insn_has_vec_predicate (enum insn_code icode)
> > {
> > return insn_data[icode].vec_predicate_opno >= 0;
> > }
> Seems basically reasonable and it allows you to trivially gate all kinds of
> behavior that most targets aren't going to need.
>
> >
> > gcc/ChangeLog:
> >
> > * cse.cc (hash_rtx): Hash predicate code.
> > (exp_equiv_p): Return false for unequal code.
> > * cselib.cc (rtx_equal_for_cselib_1): Ditto.
> > (cselib_hash_rtx): Add predicate code to hash.
> > * doc/rtl.texi: Document vector predicate.
> > * dwarf2out.cc (mem_loc_descriptor): Break for vec_predicate.
> > * emit-rtl.cc (init_emit_once): Init vec_all_true and vec_undef.
> > (rtx_expander::get_rtx): Set vec_predicate code.
> > (gen_vec_predicate): New function to generate a default
> > vector predicate.
> > (verify_vec_predicate): Verify vector predicate.
> > * genemit.cc (generator::add_exp): Encode vec_predicate code.
> > * genrecog.cc (rtx_test::vec_pred_code_field): Add.
> > (safe_to_hoist_p): Ditto.
> > (transition_parameter_type): Ditto.
> > (match_pattern_2): Ditto.
> > (print_nonbool_test): Print vec_predicate.
> > (print_test): Handle vec_predicate.
> > * print-rtl.cc (rtx_writer::print_rtx): Ditto.
> > * read-rtl.cc (apply_code_iterator): Ditto.
> > (rtx_reader::read_rtx_code): Ditto.
> > * rtl.cc (DEF_RTL_EXPR): Declare vec_all_true and vec_undef.
> > (rtx_equal_p): Compare vec_predicate code.
> > * rtl.def (VEC_ALL_TRUE): Declare.
> > (VEC_UNDEF): Ditto.
> > (VEC_PREDICATE): Declare.
> > * rtl.h (struct GTY): Add vec_pred_code.
> > (VEC_PRED_CODE): Add.
> > (PUT_VEC_PRED_CODE): Add.
> > (VEC_PRED_OPERANDS): Add.
> > (VEC_PRED_NOPERANDS): Add.
> > (VEC_PRED_OPERAND): Add.
> > (VEC_PRED_ELSE): Add.
> > (VEC_PRED_MASK): Add.
> > (VEC_PRED_LENGTH): Add.
> > (VEC_PRED_BIAS): Add.
> > (VEC_PRED_TAIL_POLICY): Add.
> > (VEC_PRED_MASK_POLICY): Add.
> > (VEC_PRED_FIRST_OPTIONAL): Add.
> > (VEC_PRED_UNDEF_ELSE_P): Add.
> > (VEC_PRED_FULL_LEN): Add.
> > (VEC_PRED_ALL_TRUE_P): Add.
> > (enum vec_pred_policy): Add.
> > (VEC_PRED_POLICY_MAX): Add.
> > (VEC_PRED_POLICY_VALID_P): Add.
> > (VEC_PRED_POLICY_PRESERVE_P): Add.
> > (gen_vec_predicate): Declare.
> > (verify_vec_predicate): Declare.
> > (GTY): Declare vec_all_true and vec_undefined.
> > ---
> > gcc/cse.cc | 9 +++++
> > gcc/cselib.cc | 9 +++++
> > gcc/doc/rtl.texi | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
> > gcc/dwarf2out.cc | 1 +
> > gcc/emit-rtl.cc | 69 ++++++++++++++++++++++++++++++++++++
> > gcc/genemit.cc | 4 +++
> > gcc/genrecog.cc | 25 +++++++++++++
> > gcc/print-rtl.cc | 3 ++
> > gcc/read-rtl.cc | 72 ++++++++++++++++++++++++++++++++++++--
> > gcc/rtl.cc | 11 ++++++
> > gcc/rtl.def | 18 ++++++++++
> > gcc/rtl.h | 55 +++++++++++++++++++++++++++++
> > 12 files changed, 365 insertions(+), 2 deletions(-)
> >
> > diff --git a/gcc/cse.cc b/gcc/cse.cc
> > index b4b39e3ebf8..ee4e8b86f1c 100644
> > --- a/gcc/cse.cc
> > +++ b/gcc/cse.cc
> > @@ -2494,6 +2494,10 @@ hash_rtx (const_rtx x, machine_mode mode,
> > }
> > break;
> > + case VEC_PREDICATE:
> > + hash += (unsigned) VEC_PRED_CODE (x);
> > + break;
> > +
> > default:
> > break;
> [ ... ]
> I wouldn't be surprised if there's more plumbing of this nature needed. I
> guess perhaps the question to ask is are you looking for this kind of thing
> systematically or by throwing code into the compiler and seeing what trips? I
> would think you'd want to include the relevant fields in the hash? Of course
> if all this is handled via a recursive step, then it's a non-issue :-)
>
> > }
> > @@ -2623,6 +2627,11 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate,
> > @@ bool for_gcse)
> > return false;
> > break;
> > + case VEC_PREDICATE:
> > + if (VEC_PRED_CODE (x) != VEC_PRED_CODE (y))
> > + return false;
> > + break;
> Similar to the hashing question above, the only difference is this is
> correctness rather than just potentially having a poor hash.
>
> >
> >
> > diff --git a/gcc/rtl.h b/gcc/rtl.h
> > index d60587dc5ce..4adbc2cb59e 100644
> > --- a/gcc/rtl.h
> > +++ b/gcc/rtl.h
> > @@ -438,6 +438,9 @@ struct GTY((desc("0"), tag("0"),
> > /* For future expansion. */
> > unsigned int unused : 8;
> > } const_vector;
> > +
> > + /* The code of a VEC_PREDICATE. */
> > + unsigned int vec_pred_code;
> > } GTY ((skip)) u2;
> It doesn't look like this is going to increase the size of an RTX as this is
> just another variant in a union. Phew!
>
> Overall it's nowhere near as complex as I expected. Obviously I worry about
> missed paths, but at some level we're going to be the guinea pig here, so if
> we'll be the ones to stumble over this stuff and need to fix it. I would
> probably not suggest other targets jump on the bandwagon until we've got
> confidence that it's working well for RISC-V.
>
> I'd love to hear Richard S's thoughts on the gen* bits, but they generally
> looked sensible to me.
>
> You mentioned that you've confirmed we can drop some of the vector patterns
> for RISC-V with your changes. That's definitely a good sign. If autovec.md
> turned into just moves, I'd jump for joy...
>
> jeff
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Abhinav Puri; (HRB 36809, AG Nuernberg)