aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-vect-data-refs.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-vect-data-refs.cc')
-rw-r--r--gcc/tree-vect-data-refs.cc370
1 files changed, 304 insertions, 66 deletions
diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc
index c794110..e8cfb88 100644
--- a/gcc/tree-vect-data-refs.cc
+++ b/gcc/tree-vect-data-refs.cc
@@ -4425,6 +4425,168 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
return opt_result::success ();
}
+/* Structure to hold information about a supported gather/scatter
+ configuration. */
+struct gather_scatter_config
+{
+ internal_fn ifn;
+ tree offset_vectype;
+ int scale;
+ vec<int> elsvals;
+};
+
+/* Determine which gather/scatter IFN is supported for the given parameters.
+ IFN_MASK_GATHER_LOAD, IFN_GATHER_LOAD, and IFN_MASK_LEN_GATHER_LOAD
+ are mutually exclusive, so we only need to find one. Return the
+ supported IFN or IFN_LAST if none are supported. */
+
+static internal_fn
+vect_gather_scatter_which_ifn (bool read_p, bool masked_p,
+ tree vectype, tree memory_type,
+ tree offset_vectype, int scale,
+ vec<int> *elsvals)
+{
+ /* Work out which functions to try. */
+ internal_fn ifn, alt_ifn, alt_ifn2;
+ if (read_p)
+ {
+ ifn = masked_p ? IFN_MASK_GATHER_LOAD : IFN_GATHER_LOAD;
+ alt_ifn = IFN_MASK_GATHER_LOAD;
+ alt_ifn2 = IFN_MASK_LEN_GATHER_LOAD;
+ }
+ else
+ {
+ ifn = masked_p ? IFN_MASK_SCATTER_STORE : IFN_SCATTER_STORE;
+ alt_ifn = IFN_MASK_SCATTER_STORE;
+ alt_ifn2 = IFN_MASK_LEN_SCATTER_STORE;
+ }
+
+ if (!offset_vectype)
+ return IFN_LAST;
+
+ if (internal_gather_scatter_fn_supported_p (ifn, vectype, memory_type,
+ offset_vectype, scale, elsvals))
+ return ifn;
+ if (internal_gather_scatter_fn_supported_p (alt_ifn, vectype, memory_type,
+ offset_vectype, scale, elsvals))
+ return alt_ifn;
+ if (internal_gather_scatter_fn_supported_p (alt_ifn2, vectype, memory_type,
+ offset_vectype, scale, elsvals))
+ return alt_ifn2;
+
+ return IFN_LAST;
+}
+
+/* Collect all supported offset vector types for a gather load or scatter
+ store. READ_P is true for loads and false for stores. MASKED_P is true
+ if the load or store is conditional. VECTYPE is the data vector type.
+ MEMORY_TYPE is the type of the memory elements being loaded or stored,
+ and OFFSET_TYPE is the type of the offset.
+ SCALE is the amount by which the offset should be multiplied.
+
+ Return a vector of all configurations the target supports (which can
+ be none). */
+
+static auto_vec<gather_scatter_config>
+vect_gather_scatter_get_configs (vec_info *vinfo, bool read_p, bool masked_p,
+ tree vectype, tree memory_type,
+ tree offset_type, int scale)
+{
+ auto_vec<gather_scatter_config> configs;
+
+ auto_vec<tree, 8> offset_types_to_try;
+
+ /* Try all sizes from the offset type's precision up to POINTER_SIZE. */
+ for (unsigned int bits = TYPE_PRECISION (offset_type);
+ bits <= POINTER_SIZE;
+ bits *= 2)
+ {
+ /* Signed variant. */
+ offset_types_to_try.safe_push
+ (build_nonstandard_integer_type (bits, 0));
+ /* Unsigned variant. */
+ offset_types_to_try.safe_push
+ (build_nonstandard_integer_type (bits, 1));
+ }
+
+ /* Once we find which IFN works for one offset type, we know that it
+ will work for other offset types as well. Then we can perform
+ the checks for the remaining offset types with only that IFN.
+ However, we might need to try different offset types to find which
+ IFN is supported, since the check is offset-type-specific. */
+ internal_fn ifn = IFN_LAST;
+
+ /* Try each offset type. */
+ for (unsigned int i = 0; i < offset_types_to_try.length (); i++)
+ {
+ tree offset_type = offset_types_to_try[i];
+ tree offset_vectype = get_vectype_for_scalar_type (vinfo, offset_type);
+ if (!offset_vectype)
+ continue;
+
+ /* Try multiple scale values. Start with exact match, then try
+ smaller common scales that a target might support . */
+ int scales_to_try[] = {scale, 1, 2, 4, 8};
+
+ for (unsigned int j = 0;
+ j < sizeof (scales_to_try) / sizeof (*scales_to_try);
+ j++)
+ {
+ int try_scale = scales_to_try[j];
+
+ /* Skip scales >= requested scale (except for exact match). */
+ if (j > 0 && try_scale >= scale)
+ continue;
+
+ /* Skip if requested scale is not a multiple of this scale. */
+ if (j > 0 && scale % try_scale != 0)
+ continue;
+
+ vec<int> elsvals = vNULL;
+
+ /* If we haven't determined which IFN is supported yet, try all three
+ to find which one the target supports. */
+ if (ifn == IFN_LAST)
+ {
+ ifn = vect_gather_scatter_which_ifn (read_p, masked_p,
+ vectype, memory_type,
+ offset_vectype, try_scale,
+ &elsvals);
+ if (ifn != IFN_LAST)
+ {
+ /* Found which IFN is supported. Save this configuration. */
+ gather_scatter_config config;
+ config.ifn = ifn;
+ config.offset_vectype = offset_vectype;
+ config.scale = try_scale;
+ config.elsvals = elsvals;
+ configs.safe_push (config);
+ }
+ }
+ else
+ {
+ /* We already know which IFN is supported, just check if this
+ offset type and scale work with it. */
+ if (internal_gather_scatter_fn_supported_p (ifn, vectype,
+ memory_type,
+ offset_vectype,
+ try_scale,
+ &elsvals))
+ {
+ gather_scatter_config config;
+ config.ifn = ifn;
+ config.offset_vectype = offset_vectype;
+ config.scale = try_scale;
+ config.elsvals = elsvals;
+ configs.safe_push (config);
+ }
+ }
+ }
+ }
+
+ return configs;
+}
+
/* Check whether we can use an internal function for a gather load
or scatter store. READ_P is true for loads and false for stores.
MASKED_P is true if the load or store is conditional. MEMORY_TYPE is
@@ -4433,18 +4595,30 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
base address. If OFFSET_TYPE is scalar the function chooses an
appropriate vector type for it. SCALE is the amount by which the
offset should be multiplied *after* it has been converted to address width.
+ If the target does not support the requested SCALE, SUPPORTED_SCALE
+ will contain the scale that is actually supported
+ (which may be smaller, requiring additional multiplication).
+ Otherwise SUPPORTED_SCALE is 0.
Return true if the function is supported, storing the function id in
*IFN_OUT and the vector type for the offset in *OFFSET_VECTYPE_OUT.
+ If we support an offset vector type with different signedness than
+ OFFSET_TYPE store it in SUPPORTED_OFFSET_VECTYPE.
- If we can use gather and store the possible else values in ELSVALS. */
+ If we can use gather/scatter and ELSVALS is nonzero, store the possible
+ else values in ELSVALS. */
bool
vect_gather_scatter_fn_p (vec_info *vinfo, bool read_p, bool masked_p,
tree vectype, tree memory_type, tree offset_type,
- int scale, internal_fn *ifn_out,
- tree *offset_vectype_out, vec<int> *elsvals)
+ int scale, int *supported_scale,
+ internal_fn *ifn_out,
+ tree *offset_vectype_out,
+ tree *supported_offset_vectype,
+ vec<int> *elsvals)
{
+ *supported_offset_vectype = NULL_TREE;
+ *supported_scale = 0;
unsigned int memory_bits = tree_to_uhwi (TYPE_SIZE (memory_type));
unsigned int element_bits = vector_element_bits (vectype);
if (element_bits != memory_bits)
@@ -4452,80 +4626,131 @@ vect_gather_scatter_fn_p (vec_info *vinfo, bool read_p, bool masked_p,
memory elements. */
return false;
- /* Work out which function we need. */
- internal_fn ifn, alt_ifn, alt_ifn2;
- if (read_p)
- {
- ifn = masked_p ? IFN_MASK_GATHER_LOAD : IFN_GATHER_LOAD;
- alt_ifn = IFN_MASK_GATHER_LOAD;
- /* When target supports MASK_LEN_GATHER_LOAD, we always
- use MASK_LEN_GATHER_LOAD regardless whether len and
- mask are valid or not. */
- alt_ifn2 = IFN_MASK_LEN_GATHER_LOAD;
- }
- else
- {
- ifn = masked_p ? IFN_MASK_SCATTER_STORE : IFN_SCATTER_STORE;
- alt_ifn = IFN_MASK_SCATTER_STORE;
- /* When target supports MASK_LEN_SCATTER_STORE, we always
- use MASK_LEN_SCATTER_STORE regardless whether len and
- mask are valid or not. */
- alt_ifn2 = IFN_MASK_LEN_SCATTER_STORE;
- }
+ /* Get the original offset vector type for comparison. */
+ tree offset_vectype = VECTOR_TYPE_P (offset_type)
+ ? offset_type : get_vectype_for_scalar_type (vinfo, offset_type);
- for (;;)
+ offset_type = TREE_TYPE (offset_vectype);
+
+ /* Get all supported configurations for this data vector type. */
+ auto_vec<gather_scatter_config> configs
+ = vect_gather_scatter_get_configs (vinfo, read_p, masked_p, vectype,
+ memory_type, offset_type, scale);
+
+ if (configs.is_empty ())
+ return false;
+
+ /* Selection priority:
+ 1 - Exact scale match + offset type match
+ 2 - Exact scale match + sign-swapped offset
+ 3 - Smaller scale + offset type match
+ 4 - Smaller scale + sign-swapped offset
+ Within each category, prefer smaller offset types. */
+
+ /* First pass: exact scale match with no conversion. */
+ for (unsigned int i = 0; i < configs.length (); i++)
{
- tree offset_vectype;
- if (VECTOR_TYPE_P (offset_type))
- offset_vectype = offset_type;
- else
+ if (configs[i].scale == scale
+ && TYPE_SIGN (configs[i].offset_vectype)
+ == TYPE_SIGN (offset_vectype))
{
- offset_vectype = get_vectype_for_scalar_type (vinfo, offset_type);
- if (!offset_vectype)
- return false;
+ *ifn_out = configs[i].ifn;
+ *offset_vectype_out = configs[i].offset_vectype;
+ if (elsvals)
+ *elsvals = configs[i].elsvals;
+ return true;
}
+ }
- /* Test whether the target supports this combination. */
- if (internal_gather_scatter_fn_supported_p (ifn, vectype, memory_type,
- offset_vectype, scale,
- elsvals))
+ /* No direct match. This means we try to find either
+ - a sign-swapped offset vectype or
+ - a different scale and 2x larger offset type
+ - a different scale and larger sign-swapped offset vectype. */
+ unsigned int offset_precision = TYPE_PRECISION (TREE_TYPE (offset_vectype));
+ unsigned int needed_precision
+ = TYPE_UNSIGNED (offset_vectype) ? offset_precision * 2 : POINTER_SIZE;
+ needed_precision = std::min (needed_precision, (unsigned) POINTER_SIZE);
+
+ /* Second pass: No direct match. This means we try to find a sign-swapped
+ offset vectype. */
+ enum tree_code tmp;
+ for (unsigned int i = 0; i < configs.length (); i++)
+ {
+ unsigned int precision
+ = TYPE_PRECISION (TREE_TYPE (configs[i].offset_vectype));
+ if (configs[i].scale == scale
+ && precision >= needed_precision
+ && (supportable_convert_operation (CONVERT_EXPR,
+ configs[i].offset_vectype,
+ offset_vectype, &tmp)
+ || (needed_precision == offset_precision
+ && tree_nop_conversion_p (configs[i].offset_vectype,
+ offset_vectype))))
{
- *ifn_out = ifn;
+ *ifn_out = configs[i].ifn;
*offset_vectype_out = offset_vectype;
+ *supported_offset_vectype = configs[i].offset_vectype;
+ if (elsvals)
+ *elsvals = configs[i].elsvals;
return true;
}
- else if (!masked_p
- && internal_gather_scatter_fn_supported_p (alt_ifn, vectype,
- memory_type,
- offset_vectype,
- scale, elsvals))
+ }
+
+ /* Third pass: Try a smaller scale with the same signedness. */
+ needed_precision = offset_precision * 2;
+ needed_precision = std::min (needed_precision, (unsigned) POINTER_SIZE);
+
+ for (unsigned int i = 0; i < configs.length (); i++)
+ {
+ unsigned int precision
+ = TYPE_PRECISION (TREE_TYPE (configs[i].offset_vectype));
+ if (configs[i].scale < scale
+ && precision >= needed_precision
+ && (supportable_convert_operation (CONVERT_EXPR,
+ configs[i].offset_vectype,
+ offset_vectype, &tmp)
+ || (needed_precision == offset_precision
+ && tree_nop_conversion_p (configs[i].offset_vectype,
+ offset_vectype))))
{
- *ifn_out = alt_ifn;
- *offset_vectype_out = offset_vectype;
+ *ifn_out = configs[i].ifn;
+ *offset_vectype_out = configs[i].offset_vectype;
+ *supported_scale = configs[i].scale;
+ if (elsvals)
+ *elsvals = configs[i].elsvals;
return true;
}
- else if (internal_gather_scatter_fn_supported_p (alt_ifn2, vectype,
- memory_type,
- offset_vectype, scale,
- elsvals))
+ }
+
+ /* Fourth pass: Try a smaller scale and sign-swapped offset vectype. */
+ needed_precision
+ = TYPE_UNSIGNED (offset_vectype) ? offset_precision * 2 : POINTER_SIZE;
+ needed_precision = std::min (needed_precision, (unsigned) POINTER_SIZE);
+
+ for (unsigned int i = 0; i < configs.length (); i++)
+ {
+ unsigned int precision
+ = TYPE_PRECISION (TREE_TYPE (configs[i].offset_vectype));
+ if (configs[i].scale < scale
+ && precision >= needed_precision
+ && (supportable_convert_operation (CONVERT_EXPR,
+ configs[i].offset_vectype,
+ offset_vectype, &tmp)
+ || (needed_precision == offset_precision
+ && tree_nop_conversion_p (configs[i].offset_vectype,
+ offset_vectype))))
{
- *ifn_out = alt_ifn2;
+ *ifn_out = configs[i].ifn;
*offset_vectype_out = offset_vectype;
+ *supported_offset_vectype = configs[i].offset_vectype;
+ *supported_scale = configs[i].scale;
+ if (elsvals)
+ *elsvals = configs[i].elsvals;
return true;
}
-
- /* For fixed offset vector type we're done. */
- if (VECTOR_TYPE_P (offset_type))
- return false;
-
- if (TYPE_PRECISION (offset_type) >= POINTER_SIZE
- && TYPE_PRECISION (offset_type) >= element_bits)
- return false;
-
- /* Try a larger offset vector type. */
- offset_type = build_nonstandard_integer_type
- (TYPE_PRECISION (offset_type) * 2, TYPE_UNSIGNED (offset_type));
}
+
+ return false;
}
/* STMT_INFO is a call to an internal gather load or scatter store function.
@@ -4678,6 +4903,8 @@ vect_check_gather_scatter (stmt_vec_info stmt_info, tree vectype,
base = fold_convert (sizetype, base);
base = size_binop (PLUS_EXPR, base, size_int (pbytepos));
+ int tmp_scale;
+ tree tmp_offset_vectype;
/* OFF at this point may be either a SSA_NAME or some tree expression
from get_inner_reference. Try to peel off loop invariants from it
@@ -4750,14 +4977,18 @@ vect_check_gather_scatter (stmt_vec_info stmt_info, tree vectype,
&& !vect_gather_scatter_fn_p (loop_vinfo, DR_IS_READ (dr),
masked_p, vectype, memory_type,
signed_char_type_node,
- new_scale, &ifn,
+ new_scale, &tmp_scale,
+ &ifn,
&offset_vectype,
+ &tmp_offset_vectype,
elsvals)
&& !vect_gather_scatter_fn_p (loop_vinfo, DR_IS_READ (dr),
masked_p, vectype, memory_type,
unsigned_char_type_node,
- new_scale, &ifn,
+ new_scale, &tmp_scale,
+ &ifn,
&offset_vectype,
+ &tmp_offset_vectype,
elsvals))
break;
scale = new_scale;
@@ -4780,8 +5011,12 @@ vect_check_gather_scatter (stmt_vec_info stmt_info, tree vectype,
&& !POINTER_TYPE_P (TREE_TYPE (off))
&& vect_gather_scatter_fn_p (loop_vinfo, DR_IS_READ (dr),
masked_p, vectype, memory_type,
- TREE_TYPE (off), scale, &ifn,
- &offset_vectype, elsvals))
+ TREE_TYPE (off),
+ scale, &tmp_scale,
+ &ifn,
+ &offset_vectype,
+ &tmp_offset_vectype,
+ elsvals))
break;
if (TYPE_PRECISION (TREE_TYPE (op0))
@@ -4834,8 +5069,11 @@ vect_check_gather_scatter (stmt_vec_info stmt_info, tree vectype,
if (use_ifn_p)
{
if (!vect_gather_scatter_fn_p (loop_vinfo, DR_IS_READ (dr), masked_p,
- vectype, memory_type, offtype, scale,
- &ifn, &offset_vectype, elsvals))
+ vectype, memory_type, offtype,
+ scale, &tmp_scale,
+ &ifn, &offset_vectype,
+ &tmp_offset_vectype,
+ elsvals))
ifn = IFN_LAST;
decl = NULL_TREE;
}