blob: 09c60fad9e70c7743852f593d4e0ebcbdb26d2b6 [file] [log] [blame]
/* ACLE support for AArch64 SVE (function_base classes)
Copyright (C) 2018-2021 Free Software Foundation, Inc.
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/>. */
#ifndef GCC_AARCH64_SVE_BUILTINS_FUNCTIONS_H
#define GCC_AARCH64_SVE_BUILTINS_FUNCTIONS_H
namespace aarch64_sve {
/* Wrap T, which is derived from function_base, and indicate that the
function never has side effects. It is only necessary to use this
wrapper on functions that might have floating-point suffixes, since
otherwise we assume by default that the function has no side effects. */
template<typename T>
class quiet : public T
{
public:
CONSTEXPR quiet () : T () {}
/* Unfortunately we can't use parameter packs yet. */
template<typename T1>
CONSTEXPR quiet (const T1 &t1) : T (t1) {}
template<typename T1, typename T2>
CONSTEXPR quiet (const T1 &t1, const T2 &t2) : T (t1, t2) {}
template<typename T1, typename T2, typename T3>
CONSTEXPR quiet (const T1 &t1, const T2 &t2, const T3 &t3)
: T (t1, t2, t3) {}
unsigned int
call_properties (const function_instance &) const OVERRIDE
{
return 0;
}
};
/* A function_base that sometimes or always operates on tuples of
vectors. */
class multi_vector_function : public function_base
{
public:
CONSTEXPR multi_vector_function (unsigned int vectors_per_tuple)
: m_vectors_per_tuple (vectors_per_tuple) {}
unsigned int
vectors_per_tuple () const OVERRIDE
{
return m_vectors_per_tuple;
}
/* The number of vectors in a tuple, or 1 if the function only operates
on single vectors. */
unsigned int m_vectors_per_tuple;
};
/* A function_base that loads or stores contiguous memory elements
without extending or truncating them. */
class full_width_access : public multi_vector_function
{
public:
CONSTEXPR full_width_access (unsigned int vectors_per_tuple = 1)
: multi_vector_function (vectors_per_tuple) {}
tree
memory_scalar_type (const function_instance &fi) const OVERRIDE
{
return fi.scalar_type (0);
}
machine_mode
memory_vector_mode (const function_instance &fi) const OVERRIDE
{
machine_mode mode = fi.vector_mode (0);
if (m_vectors_per_tuple != 1)
mode = targetm.array_mode (mode, m_vectors_per_tuple).require ();
return mode;
}
};
/* A function_base that loads elements from memory and extends them
to a wider element. The memory element type is a fixed part of
the function base name. */
class extending_load : public function_base
{
public:
CONSTEXPR extending_load (type_suffix_index memory_type)
: m_memory_type (memory_type) {}
unsigned int
call_properties (const function_instance &) const OVERRIDE
{
return CP_READ_MEMORY;
}
tree
memory_scalar_type (const function_instance &) const OVERRIDE
{
return scalar_types[type_suffixes[m_memory_type].vector_type];
}
machine_mode
memory_vector_mode (const function_instance &fi) const OVERRIDE
{
machine_mode mem_mode = type_suffixes[m_memory_type].vector_mode;
machine_mode reg_mode = fi.vector_mode (0);
return aarch64_sve_data_mode (GET_MODE_INNER (mem_mode),
GET_MODE_NUNITS (reg_mode)).require ();
}
/* Return the rtx code associated with the kind of extension that
the load performs. */
rtx_code
extend_rtx_code () const
{
return (type_suffixes[m_memory_type].unsigned_p
? ZERO_EXTEND : SIGN_EXTEND);
}
/* The type of the memory elements. This is part of the function base
name rather than a true type suffix. */
type_suffix_index m_memory_type;
};
/* A function_base that truncates vector elements and stores them to memory.
The memory element width is a fixed part of the function base name. */
class truncating_store : public function_base
{
public:
CONSTEXPR truncating_store (scalar_int_mode to_mode) : m_to_mode (to_mode) {}
unsigned int
call_properties (const function_instance &) const OVERRIDE
{
return CP_WRITE_MEMORY;
}
tree
memory_scalar_type (const function_instance &fi) const OVERRIDE
{
/* In truncating stores, the signedness of the memory element is defined
to be the same as the signedness of the vector element. The signedness
doesn't make any difference to the behavior of the function. */
type_class_index tclass = fi.type_suffix (0).tclass;
unsigned int element_bits = GET_MODE_BITSIZE (m_to_mode);
type_suffix_index suffix = find_type_suffix (tclass, element_bits);
return scalar_types[type_suffixes[suffix].vector_type];
}
machine_mode
memory_vector_mode (const function_instance &fi) const OVERRIDE
{
poly_uint64 nunits = GET_MODE_NUNITS (fi.vector_mode (0));
return aarch64_sve_data_mode (m_to_mode, nunits).require ();
}
/* The mode of a single memory element. */
scalar_int_mode m_to_mode;
};
/* An incomplete function_base for functions that have an associated rtx code.
It simply records information about the mapping for derived classes
to use. */
class rtx_code_function_base : public function_base
{
public:
CONSTEXPR rtx_code_function_base (rtx_code code_for_sint,
rtx_code code_for_uint,
int unspec_for_fp = -1)
: m_code_for_sint (code_for_sint), m_code_for_uint (code_for_uint),
m_unspec_for_fp (unspec_for_fp) {}
/* The rtx code to use for signed and unsigned integers respectively.
Can be UNKNOWN for functions that don't have integer forms. */
rtx_code m_code_for_sint;
rtx_code m_code_for_uint;
/* The UNSPEC_COND_* to use for floating-point operations. Can be -1
for functions that only operate on integers. */
int m_unspec_for_fp;
};
/* A function_base for functions that have an associated rtx code.
It supports all forms of predication except PRED_implicit. */
class rtx_code_function : public rtx_code_function_base
{
public:
CONSTEXPR rtx_code_function (rtx_code code_for_sint, rtx_code code_for_uint,
int unspec_for_fp = -1)
: rtx_code_function_base (code_for_sint, code_for_uint, unspec_for_fp) {}
rtx
expand (function_expander &e) const OVERRIDE
{
return e.map_to_rtx_codes (m_code_for_sint, m_code_for_uint,
m_unspec_for_fp);
}
};
/* Like rtx_code_function, but for functions that take what is normally
the final argument first. One use of this class is to handle binary
reversed operations; another is to handle MLA-style operations that
are normally expressed in GCC as MAD-style operations. */
class rtx_code_function_rotated : public rtx_code_function_base
{
public:
CONSTEXPR rtx_code_function_rotated (rtx_code code_for_sint,
rtx_code code_for_uint,
int unspec_for_fp = -1)
: rtx_code_function_base (code_for_sint, code_for_uint, unspec_for_fp) {}
rtx
expand (function_expander &e) const OVERRIDE
{
/* Rotate the inputs into their normal order, but continue to make _m
functions merge with what was originally the first vector argument. */
unsigned int nargs = e.args.length ();
e.rotate_inputs_left (e.pred != PRED_none ? 1 : 0, nargs);
return e.map_to_rtx_codes (m_code_for_sint, m_code_for_uint,
m_unspec_for_fp, nargs - 1);
}
};
/* An incomplete function_base for functions that have an associated
unspec code, with separate codes for signed integers, unsigned
integers and floating-point values. The class simply records
information about the mapping for derived classes to use. */
class unspec_based_function_base : public function_base
{
public:
CONSTEXPR unspec_based_function_base (int unspec_for_sint,
int unspec_for_uint,
int unspec_for_fp)
: m_unspec_for_sint (unspec_for_sint),
m_unspec_for_uint (unspec_for_uint),
m_unspec_for_fp (unspec_for_fp)
{}
/* Return the unspec code to use for INSTANCE, based on type suffix 0. */
int
unspec_for (const function_instance &instance) const
{
return (!instance.type_suffix (0).integer_p ? m_unspec_for_fp
: instance.type_suffix (0).unsigned_p ? m_unspec_for_uint
: m_unspec_for_sint);
}
/* The unspec code associated with signed-integer, unsigned-integer
and floating-point operations respectively. */
int m_unspec_for_sint;
int m_unspec_for_uint;
int m_unspec_for_fp;
};
/* A function_base for functions that have an associated unspec code.
It supports all forms of predication except PRED_implicit. */
class unspec_based_function : public unspec_based_function_base
{
public:
CONSTEXPR unspec_based_function (int unspec_for_sint, int unspec_for_uint,
int unspec_for_fp)
: unspec_based_function_base (unspec_for_sint, unspec_for_uint,
unspec_for_fp)
{}
rtx
expand (function_expander &e) const OVERRIDE
{
return e.map_to_unspecs (m_unspec_for_sint, m_unspec_for_uint,
m_unspec_for_fp);
}
};
/* Like unspec_based_function, but for functions that take what is normally
the final argument first. One use of this class is to handle binary
reversed operations; another is to handle MLA-style operations that
are normally expressed in GCC as MAD-style operations. */
class unspec_based_function_rotated : public unspec_based_function_base
{
public:
CONSTEXPR unspec_based_function_rotated (int unspec_for_sint,
int unspec_for_uint,
int unspec_for_fp)
: unspec_based_function_base (unspec_for_sint, unspec_for_uint,
unspec_for_fp)
{}
rtx
expand (function_expander &e) const OVERRIDE
{
/* Rotate the inputs into their normal order, but continue to make _m
functions merge with what was originally the first vector argument. */
unsigned int nargs = e.args.length ();
e.rotate_inputs_left (e.pred != PRED_none ? 1 : 0, nargs);
return e.map_to_unspecs (m_unspec_for_sint, m_unspec_for_uint,
m_unspec_for_fp, nargs - 1);
}
};
/* Like unspec_based_function, but map the function directly to
CODE (UNSPEC, M) instead of using the generic predication-based
expansion. where M is the vector mode associated with type suffix 0.
This is useful if the unspec doesn't describe the full operation or
if the usual predication rules don't apply for some reason. */
template<insn_code (*CODE) (int, machine_mode)>
class unspec_based_function_exact_insn : public unspec_based_function_base
{
public:
CONSTEXPR unspec_based_function_exact_insn (int unspec_for_sint,
int unspec_for_uint,
int unspec_for_fp)
: unspec_based_function_base (unspec_for_sint, unspec_for_uint,
unspec_for_fp)
{}
rtx
expand (function_expander &e) const OVERRIDE
{
return e.use_exact_insn (CODE (unspec_for (e), e.vector_mode (0)));
}
};
/* A function that performs an unspec and then adds it to another value. */
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_add>
unspec_based_add_function;
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_add_lane>
unspec_based_add_lane_function;
/* Generic unspec-based _lane function. */
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_lane>
unspec_based_lane_function;
/* A functon that uses aarch64_pred* patterns regardless of the
predication type. */
typedef unspec_based_function_exact_insn<code_for_aarch64_pred>
unspec_based_pred_function;
/* Like unspec_based_add_function and unspec_based_add_lane_function,
but using saturating addition. */
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_qadd>
unspec_based_qadd_function;
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_qadd_lane>
unspec_based_qadd_lane_function;
/* Like unspec_based_sub_function and unspec_based_sub_lane_function,
but using saturating subtraction. */
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_qsub>
unspec_based_qsub_function;
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_qsub_lane>
unspec_based_qsub_lane_function;
/* A function that performs an unspec and then subtracts it from
another value. */
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_sub>
unspec_based_sub_function;
typedef unspec_based_function_exact_insn<code_for_aarch64_sve_sub_lane>
unspec_based_sub_lane_function;
/* A function that acts like unspec_based_function_exact_insn<INT_CODE>
when operating on integers, but that expands to an (fma ...)-style
aarch64_sve* operation when applied to floats. */
template<insn_code (*INT_CODE) (int, machine_mode)>
class unspec_based_fused_function : public unspec_based_function_base
{
public:
CONSTEXPR unspec_based_fused_function (int unspec_for_sint,
int unspec_for_uint,
int unspec_for_fp)
: unspec_based_function_base (unspec_for_sint, unspec_for_uint,
unspec_for_fp)
{}
rtx
expand (function_expander &e) const OVERRIDE
{
int unspec = unspec_for (e);
insn_code icode;
if (e.type_suffix (0).float_p)
{
/* Put the operands in the normal (fma ...) order, with the accumulator
last. This fits naturally since that's also the unprinted operand
in the asm output. */
e.rotate_inputs_left (0, e.pred != PRED_none ? 4 : 3);
icode = code_for_aarch64_sve (unspec, e.vector_mode (0));
}
else
icode = INT_CODE (unspec, e.vector_mode (0));
return e.use_exact_insn (icode);
}
};
typedef unspec_based_fused_function<code_for_aarch64_sve_add>
unspec_based_mla_function;
typedef unspec_based_fused_function<code_for_aarch64_sve_sub>
unspec_based_mls_function;
/* Like unspec_based_fused_function, but for _lane functions. */
template<insn_code (*INT_CODE) (int, machine_mode)>
class unspec_based_fused_lane_function : public unspec_based_function_base
{
public:
CONSTEXPR unspec_based_fused_lane_function (int unspec_for_sint,
int unspec_for_uint,
int unspec_for_fp)
: unspec_based_function_base (unspec_for_sint, unspec_for_uint,
unspec_for_fp)
{}
rtx
expand (function_expander &e) const OVERRIDE
{
int unspec = unspec_for (e);
insn_code icode;
if (e.type_suffix (0).float_p)
{
/* Put the operands in the normal (fma ...) order, with the accumulator
last. This fits naturally since that's also the unprinted operand
in the asm output. */
e.rotate_inputs_left (0, e.pred != PRED_none ? 5 : 4);
icode = code_for_aarch64_lane (unspec, e.vector_mode (0));
}
else
icode = INT_CODE (unspec, e.vector_mode (0));
return e.use_exact_insn (icode);
}
};
typedef unspec_based_fused_lane_function<code_for_aarch64_sve_add_lane>
unspec_based_mla_lane_function;
typedef unspec_based_fused_lane_function<code_for_aarch64_sve_sub_lane>
unspec_based_mls_lane_function;
/* A function_base that uses CODE_FOR_MODE (M) to get the associated
instruction code, where M is the vector mode associated with type
suffix N. */
template<insn_code (*CODE_FOR_MODE) (machine_mode), unsigned int N>
class code_for_mode_function : public function_base
{
public:
rtx
expand (function_expander &e) const OVERRIDE
{
return e.use_exact_insn (CODE_FOR_MODE (e.vector_mode (N)));
}
};
/* A function that uses code_for_<PATTERN> (M), where M is the vector
mode associated with the first type suffix. */
#define CODE_FOR_MODE0(PATTERN) code_for_mode_function<code_for_##PATTERN, 0>
/* Likewise for the second type suffix. */
#define CODE_FOR_MODE1(PATTERN) code_for_mode_function<code_for_##PATTERN, 1>
/* Like CODE_FOR_MODE0, but the function doesn't raise exceptions when
operating on floating-point data. */
#define QUIET_CODE_FOR_MODE0(PATTERN) \
quiet< code_for_mode_function<code_for_##PATTERN, 0> >
/* A function_base for functions that always expand to a fixed insn pattern,
regardless of what the suffixes are. */
class fixed_insn_function : public function_base
{
public:
CONSTEXPR fixed_insn_function (insn_code code) : m_code (code) {}
rtx
expand (function_expander &e) const OVERRIDE
{
return e.use_exact_insn (m_code);
}
/* The instruction to use. */
insn_code m_code;
};
/* A function_base for functions that permute their arguments. */
class permute : public quiet<function_base>
{
public:
/* Fold a unary or binary permute with the permute vector given by
BUILDER. */
gimple *
fold_permute (const gimple_folder &f, const vec_perm_builder &builder) const
{
/* Punt for now on _b16 and wider; we'd need more complex evpc logic
to rerecognize the result. */
if (f.type_suffix (0).bool_p && f.type_suffix (0).element_bits > 8)
return NULL;
unsigned int nargs = gimple_call_num_args (f.call);
poly_uint64 nelts = TYPE_VECTOR_SUBPARTS (TREE_TYPE (f.lhs));
vec_perm_indices indices (builder, nargs, nelts);
tree perm_type = build_vector_type (ssizetype, nelts);
return gimple_build_assign (f.lhs, VEC_PERM_EXPR,
gimple_call_arg (f.call, 0),
gimple_call_arg (f.call, nargs - 1),
vec_perm_indices_to_tree (perm_type, indices));
}
};
/* A function_base for functions that permute two vectors using a fixed
choice of indices. */
class binary_permute : public permute
{
public:
CONSTEXPR binary_permute (int unspec) : m_unspec (unspec) {}
rtx
expand (function_expander &e) const OVERRIDE
{
insn_code icode = code_for_aarch64_sve (m_unspec, e.vector_mode (0));
return e.use_exact_insn (icode);
}
/* The unspec code associated with the operation. */
int m_unspec;
};
/* A function_base for functions that reduce a vector to a scalar. */
class reduction : public function_base
{
public:
CONSTEXPR reduction (int unspec)
: m_unspec_for_sint (unspec),
m_unspec_for_uint (unspec),
m_unspec_for_fp (unspec)
{}
CONSTEXPR reduction (int unspec_for_sint, int unspec_for_uint,
int unspec_for_fp)
: m_unspec_for_sint (unspec_for_sint),
m_unspec_for_uint (unspec_for_uint),
m_unspec_for_fp (unspec_for_fp)
{}
rtx
expand (function_expander &e) const OVERRIDE
{
machine_mode mode = e.vector_mode (0);
int unspec = (!e.type_suffix (0).integer_p ? m_unspec_for_fp
: e.type_suffix (0).unsigned_p ? m_unspec_for_uint
: m_unspec_for_sint);
/* There's no distinction between SADDV and UADDV for 64-bit elements;
the signed versions only exist for narrower elements. */
if (GET_MODE_UNIT_BITSIZE (mode) == 64 && unspec == UNSPEC_SADDV)
unspec = UNSPEC_UADDV;
return e.use_exact_insn (code_for_aarch64_pred_reduc (unspec, mode));
}
/* The unspec code associated with signed-integer, unsigned-integer
and floating-point operations respectively. */
int m_unspec_for_sint;
int m_unspec_for_uint;
int m_unspec_for_fp;
};
/* A function_base for functions that shift narrower-than-64-bit values
by 64-bit amounts. */
class shift_wide : public function_base
{
public:
CONSTEXPR shift_wide (rtx_code code, int wide_unspec)
: m_code (code), m_wide_unspec (wide_unspec) {}
rtx
expand (function_expander &e) const OVERRIDE
{
machine_mode mode = e.vector_mode (0);
machine_mode elem_mode = GET_MODE_INNER (mode);
/* If the argument is a constant that the normal shifts can handle
directly, use them instead. */
rtx shift = unwrap_const_vec_duplicate (e.args.last ());
if (aarch64_simd_shift_imm_p (shift, elem_mode, m_code == ASHIFT))
{
e.args.last () = shift;
return e.map_to_rtx_codes (m_code, m_code, -1);
}
if (e.pred == PRED_x)
return e.use_unpred_insn (code_for_aarch64_sve (m_wide_unspec, mode));
return e.use_cond_insn (code_for_cond (m_wide_unspec, mode));
}
/* The rtx code associated with a "normal" shift. */
rtx_code m_code;
/* The unspec code associated with the wide shift. */
int m_wide_unspec;
};
/* A function_base for unary functions that count bits. */
class unary_count : public quiet<function_base>
{
public:
CONSTEXPR unary_count (rtx_code code) : m_code (code) {}
rtx
expand (function_expander &e) const OVERRIDE
{
/* The md patterns treat the operand as an integer. */
machine_mode mode = aarch64_sve_int_mode (e.vector_mode (0));
e.args.last () = gen_lowpart (mode, e.args.last ());
if (e.pred == PRED_x)
return e.use_pred_x_insn (code_for_aarch64_pred (m_code, mode));
return e.use_cond_insn (code_for_cond (m_code, mode));
}
/* The rtx code associated with the operation. */
rtx_code m_code;
};
/* A function_base for svwhile* functions. */
class while_comparison : public function_base
{
public:
CONSTEXPR while_comparison (int unspec_for_sint, int unspec_for_uint)
: m_unspec_for_sint (unspec_for_sint),
m_unspec_for_uint (unspec_for_uint)
{}
rtx
expand (function_expander &e) const OVERRIDE
{
/* Suffix 0 determines the predicate mode, suffix 1 determines the
scalar mode and signedness. */
int unspec = (e.type_suffix (1).unsigned_p
? m_unspec_for_uint
: m_unspec_for_sint);
machine_mode pred_mode = e.vector_mode (0);
scalar_mode reg_mode = GET_MODE_INNER (e.vector_mode (1));
return e.use_exact_insn (code_for_while (unspec, reg_mode, pred_mode));
}
/* The unspec codes associated with signed and unsigned operations
respectively. */
int m_unspec_for_sint;
int m_unspec_for_uint;
};
}
/* Declare the global function base NAME, creating it from an instance
of class CLASS with constructor arguments ARGS. */
#define FUNCTION(NAME, CLASS, ARGS) \
namespace { static CONSTEXPR const CLASS NAME##_obj ARGS; } \
namespace functions { const function_base *const NAME = &NAME##_obj; }
#endif