/* ACLE support for Arm MVE
- Copyright (C) 2021-2023 Free Software Foundation, Inc.
+ Copyright (C) 2021-2024 Free Software Foundation, Inc.
This file is part of GCC.
#include "fold-const.h"
#include "gimple.h"
#include "gimple-iterator.h"
+#include "explow.h"
#include "emit-rtl.h"
#include "langhooks.h"
#include "stringpool.h"
TYPE_##CLASS == TYPE_signed || TYPE_##CLASS == TYPE_unsigned, \
TYPE_##CLASS == TYPE_unsigned, \
TYPE_##CLASS == TYPE_float, \
+ TYPE_##CLASS == TYPE_poly, \
0, \
MODE },
#include "arm-mve-builtins.def"
- { "", NUM_VECTOR_TYPES, TYPE_bool, 0, 0, false, false, false,
+ { "", NUM_VECTOR_TYPES, TYPE_bool, 0, 0, false, false, false, false,
0, VOIDmode }
};
#define TYPES_all_signed(S, D) \
S (s8), S (s16), S (s32)
+/* _p8 _p16. */
+#define TYPES_poly_8_16(S, D) \
+ S (p8), S (p16)
+
/* _u8 _u16 _u32. */
#define TYPES_all_unsigned(S, D) \
S (u8), S (u16), S (u32)
DEF_MVE_TYPES_ARRAY (integer_8_16);
DEF_MVE_TYPES_ARRAY (integer_16_32);
DEF_MVE_TYPES_ARRAY (integer_32);
+DEF_MVE_TYPES_ARRAY (poly_8_16);
DEF_MVE_TYPES_ARRAY (signed_16_32);
DEF_MVE_TYPES_ARRAY (signed_32);
DEF_MVE_TYPES_ARRAY (reinterpret_integer);
preserve_user_namespace);
}
+/* Return the function decl with MVE function subcode CODE, or error_mark_node
+ if no such function exists. */
+tree
+builtin_decl (unsigned int code)
+{
+ if (code >= vec_safe_length (registered_functions))
+ return error_mark_node;
+ return (*registered_functions)[code]->decl;
+}
+
/* Return true if CANDIDATE is equivalent to MODEL_TYPE for overloading
purposes. */
static bool
&& TYPE_MAIN_VARIANT (model_type) == TYPE_MAIN_VARIANT (candidate));
}
+/* If TYPE is a valid MVE element type, return the corresponding type
+ suffix, otherwise return NUM_TYPE_SUFFIXES. */
+static type_suffix_index
+find_type_suffix_for_scalar_type (const_tree type)
+{
+ /* A linear search should be OK here, since the code isn't hot and
+ the number of types is only small. */
+ for (unsigned int suffix_i = 0; suffix_i < NUM_TYPE_SUFFIXES; ++suffix_i)
+ {
+ vector_type_index vector_i = type_suffixes[suffix_i].vector_type;
+ if (matches_type_p (scalar_types[vector_i], type))
+ return type_suffix_index (suffix_i);
+ }
+ return NUM_TYPE_SUFFIXES;
+}
+
/* Report an error against LOCATION that the user has tried to use
a floating point function when the mve.fp extension is disabled. */
static void
return false;
if (mode_suffix_id == MODE_r
+ || base == functions::vcmlaq
+ || base == functions::vcmlaq_rot90
+ || base == functions::vcmlaq_rot180
+ || base == functions::vcmlaq_rot270
+ || base == functions::vcmpeqq
+ || base == functions::vcmpneq
+ || base == functions::vcmpgeq
+ || base == functions::vcmpgtq
+ || base == functions::vcmpleq
+ || base == functions::vcmpltq
+ || base == functions::vcmpcsq
+ || base == functions::vcmphiq
+ || base == functions::vfmaq
+ || base == functions::vfmasq
+ || base == functions::vfmsq
+ || base == functions::vmaxaq
+ || base == functions::vmaxnmaq
+ || base == functions::vminaq
+ || base == functions::vminnmaq
+ || base == functions::vmlaq
+ || base == functions::vmlasq
+ || base == functions::vmovnbq
+ || base == functions::vmovntq
+ || base == functions::vqmovnbq
+ || base == functions::vqmovntq
+ || base == functions::vqmovunbq
+ || base == functions::vqmovuntq
|| (base == functions::vorrq && mode_suffix_id == MODE_n)
+ || base == functions::vqdmladhq
+ || base == functions::vqdmladhxq
+ || base == functions::vqdmlahq
+ || base == functions::vqdmlashq
+ || base == functions::vqdmlsdhq
+ || base == functions::vqdmlsdhxq
+ || base == functions::vqrdmladhq
+ || base == functions::vqrdmladhxq
+ || base == functions::vqrdmlahq
+ || base == functions::vqrdmlashq
+ || base == functions::vqrdmlsdhq
+ || base == functions::vqrdmlsdhxq
|| (base == functions::vqrshlq && mode_suffix_id == MODE_n)
|| base == functions::vqrshrnbq
|| base == functions::vqrshrntq
|| base == functions::vrshrnbq
|| base == functions::vrshrntq
|| base == functions::vshrnbq
- || base == functions::vshrntq)
+ || base == functions::vshrntq
+ || base == functions::vsliq
+ || base == functions::vsriq)
return false;
return true;
? integer_zero_node
: simulate_builtin_function_decl (input_location, name, fntype,
code, NULL, attrs);
-
registered_function &rfn = *ggc_alloc <registered_function> ();
rfn.instance = instance;
rfn.decl = decl;
gcc_assert (!*rfn_slot);
*rfn_slot = &rfn;
- /* Also add the non-prefixed non-overloaded function, if the user namespace
- does not need to be preserved. */
- if (!preserve_user_namespace)
- {
- char *noprefix_name = get_name (instance, false, false);
- tree attrs = get_attributes (instance);
- add_function (instance, noprefix_name, fntype, attrs, requires_float,
- false, false);
- }
+ /* Also add the non-prefixed non-overloaded function, as placeholder
+ if the user namespace does not need to be preserved. */
+ char *noprefix_name = get_name (instance, false, false);
+ attrs = get_attributes (instance);
+ add_function (instance, noprefix_name, fntype, attrs, requires_float,
+ false, preserve_user_namespace);
/* Also add the function under its overloaded alias, if we want
a separate decl for each instance of an overloaded function. */
if (strcmp (name, overload_name) != 0)
{
/* Attribute lists shouldn't be shared. */
- tree attrs = get_attributes (instance);
+ attrs = get_attributes (instance);
bool placeholder_p = !(m_direct_overloads || force_direct_overloads);
add_function (instance, overload_name, fntype, attrs,
requires_float, false, placeholder_p);
- /* Also add the non-prefixed overloaded function, if the user namespace
- does not need to be preserved. */
- if (!preserve_user_namespace)
- {
- char *noprefix_overload_name = get_name (instance, false, true);
- tree attrs = get_attributes (instance);
- add_function (instance, noprefix_overload_name, fntype, attrs,
- requires_float, false, placeholder_p);
- }
+ /* Also add the non-prefixed overloaded function, as placeholder
+ if the user namespace does not need to be preserved. */
+ char *noprefix_overload_name = get_name (instance, false, true);
+ attrs = get_attributes (instance);
+ add_function (instance, noprefix_overload_name, fntype, attrs,
+ requires_float, false, preserve_user_namespace || placeholder_p);
}
obstack_free (&m_string_obstack, name);
= add_function (instance, name, m_overload_type, NULL_TREE,
requires_float, true, m_direct_overloads);
m_overload_names.put (name, &rfn);
- if (!preserve_user_namespace)
- {
- char *noprefix_name = get_name (instance, false, true);
- registered_function &noprefix_rfn
- = add_function (instance, noprefix_name, m_overload_type,
- NULL_TREE, requires_float, true,
- m_direct_overloads);
- m_overload_names.put (noprefix_name, &noprefix_rfn);
- }
+
+ /* Also add the non-prefixed function, as placeholder if the
+ user namespace does not need to be preserved. */
+ char *noprefix_name = get_name (instance, false, true);
+ registered_function &noprefix_rfn
+ = add_function (instance, noprefix_name, m_overload_type,
+ NULL_TREE, requires_float, true,
+ preserve_user_namespace || m_direct_overloads);
+ m_overload_names.put (noprefix_name, &noprefix_rfn);
}
}
return res;
}
+/* Require argument ARGNO to be a pointer to a scalar type that has a
+ corresponding type suffix. Return that type suffix on success,
+ otherwise report an error and return NUM_TYPE_SUFFIXES. */
+type_suffix_index
+function_resolver::infer_pointer_type (unsigned int argno)
+{
+ tree actual = get_argument_type (argno);
+ if (actual == error_mark_node)
+ return NUM_TYPE_SUFFIXES;
+
+ if (TREE_CODE (actual) != POINTER_TYPE)
+ {
+ error_at (location, "passing %qT to argument %d of %qE, which"
+ " expects a pointer type", actual, argno + 1, fndecl);
+ return NUM_TYPE_SUFFIXES;
+ }
+
+ tree target = TREE_TYPE (actual);
+ type_suffix_index type = find_type_suffix_for_scalar_type (target);
+ if (type == NUM_TYPE_SUFFIXES)
+ {
+ error_at (location, "passing %qT to argument %d of %qE, but %qT is not"
+ " a valid MVE element type", actual, argno + 1, fndecl,
+ build_qualified_type (target, 0));
+ return NUM_TYPE_SUFFIXES;
+ }
+
+ return type;
+}
+
/* Require argument ARGNO to be a single vector or a tuple of NUM_VECTORS
vectors; NUM_VECTORS is 1 for the former. Return the associated type
suffix on success, using TYPE_SUFFIX_b for predicates. Report an error
return true;
}
+/* Require argument ARGNO to be some form of pointer, without being specific
+ about its target type. Return true if the argument has the right form,
+ otherwise report an appropriate error. */
+bool
+function_resolver::require_pointer_type (unsigned int argno)
+{
+ if (!scalar_argument_p (argno))
+ {
+ error_at (location, "passing %qT to argument %d of %qE, which"
+ " expects a scalar pointer", get_argument_type (argno),
+ argno + 1, fndecl);
+ return false;
+ }
+ return true;
+}
+
/* Require the function to have exactly EXPECTED arguments. Return true
if it does, otherwise report an appropriate error. */
bool
return ::direct_optab_handler (op, vector_mode (suffix_i));
}
+/* Return the base address for a contiguous load or store
+ function. */
+rtx
+function_expander::get_contiguous_base ()
+{
+ return args[0];
+}
+
/* For a function that does the equivalent of:
OUTPUT = COND ? FN (INPUTS) : FALLBACK;
create_integer_operand (&m_ops.last (), x);
}
+/* Add a memory operand with mode MODE and address ADDR. */
+void
+function_expander::add_mem_operand (machine_mode mode, rtx addr)
+{
+ gcc_assert (VECTOR_MODE_P (mode));
+ rtx mem = gen_rtx_MEM (mode, memory_address (mode, addr));
+ /* The memory is only guaranteed to be element-aligned. */
+ set_mem_align (mem, GET_MODE_ALIGNMENT (GET_MODE_INNER (mode)));
+ add_fixed_operand (mem);
+}
+
+/* Add an operand that must be X. The only way of legitimizing an
+ invalid X is to reload the address of a MEM. */
+void
+function_expander::add_fixed_operand (rtx x)
+{
+ m_ops.safe_grow (m_ops.length () + 1, true);
+ create_fixed_operand (&m_ops.last (), x);
+}
+
/* Generate instruction ICODE, given that its operands have already
been added to M_OPS. Return the value of the first operand. */
rtx
return generate_insn (icode);
}
+/* Implement the call using instruction ICODE, which loads memory operand 1
+ into register operand 0. */
+rtx
+function_expander::use_contiguous_load_insn (insn_code icode)
+{
+ machine_mode mem_mode = memory_vector_mode ();
+
+ add_output_operand (icode);
+ add_mem_operand (mem_mode, get_contiguous_base ());
+ return generate_insn (icode);
+}
+
+/* Implement the call using instruction ICODE, which stores register operand 1
+ into memory operand 0. */
+rtx
+function_expander::use_contiguous_store_insn (insn_code icode)
+{
+ machine_mode mem_mode = memory_vector_mode ();
+
+ add_mem_operand (mem_mode, get_contiguous_base ());
+ add_input_operand (icode, args[1]);
+ return generate_insn (icode);
+}
+
/* Implement the call using a normal unpredicated optab for PRED_none.
<optab> corresponds to: