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

Reply via email to