blob: 43150aa47a4797d111faa86bf1a6db27931aaf5f [file] [log] [blame]
/* Builtins implementation for RISC-V 'V' Extension for GNU compiler.
Copyright (C) 2022-2022 Free Software Foundation, Inc.
Contributed by Ju-Zhe Zhong (juzhe.zhong@rivai.ai), RiVAI Technologies Ltd.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#define IN_TARGET_CODE 1
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "memmodel.h"
#include "insn-codes.h"
#include "optabs.h"
#include "recog.h"
#include "diagnostic.h"
#include "expr.h"
#include "function.h"
#include "fold-const.h"
#include "gimplify.h"
#include "explow.h"
#include "stor-layout.h"
#include "alias.h"
#include "langhooks.h"
#include "stringpool.h"
#include "attribs.h"
#include "targhooks.h"
#include "regs.h"
#include "riscv-vector-builtins.h"
#include "riscv-vector-builtins-shapes.h"
#include "riscv-vector-builtins-bases.h"
using namespace riscv_vector;
namespace riscv_vector {
/* Static information about each vector type. */
struct vector_type_info
{
/* The name of the type as declared by riscv_vector.h
which is recommend to use. For example: 'vint32m1_t'. */
const char *name;
/* ABI name of vector type. The type is always available
under this name, even when riscv_vector.h isn't included.
For example: '__rvv_int32m1_t'. */
const char *abi_name;
/* The C++ mangling of ABI_NAME. */
const char *mangled_name;
};
/* Describes a function decl. */
class GTY (()) registered_function
{
public:
function_instance GTY ((skip)) instance;
/* The decl itself. */
tree GTY ((skip)) decl;
};
/* Hash traits for registered_function. */
struct registered_function_hasher : nofree_ptr_hash<registered_function>
{
typedef function_instance compare_type;
static hashval_t hash (value_type);
static bool equal (value_type, const compare_type &);
};
/* Static information about each RVV type. */
static CONSTEXPR const vector_type_info vector_types[] = {
#define DEF_RVV_TYPE(NAME, NCHARS, ABI_NAME, ARGS...) \
{#NAME, #ABI_NAME, "u" #NCHARS #ABI_NAME},
#include "riscv-vector-builtins.def"
};
/* Static information about operand suffix for each RVV type. */
const char *const operand_suffixes[NUM_OP_TYPES] = {
"", /* OP_TYPE_none. */
#define DEF_RVV_OP_TYPE(NAME) "_" # NAME,
#include "riscv-vector-builtins.def"
};
/* Static information about type suffix for each RVV type. */
const rvv_builtin_suffixes type_suffixes[NUM_VECTOR_TYPES + 1] = {
#define DEF_RVV_TYPE(NAME, NCHARS, ABI_NAME, SCALAR_TYPE, VECTOR_MODE, \
VECTOR_MODE_MIN_VLEN_32, VECTOR_SUFFIX, SCALAR_SUFFIX, \
VSETVL_SUFFIX) \
{#VECTOR_SUFFIX, #SCALAR_SUFFIX, #VSETVL_SUFFIX},
#include "riscv-vector-builtins.def"
};
/* Static information about predication suffix for each RVV type. */
const char *const predication_suffixes[NUM_PRED_TYPES] = {
"", /* PRED_TYPE_none. */
#define DEF_RVV_PRED_TYPE(NAME) "_" # NAME,
#include "riscv-vector-builtins.def"
};
/* A list of all signed integer will be registered for intrinsic functions. */
static const rvv_type_info i_ops[] = {
#define DEF_RVV_I_OPS(TYPE, REQUIRE) {VECTOR_TYPE_##TYPE, REQUIRE},
#include "riscv-vector-builtins-types.def"
{NUM_VECTOR_TYPES, 0}};
static CONSTEXPR const rvv_arg_type_info rvv_arg_type_info_end
= rvv_arg_type_info (NUM_BASE_TYPES);
/* A list of args for size_t func (void) function. */
static CONSTEXPR const rvv_arg_type_info void_args[]
= {rvv_arg_type_info (RVV_BASE_void), rvv_arg_type_info_end};
/* A list of args for size_t func (size_t) function. */
static CONSTEXPR const rvv_arg_type_info size_args[]
= {rvv_arg_type_info (RVV_BASE_size), rvv_arg_type_info_end};
/* A list of none preds that will be registered for intrinsic functions. */
static CONSTEXPR const predication_type_index none_preds[]
= {PRED_TYPE_none, NUM_PRED_TYPES};
/* A static operand information for size_t func (void) function registration. */
static CONSTEXPR const rvv_op_info i_none_size_void_ops
= {i_ops, /* Types */
OP_TYPE_none, /* Suffix */
rvv_arg_type_info (RVV_BASE_size), /* Return type */
void_args /* Args */};
/* A static operand information for size_t func (size_t) function registration.
*/
static CONSTEXPR const rvv_op_info i_none_size_size_ops
= {i_ops, /* Types */
OP_TYPE_none, /* Suffix */
rvv_arg_type_info (RVV_BASE_size), /* Return type */
size_args /* Args */};
/* A list of all RVV intrinsic functions. */
static function_group_info function_groups[] = {
#define DEF_RVV_FUNCTION(NAME, SHAPE, PREDS, OPS_INFO) \
{#NAME, &bases::NAME, &shapes::SHAPE, PREDS, OPS_INFO},
#include "riscv-vector-builtins-functions.def"
};
/* The RVV types, with their built-in
"__rvv..._t" name. Allow an index of NUM_VECTOR_TYPES, which always
yields a null tree. */
static GTY (()) tree abi_vector_types[NUM_VECTOR_TYPES + 1];
/* Same, but with the riscv_vector.h "v..._t" name. */
extern GTY (()) rvv_builtin_types_t builtin_types[NUM_VECTOR_TYPES + 1];
rvv_builtin_types_t builtin_types[NUM_VECTOR_TYPES + 1];
/* The list of all registered function decls, indexed by code. */
static GTY (()) vec<registered_function *, va_gc> *registered_functions;
/* All registered function decls, hashed on the function_instance
that they implement. This is used for looking up implementations of
overloaded functions. */
static hash_table<registered_function_hasher> *function_table;
/* RAII class for enabling enough RVV features to define the built-in
types and implement the riscv_vector.h pragma.
Note: According to 'TYPE_MODE' macro implementation, we need set
have_regs_of_mode[mode] to be true if we want to get the exact mode
from 'TYPE_MODE'. However, have_regs_of_mode has not been set yet in
targetm.init_builtins (). We need rvv_switcher to set have_regs_of_mode
before targetm.init_builtins () and recover back have_regs_of_mode
after targetm.init_builtins (). */
class rvv_switcher
{
public:
rvv_switcher ();
~rvv_switcher ();
private:
bool m_old_have_regs_of_mode[MAX_MACHINE_MODE];
};
rvv_switcher::rvv_switcher ()
{
/* Set have_regs_of_mode before targetm.init_builtins (). */
memcpy (m_old_have_regs_of_mode, have_regs_of_mode,
sizeof (have_regs_of_mode));
for (int i = 0; i < NUM_MACHINE_MODES; ++i)
if (riscv_v_ext_vector_mode_p ((machine_mode) i))
have_regs_of_mode[i] = true;
}
rvv_switcher::~rvv_switcher ()
{
/* Recover back have_regs_of_mode. */
memcpy (have_regs_of_mode, m_old_have_regs_of_mode,
sizeof (have_regs_of_mode));
}
/* Add attribute NAME to ATTRS. */
static tree
add_attribute (const char *name, tree attrs)
{
return tree_cons (get_identifier (name), NULL_TREE, attrs);
}
/* Add type attributes to builtin type tree, currently only the mangled name. */
static void
add_vector_type_attribute (tree type, const char *mangled_name)
{
tree mangled_name_tree = get_identifier (mangled_name);
tree value = tree_cons (NULL_TREE, mangled_name_tree, NULL_TREE);
TYPE_ATTRIBUTES (type)
= tree_cons (get_identifier ("RVV type"), value, TYPE_ATTRIBUTES (type));
}
/* Force TYPE to be a sizeless type. */
static void
make_type_sizeless (tree type)
{
TYPE_ATTRIBUTES (type) = tree_cons (get_identifier ("RVV sizeless type"),
NULL_TREE, TYPE_ATTRIBUTES (type));
}
/* Return true if TYPE is a sizeless type. */
static bool
sizeless_type_p (const_tree type)
{
if (type == error_mark_node)
return NULL_TREE;
return lookup_attribute ("RVV sizeless type", TYPE_ATTRIBUTES (type));
}
/* If TYPE is an ABI-defined RVV type, return its attribute descriptor,
otherwise return null. */
static tree
lookup_vector_type_attribute (const_tree type)
{
if (type == error_mark_node)
return NULL_TREE;
return lookup_attribute ("RVV type", TYPE_ATTRIBUTES (type));
}
/* Return a representation of "const T *". */
static tree
build_const_pointer (tree t)
{
return build_pointer_type (build_qualified_type (t, TYPE_QUAL_CONST));
}
/* Helper function for register a single built-in RVV ABI type. */
static void
register_builtin_type (vector_type_index type, tree eltype, machine_mode mode)
{
builtin_types[type].scalar = eltype;
builtin_types[type].scalar_ptr = build_pointer_type (eltype);
builtin_types[type].scalar_const_ptr = build_const_pointer (eltype);
if (!riscv_v_ext_vector_mode_p (mode))
return;
tree vectype = build_vector_type_for_mode (eltype, mode);
gcc_assert (VECTOR_MODE_P (TYPE_MODE (vectype)) && TYPE_MODE (vectype) == mode
&& TYPE_MODE_RAW (vectype) == mode && TYPE_ALIGN (vectype) <= 128
&& known_eq (tree_to_poly_uint64 (TYPE_SIZE (vectype)),
GET_MODE_BITSIZE (mode)));
vectype = build_distinct_type_copy (vectype);
gcc_assert (vectype == TYPE_MAIN_VARIANT (vectype));
SET_TYPE_STRUCTURAL_EQUALITY (vectype);
TYPE_ARTIFICIAL (vectype) = 1;
TYPE_INDIVISIBLE_P (vectype) = 1;
add_vector_type_attribute (vectype, vector_types[type].mangled_name);
make_type_sizeless (vectype);
abi_vector_types[type] = vectype;
lang_hooks.types.register_builtin_type (vectype, vector_types[type].abi_name);
}
/* Register the built-in RVV ABI types, such as __rvv_int32m1_t. */
static void
register_builtin_types ()
{
/* int32_t/uint32_t defined as `long`/`unsigned long` in RV32,
but intSI_type_node/unsigned_intSI_type_node is
`int` and `unsigned int`, so use long_integer_type_node and
long_unsigned_type_node here for type consistent. */
tree int32_type_node
= TARGET_64BIT ? intSI_type_node : long_integer_type_node;
tree unsigned_int32_type_node
= TARGET_64BIT ? unsigned_intSI_type_node : long_unsigned_type_node;
machine_mode mode;
#define DEF_RVV_TYPE(NAME, NCHARS, ABI_NAME, SCALAR_TYPE, VECTOR_MODE, \
VECTOR_MODE_MIN_VLEN_32, ARGS...) \
mode = TARGET_MIN_VLEN > 32 ? VECTOR_MODE##mode \
: VECTOR_MODE_MIN_VLEN_32##mode; \
register_builtin_type (VECTOR_TYPE_##NAME, SCALAR_TYPE##_type_node, mode);
#include "riscv-vector-builtins.def"
}
/* Register vector type TYPE under its risv_vector.h name. */
static void
register_vector_type (vector_type_index type)
{
tree vectype = abi_vector_types[type];
/* When vectype is NULL, the corresponding builtin type
is disabled according to '-march'. */
if (!vectype)
return;
tree id = get_identifier (vector_types[type].name);
tree decl = build_decl (input_location, TYPE_DECL, id, vectype);
decl = lang_hooks.decls.pushdecl (decl);
/* Record the new RVV type if pushdecl succeeded without error. Use
the ABI type otherwise, so that the type we record at least has the
right form, even if it doesn't have the right name. This should give
better error recovery behavior than installing error_mark_node or
installing an incorrect type. */
if (decl && TREE_CODE (decl) == TYPE_DECL
&& TREE_TYPE (decl) != error_mark_node
&& TYPE_MAIN_VARIANT (TREE_TYPE (decl)) == vectype)
vectype = TREE_TYPE (decl);
builtin_types[type].vector = vectype;
builtin_types[type].vector_ptr = build_pointer_type (vectype);
}
/* Check whether all the RVV_REQUIRE_* values in REQUIRED_EXTENSIONS are
enabled. */
static bool
check_required_extensions (uint64_t required_extensions)
{
uint64_t riscv_isa_flags = 0;
if (TARGET_VECTOR_ELEN_FP_32)
riscv_isa_flags |= RVV_REQUIRE_ELEN_FP_32;
if (TARGET_VECTOR_ELEN_FP_64)
riscv_isa_flags |= RVV_REQUIRE_ELEN_FP_64;
if (TARGET_MIN_VLEN > 32)
riscv_isa_flags |= RVV_REQUIRE_ZVE64;
if (TARGET_64BIT)
riscv_isa_flags |= RVV_REQUIRE_RV64BIT;
uint64_t missing_extensions = required_extensions & ~riscv_isa_flags;
if (missing_extensions != 0)
return false;
return true;
}
tree
rvv_arg_type_info::get_tree_type (vector_type_index type_idx) const
{
switch (base_type)
{
case RVV_BASE_vector:
return builtin_types[type_idx].vector;
case RVV_BASE_scalar:
return builtin_types[type_idx].scalar;
case RVV_BASE_vector_ptr:
return builtin_types[type_idx].vector_ptr;
case RVV_BASE_scalar_ptr:
return builtin_types[type_idx].scalar_ptr;
case RVV_BASE_scalar_const_ptr:
return builtin_types[type_idx].scalar_const_ptr;
case RVV_BASE_void:
return void_type_node;
case RVV_BASE_size:
return size_type_node;
case RVV_BASE_ptrdiff:
return ptrdiff_type_node;
case RVV_BASE_unsigned_long:
return long_unsigned_type_node;
case RVV_BASE_long:
return long_integer_type_node;
default:
gcc_unreachable ();
}
}
function_instance::function_instance (const char *base_name_in,
const function_base *base_in,
const function_shape *shape_in,
rvv_type_info type_in,
predication_type_index pred_in,
const rvv_op_info *op_info_in)
: base_name (base_name_in), base (base_in), shape (shape_in), type (type_in),
pred (pred_in), op_info (op_info_in)
{
}
bool
function_instance::operator== (const function_instance &other) const
{
for (unsigned int i = 0; op_info->args[i].base_type != NUM_BASE_TYPES; ++i)
if (op_info->args[i].base_type != other.op_info->args[i].base_type)
return false;
return (base == other.base && shape == other.shape
&& type.index == other.type.index && op_info->op == other.op_info->op
&& pred == other.pred
&& op_info->ret.base_type == other.op_info->ret.base_type);
}
bool
function_instance::any_type_float_p () const
{
if (FLOAT_MODE_P (TYPE_MODE (get_return_type ())))
return true;
for (int i = 0; op_info->args[i].base_type != NUM_BASE_TYPES; ++i)
if (FLOAT_MODE_P (TYPE_MODE (get_arg_type (i))))
return true;
return false;
}
tree
function_instance::get_return_type () const
{
return op_info->ret.get_tree_type (type.index);
}
tree
function_instance::get_arg_type (unsigned opno) const
{
return op_info->args[opno].get_tree_type (type.index);
}
/* Return a hash code for a function_instance. */
hashval_t
function_instance::hash () const
{
inchash::hash h;
/* BASE uniquely determines BASE_NAME, so we don't need to hash both. */
h.add_ptr (base);
h.add_ptr (shape);
h.add_int (type.index);
h.add_int (op_info->op);
h.add_int (pred);
h.add_int (op_info->ret.base_type);
for (unsigned int i = 0; op_info->args[i].base_type != NUM_BASE_TYPES; ++i)
h.add_int (op_info->args[i].base_type);
return h.end ();
}
/* Return a set of CP_* flags that describe what the function could do,
taking the command-line flags into account. */
unsigned int
function_instance::call_properties () const
{
unsigned int flags = base->call_properties (*this);
/* -fno-trapping-math means that we can assume any FP exceptions
are not user-visible. */
if (!flag_trapping_math)
flags &= ~CP_RAISE_FP_EXCEPTIONS;
return flags;
}
/* Return true if calls to the function could read some form of
global state. */
bool
function_instance::reads_global_state_p () const
{
unsigned int flags = call_properties ();
/* Preserve any dependence on rounding mode, flush to zero mode, etc.
There is currently no way of turning this off; in particular,
-fno-rounding-math (which is the default) means that we should make
the usual assumptions about rounding mode, which for intrinsics means
acting as the instructions do. */
if (flags & CP_READ_FPCR)
return true;
/* Handle direct reads of global state. */
return flags & (CP_READ_MEMORY | CP_READ_CSR);
}
/* Return true if calls to the function could modify some form of
global state. */
bool
function_instance::modifies_global_state_p () const
{
unsigned int flags = call_properties ();
/* Preserve any exception state written back to the FPCR,
unless -fno-trapping-math says this is unnecessary. */
if (flags & CP_RAISE_FP_EXCEPTIONS)
return true;
/* Handle direct modifications of global state. */
return flags & (CP_WRITE_MEMORY | CP_WRITE_CSR);
}
/* Return true if calls to the function could raise a signal. */
bool
function_instance::could_trap_p () const
{
unsigned int flags = call_properties ();
/* Handle functions that could raise SIGFPE. */
if (flags & CP_RAISE_FP_EXCEPTIONS)
return true;
/* Handle functions that could raise SIGBUS or SIGSEGV. */
if (flags & (CP_READ_MEMORY | CP_WRITE_MEMORY))
return true;
return false;
}
function_builder::function_builder ()
{
m_direct_overloads = lang_GNU_CXX ();
gcc_obstack_init (&m_string_obstack);
}
function_builder::~function_builder ()
{
obstack_free (&m_string_obstack, NULL);
}
/* Allocate arguments of the function. */
void
function_builder::allocate_argument_types (const function_instance &instance,
vec<tree> &argument_types) const
{
for (unsigned int i = 0;
instance.op_info->args[i].base_type != NUM_BASE_TYPES; ++i)
argument_types.quick_push (
instance.op_info->args[i].get_tree_type (instance.type.index));
}
/* Register all the functions in GROUP. */
void
function_builder::register_function_group (const function_group_info &group)
{
(*group.shape)->build (*this, group);
}
/* Add NAME to the end of the function name being built. */
void
function_builder::append_name (const char *name)
{
obstack_grow (&m_string_obstack, name, strlen (name));
}
/* Zero-terminate and complete the function name being built. */
char *
function_builder::finish_name ()
{
obstack_1grow (&m_string_obstack, 0);
return (char *) obstack_finish (&m_string_obstack);
}
/* Return the appropriate function attributes for INSTANCE. */
tree
function_builder::get_attributes (const function_instance &instance)
{
tree attrs = NULL_TREE;
if (!instance.modifies_global_state_p ())
{
if (instance.reads_global_state_p ())
attrs = add_attribute ("pure", attrs);
else
attrs = add_attribute ("const", attrs);
}
if (!flag_non_call_exceptions || !instance.could_trap_p ())
attrs = add_attribute ("nothrow", attrs);
return add_attribute ("leaf", attrs);
}
/* Add a function called NAME with type FNTYPE and attributes ATTRS.
INSTANCE describes what the function does. */
registered_function &
function_builder::add_function (const function_instance &instance,
const char *name, tree fntype, tree attrs,
bool placeholder_p)
{
unsigned int code = vec_safe_length (registered_functions);
code = (code << RISCV_BUILTIN_SHIFT) + RISCV_BUILTIN_VECTOR;
/* We need to be able to generate placeholders to enusre that we have a
consistent numbering scheme for function codes between the C and C++
frontends, so that everything ties up in LTO.
Currently, tree-streamer-in.c:unpack_ts_function_decl_value_fields
validates that tree nodes returned by TARGET_BUILTIN_DECL are non-NULL and
some node other than error_mark_node. This is a holdover from when builtin
decls were streamed by code rather than by value.
Ultimately, we should be able to remove this validation of BUILT_IN_MD
nodes and remove the target hook. For now, however, we need to appease the
validation and return a non-NULL, non-error_mark_node node, so we
arbitrarily choose integer_zero_node. */
tree decl = placeholder_p
? 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;
vec_safe_push (registered_functions, &rfn);
return rfn;
}
/* Add a built-in function for INSTANCE, with the argument types given
by ARGUMENT_TYPES and the return type given by RETURN_TYPE. NAME is
the "full" name for C function. OVERLOAD_NAME is the "short" name for
C++ overloaded function. OVERLOAD_NAME can be nullptr because some
instance doesn't have C++ overloaded function. */
void
function_builder::add_unique_function (const function_instance &instance,
const function_shape *shape,
tree return_type,
vec<tree> &argument_types)
{
/* Do not add this function if it is invalid. */
if (!check_required_extensions (instance.type.required_extensions))
return;
/* Add the function under its full (unique) name. */
char *name = shape->get_name (*this, instance, false);
tree fntype
= build_function_type_array (return_type, argument_types.length (),
argument_types.address ());
tree attrs = get_attributes (instance);
registered_function &rfn
= add_function (instance, name, fntype, attrs, false);
/* Enter the function into the hash table. */
hashval_t hash = instance.hash ();
registered_function **rfn_slot
= function_table->find_slot_with_hash (instance, hash, INSERT);
gcc_assert (!*rfn_slot);
*rfn_slot = &rfn;
/* Also add the function under its overloaded alias, if we want
a separate decl for each instance of an overloaded function. */
char *overload_name = shape->get_name (*this, instance, true);
if (overload_name)
{
/* Attribute lists shouldn't be shared. */
tree attrs = get_attributes (instance);
bool placeholder_p = !m_direct_overloads;
add_function (instance, overload_name, fntype, attrs, placeholder_p);
}
obstack_free (&m_string_obstack, name);
}
function_call_info::function_call_info (location_t location_in,
const function_instance &instance_in,
tree fndecl_in)
: function_instance (instance_in), location (location_in), fndecl (fndecl_in)
{}
function_expander::function_expander (const function_instance &instance,
tree fndecl_in, tree exp_in,
rtx target_in)
: function_call_info (EXPR_LOCATION (exp_in), instance, fndecl_in),
exp (exp_in), target (target_in), opno (0)
{
if (!function_returns_void_p ())
create_output_operand (&m_ops[opno++], target, TYPE_MODE (TREE_TYPE (exp)));
}
/* Take argument ARGNO from EXP's argument list and convert it into
an expand operand. Store the operand in *M_OPS. */
void
function_expander::add_input_operand (unsigned argno)
{
tree arg = CALL_EXPR_ARG (exp, argno);
rtx x = expand_normal (arg);
add_input_operand (TYPE_MODE (TREE_TYPE (arg)), x);
}
/* Generate instruction ICODE, given that its operands have already
been added to M_OPS. Return the value of the first operand. */
rtx
function_expander::generate_insn (insn_code icode)
{
gcc_assert (opno == insn_data[icode].n_generator_args);
if (!maybe_expand_insn (icode, opno, m_ops))
{
error ("invalid argument to built-in function");
return NULL_RTX;
}
return function_returns_void_p () ? const0_rtx : m_ops[0].value;
}
inline hashval_t
registered_function_hasher::hash (value_type value)
{
return value->instance.hash ();
}
inline bool
registered_function_hasher::equal (value_type value, const compare_type &key)
{
return value->instance == key;
}
/* If TYPE is a built-in type defined by the RVV ABI, return the mangled name,
otherwise return NULL. */
const char *
mangle_builtin_type (const_tree type)
{
if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL)
type = TREE_TYPE (TYPE_NAME (type));
if (tree attr = lookup_vector_type_attribute (type))
if (tree id = TREE_VALUE (chain_index (0, TREE_VALUE (attr))))
return IDENTIFIER_POINTER (id);
return NULL;
}
/* Initialize all compiler built-ins related to RVV that should be
defined at start-up. */
void
init_builtins ()
{
rvv_switcher rvv;
if (!TARGET_VECTOR)
return;
register_builtin_types ();
if (in_lto_p)
handle_pragma_vector ();
}
/* Implement TARGET_VERIFY_TYPE_CONTEXT for RVV types. */
bool
verify_type_context (location_t loc, type_context_kind context, const_tree type,
bool silent_p)
{
if (!sizeless_type_p (type))
return true;
switch (context)
{
case TCTX_SIZEOF:
case TCTX_STATIC_STORAGE:
if (!silent_p)
error_at (loc, "RVV type %qT does not have a fixed size", type);
return false;
case TCTX_ALIGNOF:
if (!silent_p)
error_at (loc, "RVV type %qT does not have a defined alignment", type);
return false;
case TCTX_THREAD_STORAGE:
if (!silent_p)
error_at (loc,
"variables of type %qT cannot have thread-local"
" storage duration",
type);
return false;
case TCTX_POINTER_ARITH:
if (!silent_p)
error_at (loc, "arithmetic on pointer to RVV type %qT", type);
return false;
case TCTX_FIELD:
if (silent_p)
;
else if (lang_GNU_CXX ())
error_at (loc, "member variables cannot have RVV type %qT", type);
else
error_at (loc, "fields cannot have RVV type %qT", type);
return false;
case TCTX_ARRAY_ELEMENT:
if (!silent_p)
error_at (loc, "array elements cannot have RVV type %qT", type);
return false;
case TCTX_ALLOCATION:
if (!silent_p)
error_at (loc, "cannot allocate objects with RVV type %qT", type);
return false;
case TCTX_DEALLOCATION:
if (!silent_p)
error_at (loc, "cannot delete objects with RVV type %qT", type);
return false;
case TCTX_EXCEPTIONS:
if (!silent_p)
error_at (loc, "cannot throw or catch RVV type %qT", type);
return false;
case TCTX_CAPTURE_BY_COPY:
if (!silent_p)
error_at (loc, "capture by copy of RVV type %qT", type);
return false;
}
gcc_unreachable ();
}
/* Implement #pragma riscv intrinsic vector. */
void
handle_pragma_vector ()
{
if (function_table)
{
error ("duplicate definition of %qs", "riscv_vector.h");
return;
}
rvv_switcher rvv;
/* Define the vector and tuple types. */
for (unsigned int type_i = 0; type_i < NUM_VECTOR_TYPES; ++type_i)
register_vector_type ((enum vector_type_index) type_i);
/* Define the functions. */
function_table = new hash_table<registered_function_hasher> (1023);
function_builder builder;
for (unsigned int i = 0; i < ARRAY_SIZE (function_groups); ++i)
builder.register_function_group (function_groups[i]);
}
/* Return the function decl with RVV function subcode CODE, or error_mark_node
if no such function exists. */
tree
builtin_decl (unsigned int code, bool)
{
if (code >= vec_safe_length (registered_functions))
return error_mark_node;
return (*registered_functions)[code]->decl;
}
/* Expand a call to the RVV function with subcode CODE. EXP is the call
expression and TARGET is the preferred location for the result.
Return the value of the lhs. */
rtx
expand_builtin (unsigned int code, tree exp, rtx target)
{
registered_function &rfn = *(*registered_functions)[code];
return function_expander (rfn.instance, rfn.decl, exp, target).expand ();
}
} // end namespace riscv_vector
inline void
gt_ggc_mx (function_instance *)
{}
inline void
gt_pch_nx (function_instance *)
{}
inline void
gt_pch_nx (function_instance *, gt_pointer_operator, void *)
{}
#include "gt-riscv-vector-builtins.h"