> -----Original Message-----
> From: Alfie Richards <[email protected]>
> Sent: 17 September 2026 15:40
> To: [email protected]
> Cc: Alex Coplan <[email protected]>; Alice Carlotti
> <[email protected]>; Andrea Pinski
> <[email protected]>; Richard Biener <[email protected]>;
> Tamar Christina <[email protected]>; Zdenek Dvorak <[email protected]>;
> Robin Dapp <[email protected]>; Richard Sandiford
> <[email protected]>; Kyrylo Tkachov <[email protected]>;
> Alfie Richards <[email protected]>
> Subject: [PATCH v3 03/16] ifn: optab: Add HSSR ifns and optabs
> 
> Adds 4 new optabs and respective internal functions for hardware
> safe speculative reads:
> 
> - mask_ff_hssr_load: a contiguous masked HSSR load
> - read_hssr_state: reads the global HSSR state
> - set_hssr_state: sets the global HSSR state
> - detect_hssr_fault: takes a loaded HSSR state value and returns a
>   boolean value for if the mask implies a partial read happens.
> 
> As this is initially only for targets that support partial vectors, the
> HSSR state is initially assumed to be masks.
> 
> The last optab is required to hide from the gimple representation the
> value of the hssr mask. This is because, otherwise later optimizations
> try use it when it is a full mask, which introduces a very unfortunate
> data dependency.
> 
> gcc/ChangeLog:
> 
>       * doc/ifn.texi: Document new internal functions.
>       * doc/md.texi: Document new optabs.
>       * gimple-fold.cc (gimple_fold_partial_load_store_mem_ref):
>       Add handling of IFN_MASK_FF_HSSR_LOAD.
>       (gimple_fold_partial_load_store): Likewise.
>       (gimple_fold_call): Likewise.
>       * internal-fn.cc (read_hssr_state_direct): New macro.
>       (set_hssr_state_direct): New macro.
>       (detect_hssr_fault_direct): New macro.
>       (expand_read_hssr_state_optab_fn): New macro.
>       (expand_detect_hssr_fault_optab_fn): New macro.
>       (expand_set_hssr_state_optab_fn): New function.
>       (direct_read_hssr_state_optab_supported_p): New macro.
>       (direct_set_hssr_state_optab_supported_p): New macro.
>       (direct_detect_hssr_fault_optab_supported_p): New macro.
>       (internal_load_fn_p): Add support for IFN_MASK_FF_HSSR_LOAD.
>       (internal_hssr_load_fn_p): New function.
>       (internal_fn_else_index): Add support for IFN_MASK_FF_HSSR_LOAD.
>       (internal_fn_mask_index): Add support for
> IFN_MASK_FF_HSSR_LOAD.
>       (internal_fn_alias_ptr_index): Add support for
> IFN_MASK_FF_HSSR_LOAD.
>       * internal-fn.def (MASK_FF_HSSR_LOAD): New IFN.
>       (SET_HSSR_STATE): New IFN.
>       (READ_HSSR_STATE): New IFN.
>       (DETECT_HSSR_FAULT): New IFN.
>       * internal-fn.h (internal_hssr_load_fn_p): New function.
>       * optabs.def (mask_ff_hssr_load_optab): New optab.
>       (read_hssr_state_optab): New optab.
>       (set_hssr_state_optab): New optab.
>       (detect_hssr_fault_optab): New optab.
>       * tree-data-ref.cc (get_references_in_stmt):
>       Add support for IFN_MASK_FF_HSSR_LOAD.
>       * tree-ssa-alias.cc (ref_maybe_used_by_call_p_1):
>       Add support for IFN_MASK_FF_HSSR_LOAD.
>       * tree-ssa-loop-ivopts.cc (get_mem_type_for_internal_fn):
>       Add support for IFN_MASK_FF_HSSR_LOAD.
>       (get_alias_ptr_type_for_ptr_address):
>       Add support for IFN_MASK_FF_HSSR_LOAD.
>       * tree-vect-slp.cc (vect_get_operand_map):
>       Add support for IFN_MASK_FF_HSSR_LOAD.
> 
> -- >8 --
> 
> Just to note, I fixed the issue with the test by instead making
> gimple_fold_call corectly handle IFN_MASK_FF_HSSR_LOAD and fold it
> in this situation.

Oro? What issue? Cover letter doesn't mention any :)

> ---
>  gcc/doc/ifn.texi            | 29 +++++++++++++++++++++++++
>  gcc/doc/md.texi             | 42 +++++++++++++++++++++++++++++++++++++
>  gcc/gimple-fold.cc          |  9 ++++++++
>  gcc/internal-fn.cc          | 39 ++++++++++++++++++++++++++++++++++
>  gcc/internal-fn.def         | 18 ++++++++++++++++
>  gcc/internal-fn.h           |  1 +
>  gcc/optabs.def              |  8 +++++++
>  gcc/tree-data-ref.cc        |  2 ++
>  gcc/tree-ssa-alias.cc       |  1 +
>  gcc/tree-ssa-loop-ivopts.cc |  2 ++
>  gcc/tree-vect-slp.cc        |  3 +++
>  11 files changed, 154 insertions(+)
> 
> diff --git a/gcc/doc/ifn.texi b/gcc/doc/ifn.texi
> index 7cfc80eecf2..0742803ccd4 100644
> --- a/gcc/doc/ifn.texi
> +++ b/gcc/doc/ifn.texi
> @@ -122,6 +122,35 @@ instruction pattern.
>  The @code{IFN_MASK_LEN_LOAD} internal function directly maps to the
>  @code{mask_len_load} instruction pattern.
> 
> +@cindex @code{IFN_MASK_FF_HSSR_LOAD} internal function
> +@item @samp{IFN_MASK_FF_HSSR_LOAD}
> +The @code{IFN_MASK_FF_HSSR_LOAD} internal function directly maps to
> the
> +@code{mask_ff_hssr_load} instruction pattern.

We've now started documenting IFNs more usefully these days. See e.g.

https://patchwork.sourceware.org/project/gcc/patch/[email protected]/

also what does the _ff_ stand for? If it's first_faulting, is that not already 
implicit in the
hssr?

> +
> +This internal function has a VDEF to represent it changing the global
> +HSSR state.
> +
> +@cindex @code{IFN_READ_HSSR_STATE} internal function
> +@item @samp{IFN_READ_HSSR_STATE}
> +The @code{IFN_READ_HSSR_STATE} internal function directly maps to the
> +@code{read_hssr_state} instruction pattern.
> +
> +This internal function has a VUSE to represent it reading the global
> +HSSR state.
> +
> +@cindex @code{IFN_SET_HSSR_STATE} internal function
> +@item @samp{IFN_SET_HSSR_STATE}
> +The @code{IFN_SET_HSSR_STATE} internal function directly maps to the
> +@code{set_hssr_state} instruction pattern.
> +
> +This internal function has a VDEF to represent it reading the global
> +HSSR state.
> +
> +@cindex @code{IFN_DETECT_HSSR_FAULT} internal function
> +@item @samp{IFN_DETECT_HSSR_FAULT}
> +The @code{IFN_DETECT_HSSR_FAULT} internal function directly maps to the
> +@code{detect_hssr_fault} instruction pattern.
> +
>  @cindex @code{IFN_SCATTER_STORE} internal function
>  @item @samp{IFN_SCATTER_STORE}
>  The @code{IFN_SCATTER_STORE} internal function directly maps to the
> diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
> index 80761b0a8a6..1dd9050b6e3 100644
> --- a/gcc/doc/md.texi
> +++ b/gcc/doc/md.texi
> @@ -7823,6 +7823,48 @@ of @code{QI} elements.
> 
>  This pattern is not allowed to @code{FAIL}.
> 
> +@mdindex mask_ff_hssr_load@var{m}@var{n}
> +@anchor{mask_ff_hssr_load@var{m}@var{n}}
> +@item @samp{mask_ff_hssr_load@var{m}@var{n}}
> +Perform a masked hardware-safe-speculative load of vector from memory
> +operand 1 of mode @var{m} into register operand 0.  The mask is provided in
> +register operand 2 of mode @var{n}.  Operand 3 (the ``else value'') is of
> mode
> +@var{m} and specifies which value is loaded when the mask is unset.
> +The predicate of operand 3 must only accept the else values that the target
> +actually supports.  Currently three values are attempted, zero, -1, and
> +undefined.  GCC handles an else value of zero more efficiently than -1 or
> +undefined.
> +
> +The instruction must also set the global HSSR state so it can be read by
> +@samp{read_hssr_state@var{m}}.
> +
> +This pattern is not allowed to @code{FAIL}.
> +
> +@mdindex read_hssr_state@var{m}
> +@anchor{read_hssr_state@var{m}}
> +@item @samp{read_hssr_state@var{m}}
> +Reads the global hardware safe speculative read state as a mask of mode
> @var{m}
> +into register operand 0.
> +
> +This pattern is not allowed to @code{FAIL}.
> +
> +@mdindex set_hssr_state@var{m}
> +@anchor{set_hssr_state@var{m}}
> +@item @samp{set_hssr_state@var{m}}
> +Sets the global HSSR state to be the mask in operand 0 of mode @var{m}.
> +
> +This pattern is not allowed to @code{FAIL}.
> +
> +@mdindex detect_hssr_fault@var{m}
> +@anchor{detect_hssr_fault@var{m}}
> +@item @samp{detect_hssr_fault@var{m}}
> +Checks if the given hardware safe speculative read mask of mode @var{m} in
> +operand 1 contains a partial read requiring a fixup path to be taken.
> +
> +Sets operand 0 of mode @samp{QI} to 1 if there is, and 0 otherwise.
> +
> +This pattern is not allowed to @code{FAIL}.

So this is purely a boolean? Why not make it a vect param? That allows for 
better
testing and experimentation and would allow a backend to set it based on a CPU
cost model.

I assume the branch is on the actual result of the read_hssr_state? So codegen 
is
state <- read_hssr_state
if (state && param_hssr_avoid_dependency)
  ...
  use state.
else
   state = param_hssr_avoid_dependency ? loop_mask : state;
   ...

so that when 0 it folds the branch away?

I haven't looked at it's usages yet but I am assuming that's sorta what it 
looks like?

Thanks,
Tamar

> +
>  @mdindex vec_perm@var{m}
>  @item @samp{vec_perm@var{m}}
>  Output a (variable) vector permutation.  Operand 0 is the destination
> diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
> index e3df0d1163c..fbd06f9dddd 100644
> --- a/gcc/gimple-fold.cc
> +++ b/gcc/gimple-fold.cc
> @@ -6049,6 +6049,10 @@ partial_load_store_mask_state (gcall *call, tree
> vectype)
>  static tree
>  gimple_fold_partial_load_store_mem_ref (gcall *call, tree vectype)
>  {
> +  internal_fn ifn = gimple_call_internal_fn (call);
> +  /* HSSR loads cannot be folded to MEM_REFs.  */
> +  if (internal_hssr_load_fn_p (ifn))
> +    return NULL_TREE;
>    /* Only fold if all lanes are active (unconditional).  */
>    if (partial_load_store_mask_state (call, vectype) != MASK_ALL_ACTIVE)
>      return NULL_TREE;
> @@ -6106,6 +6110,10 @@ gimple_fold_partial_load_store
> (gimple_stmt_iterator *gsi, gcall *call)
>            a store, move the vops from the old stmt to the new one.  */
>         if (!is_gimple_reg (lhs))
>           gimple_move_vops (new_stmt, call);
> +       /* HSSR loads have a VDEF in addition to represent changes to the
> +          global HSSR state.  */
> +       if (internal_hssr_load_fn_p (ifn))
> +         unlink_stmt_vdef (call);
>         gsi_replace (gsi, new_stmt, false);
>         return true;
>       }
> @@ -6387,6 +6395,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool
> inplace)
>       case IFN_MASK_LEN_SCATTER_STORE:
>       case IFN_MASK_STORE_LANES:
>       case IFN_MASK_LEN_STORE_LANES:
> +     case IFN_MASK_FF_HSSR_LOAD:
>         changed |= gimple_fold_partial_load_store (gsi, stmt);
>         break;
>       default:
> diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
> index 3fdf89e0e6e..6f451f9e3ff 100644
> --- a/gcc/internal-fn.cc
> +++ b/gcc/internal-fn.cc
> @@ -197,6 +197,9 @@ init_internal_fns ()
>  #define crc_direct { 1, -1, true }
>  #define reduc_sbool_direct { 0, 0, true }
>  #define select_vl_direct { 2, 0, false }
> +#define read_hssr_state_direct { -1, -1, false }
> +#define set_hssr_state_direct { 0, 0, false }
> +#define detect_hssr_fault_direct { 0, 0, false }
> 
>  const direct_internal_fn_info direct_internal_fn_array[IFN_LAST + 1] = {
>  #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) not_direct,
> @@ -4317,6 +4320,21 @@ expand_reduc_sbool_optab_fn (internal_fn fn,
> gcall *stmt, direct_optab optab)
>  #define expand_select_vl_optab_fn(FN, STMT, OPTAB) \
>    expand_convert_optab_fn (FN, STMT, OPTAB, 3)
> 
> +#define expand_read_hssr_state_optab_fn(FN, STMT, OPTAB) \
> +  expand_direct_optab_fn (FN, STMT, OPTAB, 0)
> +
> +#define expand_detect_hssr_fault_optab_fn(FN, STMT, OPTAB) \
> +  expand_direct_optab_fn (FN, STMT, OPTAB, 1)
> +
> +static void
> +expand_set_hssr_state_optab_fn (internal_fn fn, gcall *stmt,
> +                                   direct_optab optab)
> +{
> +  tree_pair types = direct_internal_fn_types (fn, stmt);
> +  insn_code icode = direct_optab_handler (optab, TYPE_MODE (types.first));
> +  expand_fn_using_insn (stmt, icode, 0, 1);
> +}
> +
>  /* Expanders for optabs that can use expand_convert_optab_fn.  */
> 
>  #define expand_unary_convert_optab_fn(FN, STMT, OPTAB) \
> @@ -4434,6 +4452,9 @@ multi_vector_optab_supported_p (convert_optab
> optab, tree_pair types,
>  #define direct_vec_extract_optab_supported_p convert_optab_supported_p
>  #define direct_reduc_sbool_optab_supported_p direct_optab_supported_p
>  #define direct_select_vl_optab_supported_p convert_optab_supported_p
> +#define direct_read_hssr_state_optab_supported_p
> direct_optab_supported_p
> +#define direct_set_hssr_state_optab_supported_p
> direct_optab_supported_p
> +#define direct_detect_hssr_fault_optab_supported_p
> direct_optab_supported_p
> 
>  /* Return the optab used by internal function FN.  */
> 
> @@ -5083,6 +5104,7 @@ internal_load_fn_p (internal_fn fn)
>      case IFN_MASK_LEN_GATHER_LOAD:
>      case IFN_LEN_LOAD:
>      case IFN_MASK_LEN_LOAD:
> +    case IFN_MASK_FF_HSSR_LOAD:
>        return true;
> 
>      default:
> @@ -5090,6 +5112,20 @@ internal_load_fn_p (internal_fn fn)
>      }
>  }
> 
> +/* Return true if IFN is a HSSR load.  */
> +
> +bool
> +internal_hssr_load_fn_p (internal_fn fn)
> +{
> +  switch (fn)
> +    {
> +    case IFN_MASK_FF_HSSR_LOAD:
> +      return true;
> +    default:
> +      return false;
> +    }
> +}
> +
>  /* Return true if IFN is some form of store to memory.  */
> 
>  bool
> @@ -5255,6 +5291,7 @@ internal_fn_else_index (internal_fn fn)
>      case IFN_MASK_LEN_LOAD:
>      case IFN_MASK_LOAD_LANES:
>      case IFN_MASK_LEN_LOAD_LANES:
> +    case IFN_MASK_FF_HSSR_LOAD:
>        return 3;
> 
>      case IFN_COND_FMA:
> @@ -5295,6 +5332,7 @@ internal_fn_mask_index (internal_fn fn)
>      case IFN_MASK_LEN_STORE_LANES:
>      case IFN_MASK_LEN_LOAD:
>      case IFN_MASK_LEN_STORE:
> +    case IFN_MASK_FF_HSSR_LOAD:
>        return 2;
> 
>      case IFN_MASK_LEN_STRIDED_LOAD:
> @@ -5363,6 +5401,7 @@ internal_fn_alias_ptr_index (internal_fn fn)
>      case IFN_SCATTER_STORE:
>      case IFN_MASK_SCATTER_STORE:
>      case IFN_MASK_LEN_SCATTER_STORE:
> +    case IFN_MASK_FF_HSSR_LOAD:
>        return 1;
> 
>      default:
> diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
> index 28ad5b7809d..68397c7bced 100644
> --- a/gcc/internal-fn.def
> +++ b/gcc/internal-fn.def
> @@ -651,6 +651,24 @@ DEF_INTERNAL_FN (BITINTTOFLOAT, ECF_PURE |
> ECF_LEAF, ". R . ")
>  DEF_INTERNAL_OPTAB_FN (BIT_ANDN, ECF_CONST, andn, binary)
>  DEF_INTERNAL_OPTAB_FN (BIT_IORN, ECF_CONST, iorn, binary)
> 
> +/* Hardware Safe Speculative Reads internal functions.  */
> +DEF_INTERNAL_OPTAB_FN (MASK_FF_HSSR_LOAD,
> +                    0,
> +                    mask_ff_hssr_load,
> +                    mask_load)
> +DEF_INTERNAL_OPTAB_FN (SET_HSSR_STATE,
> +                    ECF_NOTHROW,
> +                    set_hssr_state,
> +                    set_hssr_state)
> +DEF_INTERNAL_OPTAB_FN (READ_HSSR_STATE,
> +                    ECF_NOTHROW,
> +                    read_hssr_state,
> +                    read_hssr_state)
> +DEF_INTERNAL_OPTAB_FN (DETECT_HSSR_FAULT,
> +                    ECF_CONST,
> +                    detect_hssr_fault,
> +                    detect_hssr_fault)
> +
>  #undef DEF_INTERNAL_WIDENING_OPTAB_FN
>  #undef DEF_INTERNAL_SIGNED_COND_FN
>  #undef DEF_INTERNAL_COND_FN
> diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
> index 30930f388b0..d6a28a47d55 100644
> --- a/gcc/internal-fn.h
> +++ b/gcc/internal-fn.h
> @@ -234,6 +234,7 @@ extern bool can_interpret_as_conditional_op_p
> (gimple *, tree *,
>                                              tree *, tree *, tree *);
> 
>  extern bool internal_load_fn_p (internal_fn);
> +extern bool internal_hssr_load_fn_p (internal_fn);
>  extern bool internal_store_fn_p (internal_fn);
>  extern bool internal_gather_scatter_fn_p (internal_fn);
>  extern int internal_fn_mask_index (internal_fn);
> diff --git a/gcc/optabs.def b/gcc/optabs.def
> index d1f70a9b44a..4ae3ef8b78d 100644
> --- a/gcc/optabs.def
> +++ b/gcc/optabs.def
> @@ -118,6 +118,9 @@ OPTAB_CD (usdot_prod_optab,
> "usdot_prod$I$a$b")
>  OPTAB_CD (while_ult_optab, "while_ult$a$b")
>  OPTAB_CD (select_vl_optab, "select_vl$a$b")
> 
> +/* Hardware Safe Speculative Reads optabs.  */
> +OPTAB_CD (mask_ff_hssr_load_optab, "mask_ff_hssr_load$a$b")
> +
>  OPTAB_NL(add_optab, "add$P$a3", PLUS, "add", '3',
> gen_int_fp_fixed_libfunc)
>  OPTAB_NX(add_optab, "add$F$a3")
>  OPTAB_NX(add_optab, "add$Q$a3")
> @@ -241,6 +244,11 @@ OPTAB_D (push_optab, "push$a1")
>  OPTAB_D (reload_in_optab, "reload_in$a")
>  OPTAB_D (reload_out_optab, "reload_out$a")
> 
> +/* Hardware Safe Speculative Reads optabs.  */
> +OPTAB_D (read_hssr_state_optab, "read_hssr_state$a")
> +OPTAB_D (set_hssr_state_optab, "set_hssr_state$a")
> +OPTAB_D (detect_hssr_fault_optab, "detect_hssr_fault$a")
> +
>  OPTAB_DC(cbranch_optab, "cbranch$a4", COMPARE)
>  OPTAB_D (tbranch_eq_optab, "tbranch_eq$a3")
>  OPTAB_D (tbranch_ne_optab, "tbranch_ne$a3")
> diff --git a/gcc/tree-data-ref.cc b/gcc/tree-data-ref.cc
> index d6da0fe58c3..4100c66521b 100644
> --- a/gcc/tree-data-ref.cc
> +++ b/gcc/tree-data-ref.cc
> @@ -5905,6 +5905,7 @@ get_references_in_stmt (gimple *stmt,
> vec<data_ref_loc, va_heap> *references)
>             break;
>           }
>         case IFN_MASK_LOAD:
> +       case IFN_MASK_FF_HSSR_LOAD:
>         case IFN_MASK_STORE:
>         break;
>         case IFN_MASK_CALL:
> @@ -5962,6 +5963,7 @@ get_references_in_stmt (gimple *stmt,
> vec<data_ref_loc, va_heap> *references)
>       switch (gimple_call_internal_fn (stmt))
>         {
>         case IFN_MASK_LOAD:
> +       case IFN_MASK_FF_HSSR_LOAD:
>           if (gimple_call_lhs (stmt) == NULL_TREE)
>             break;
>           ref.is_read = true;
> diff --git a/gcc/tree-ssa-alias.cc b/gcc/tree-ssa-alias.cc
> index 7fc87b3b944..6122a6505c0 100644
> --- a/gcc/tree-ssa-alias.cc
> +++ b/gcc/tree-ssa-alias.cc
> @@ -2888,6 +2888,7 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref
> *ref, bool tbaa_p)
>        case IFN_MASK_LEN_STORE_LANES:
>       goto process_args;
>        case IFN_MASK_LOAD:
> +      case IFN_MASK_FF_HSSR_LOAD:
>        case IFN_LEN_LOAD:
>        case IFN_MASK_LEN_LOAD:
>        case IFN_MASK_LOAD_LANES:
> diff --git a/gcc/tree-ssa-loop-ivopts.cc b/gcc/tree-ssa-loop-ivopts.cc
> index fa3c2c136fc..3765b7070e9 100644
> --- a/gcc/tree-ssa-loop-ivopts.cc
> +++ b/gcc/tree-ssa-loop-ivopts.cc
> @@ -2371,6 +2371,7 @@ get_mem_type_for_internal_fn (gcall *call, tree
> *op_p)
>    switch (gimple_call_internal_fn (call))
>      {
>      case IFN_MASK_LOAD:
> +    case IFN_MASK_FF_HSSR_LOAD:
>      case IFN_MASK_LOAD_LANES:
>      case IFN_MASK_LEN_LOAD_LANES:
>      case IFN_LEN_LOAD:
> @@ -7639,6 +7640,7 @@ get_alias_ptr_type_for_ptr_address (iv_use *use)
>    switch (gimple_call_internal_fn (call))
>      {
>      case IFN_MASK_LOAD:
> +    case IFN_MASK_FF_HSSR_LOAD:
>      case IFN_MASK_STORE:
>      case IFN_MASK_LOAD_LANES:
>      case IFN_MASK_STORE_LANES:
> diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
> index eee02124d5b..e0a32ade79a 100644
> --- a/gcc/tree-vect-slp.cc
> +++ b/gcc/tree-vect-slp.cc
> @@ -587,6 +587,9 @@ vect_get_operand_map (const gimple *stmt, bool
> gather_scatter_p,
>         case IFN_MASK_LOAD:
>           return gather_scatter_p ? off_arg2_arg3_map : arg2_arg3_map;
> 
> +       case IFN_MASK_FF_HSSR_LOAD:
> +         return arg2_arg3_map;
> +
>         case IFN_GATHER_LOAD:
>           return arg2_map;
> 
> --
> 2.43.0

Reply via email to