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")]
(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.
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