Add helpers to test whether an optab can be implemented

The vectoriser and vector lowering passes both had tests of the form:

    if (op
	&& (optab_handler (op, compute_mode) != CODE_FOR_nothing
	    || optab_libfunc (op, compute_mode)))
      ...success...
    if (code == MULT_HIGHPART_EXPR
	&& can_mult_highpart_p (compute_mode,
				TYPE_UNSIGNED (compute_type)))
      ...success...

This patch adds helper routines for this kind of test, so that it's
easier to handle other optab alternatives in a similar way.

gcc/
	* optabs-query.cc (can_open_code_p, can_implement_p): Declare.
	* optabs-query.h (can_open_code_p, can_implement_p): New functions.
	* optabs-tree.cc (target_supports_op_p): Use can_implement_p.
	* tree-vect-stmts.cc (vectorizable_operation): Likewise.
	* tree-vect-generic.cc (get_compute_type): Likewise.  Remove code
	parameter.
	(expand_vector_scalar_condition, expand_vector_conversion)
	(expand_vector_operations_1): Update calls accordingly.
This commit is contained in:
Richard Sandiford 2024-11-20 10:04:45 +00:00
parent 3ec0b7cd7c
commit 485ab50c20
5 changed files with 67 additions and 62 deletions

View File

@ -781,3 +781,33 @@ can_vec_extract (machine_mode mode, machine_mode extr_mode)
/* We assume we can pun mode to vmode and imode to extr_mode. */
return true;
}
/* Return true if we can implement OP for mode MODE directly, without resorting
to a libfunc. This usually means that OP will be implemented inline.
Note that this function cannot tell whether the target pattern chooses to
use libfuncs internally. */
bool
can_open_code_p (optab op, machine_mode mode)
{
if (optab_handler (op, mode) != CODE_FOR_nothing)
return true;
if (op == umul_highpart_optab)
return can_mult_highpart_p (mode, true);
if (op == smul_highpart_optab)
return can_mult_highpart_p (mode, false);
return false;
}
/* Return true if we can implement OP for mode MODE in some way, either by
open-coding it or by calling a libfunc. */
bool
can_implement_p (optab op, machine_mode mode)
{
return can_open_code_p (op, mode) || optab_libfunc (op, mode);
}

View File

@ -172,6 +172,8 @@ bool supports_vec_gather_load_p (machine_mode = E_VOIDmode,
vec<int> * = nullptr);
bool supports_vec_scatter_store_p (machine_mode = E_VOIDmode);
bool can_vec_extract (machine_mode, machine_mode);
bool can_open_code_p (optab, machine_mode);
bool can_implement_p (optab, machine_mode);
/* Version of find_widening_optab_handler_and_mode that operates on
specific mode types. */

View File

@ -504,8 +504,7 @@ target_supports_op_p (tree type, enum tree_code code,
enum optab_subtype ot_subtype)
{
optab ot = optab_for_tree_code (code, type, ot_subtype);
return (ot != unknown_optab
&& optab_handler (ot, TYPE_MODE (type)) != CODE_FOR_nothing);
return ot != unknown_optab && can_implement_p (ot, TYPE_MODE (type));
}
/* Return true if the target has support for masked load/store.

View File

@ -1602,49 +1602,29 @@ ssa_uniform_vector_p (tree op)
return NULL_TREE;
}
/* Return type in which CODE operation with optab OP can be
computed. */
/* Return the type that should be used to implement OP on type TYPE.
This is TYPE itself if the target can do the operation directly,
otherwise it is a scalar type or a smaller vector type. */
static tree
get_compute_type (enum tree_code code, optab op, tree type)
get_compute_type (optab op, tree type)
{
/* For very wide vectors, try using a smaller vector mode. */
tree compute_type = type;
if (op
&& (!VECTOR_MODE_P (TYPE_MODE (type))
|| optab_handler (op, TYPE_MODE (type)) == CODE_FOR_nothing))
if (op)
{
tree vector_compute_type
= type_for_widest_vector_mode (type, op);
if (VECTOR_MODE_P (TYPE_MODE (type))
&& can_implement_p (op, TYPE_MODE (type)))
return type;
/* For very wide vectors, try using a smaller vector mode. */
tree vector_compute_type = type_for_widest_vector_mode (type, op);
if (vector_compute_type != NULL_TREE
&& maybe_ne (TYPE_VECTOR_SUBPARTS (vector_compute_type), 1U)
&& (optab_handler (op, TYPE_MODE (vector_compute_type))
!= CODE_FOR_nothing))
compute_type = vector_compute_type;
&& can_implement_p (op, TYPE_MODE (vector_compute_type)))
return vector_compute_type;
}
/* If we are breaking a BLKmode vector into smaller pieces,
type_for_widest_vector_mode has already looked into the optab,
so skip these checks. */
if (compute_type == type)
{
machine_mode compute_mode = TYPE_MODE (compute_type);
if (VECTOR_MODE_P (compute_mode))
{
if (op
&& (optab_handler (op, compute_mode) != CODE_FOR_nothing
|| optab_libfunc (op, compute_mode)))
return compute_type;
if (code == MULT_HIGHPART_EXPR
&& can_mult_highpart_p (compute_mode,
TYPE_UNSIGNED (compute_type)))
return compute_type;
}
/* There is no operation in hardware, so fall back to scalars. */
compute_type = TREE_TYPE (type);
}
return compute_type;
/* There is no operation in hardware, so fall back to scalars. */
return TREE_TYPE (type);
}
static tree
@ -1667,7 +1647,7 @@ expand_vector_scalar_condition (gimple_stmt_iterator *gsi)
gassign *stmt = as_a <gassign *> (gsi_stmt (*gsi));
tree lhs = gimple_assign_lhs (stmt);
tree type = TREE_TYPE (lhs);
tree compute_type = get_compute_type (COND_EXPR, mov_optab, type);
tree compute_type = get_compute_type (mov_optab, type);
machine_mode compute_mode = TYPE_MODE (compute_type);
gcc_assert (compute_mode != BLKmode);
tree rhs2 = gimple_assign_rhs2 (stmt);
@ -1850,7 +1830,7 @@ expand_vector_conversion (gimple_stmt_iterator *gsi)
}
if (optab1)
compute_type = get_compute_type (code1, optab1, arg_type);
compute_type = get_compute_type (optab1, arg_type);
enum insn_code icode1;
if (VECTOR_TYPE_P (compute_type)
&& ((icode1 = optab_handler (optab1, TYPE_MODE (compute_type)))
@ -1931,7 +1911,7 @@ expand_vector_conversion (gimple_stmt_iterator *gsi)
}
if (optab1 && optab2)
compute_type = get_compute_type (code1, optab1, arg_type);
compute_type = get_compute_type (optab1, arg_type);
enum insn_code icode1, icode2;
if (VECTOR_TYPE_P (compute_type)
@ -2184,12 +2164,12 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi)
{
op = optab_for_tree_code (code, type, optab_scalar);
compute_type = get_compute_type (code, op, type);
compute_type = get_compute_type (op, type);
if (compute_type == type)
return;
/* The rtl expander will expand vector/scalar as vector/vector
if necessary. Pick one with wider vector type. */
tree compute_vtype = get_compute_type (code, opv, type);
tree compute_vtype = get_compute_type (opv, type);
if (subparts_gt (compute_vtype, compute_type))
{
compute_type = compute_vtype;
@ -2200,7 +2180,7 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi)
if (code == LROTATE_EXPR || code == RROTATE_EXPR)
{
if (compute_type == NULL_TREE)
compute_type = get_compute_type (code, op, type);
compute_type = get_compute_type (op, type);
if (compute_type == type)
return;
/* Before splitting vector rotates into scalar rotates,
@ -2213,11 +2193,11 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi)
{
optab oplv = vashl_optab, opl = ashl_optab;
optab oprv = vlshr_optab, opr = lshr_optab, opo = ior_optab;
tree compute_lvtype = get_compute_type (LSHIFT_EXPR, oplv, type);
tree compute_rvtype = get_compute_type (RSHIFT_EXPR, oprv, type);
tree compute_otype = get_compute_type (BIT_IOR_EXPR, opo, type);
tree compute_ltype = get_compute_type (LSHIFT_EXPR, opl, type);
tree compute_rtype = get_compute_type (RSHIFT_EXPR, opr, type);
tree compute_lvtype = get_compute_type (oplv, type);
tree compute_rvtype = get_compute_type (oprv, type);
tree compute_otype = get_compute_type (opo, type);
tree compute_ltype = get_compute_type (opl, type);
tree compute_rtype = get_compute_type (opr, type);
/* The rtl expander will expand vector/scalar as vector/vector
if necessary. Pick one with wider vector type. */
if (subparts_gt (compute_lvtype, compute_ltype))
@ -2263,7 +2243,7 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi)
op = optab_for_tree_code (MINUS_EXPR, type, optab_default);
if (compute_type == NULL_TREE)
compute_type = get_compute_type (code, op, type);
compute_type = get_compute_type (op, type);
if (compute_type == type)
return;

View File

@ -6904,21 +6904,15 @@ vectorizable_operation (vec_info *vinfo,
/* Supportable by target? */
vec_mode = TYPE_MODE (vectype);
if (code == MULT_HIGHPART_EXPR)
target_support_p = can_mult_highpart_p (vec_mode, TYPE_UNSIGNED (vectype));
else
optab = optab_for_tree_code (code, vectype, optab_default);
if (!optab)
{
optab = optab_for_tree_code (code, vectype, optab_default);
if (!optab)
{
if (dump_enabled_p ())
dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
"no optab.\n");
return false;
}
target_support_p = (optab_handler (optab, vec_mode) != CODE_FOR_nothing
|| optab_libfunc (optab, vec_mode));
if (dump_enabled_p ())
dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
"no optab.\n");
return false;
}
target_support_p = can_implement_p (optab, vec_mode);
bool using_emulated_vectors_p = vect_emulated_vector_p (vectype);
if (!target_support_p || using_emulated_vectors_p)