blob: b2901dbcab2d336fe7a061322e569369194c171e [file] [log] [blame]
/* Builtins' description for AArch64 SIMD architecture.
Copyright (C) 2011-2013 Free Software Foundation, Inc.
Contributed by ARM 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/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "expr.h"
#include "tm_p.h"
#include "recog.h"
#include "langhooks.h"
#include "diagnostic-core.h"
#include "optabs.h"
enum aarch64_simd_builtin_type_mode
{
T_V8QI,
T_V4HI,
T_V2SI,
T_V2SF,
T_DI,
T_DF,
T_V16QI,
T_V8HI,
T_V4SI,
T_V4SF,
T_V2DI,
T_V2DF,
T_TI,
T_EI,
T_OI,
T_XI,
T_SI,
T_HI,
T_QI,
T_MAX
};
#define v8qi_UP T_V8QI
#define v4hi_UP T_V4HI
#define v2si_UP T_V2SI
#define v2sf_UP T_V2SF
#define di_UP T_DI
#define df_UP T_DF
#define v16qi_UP T_V16QI
#define v8hi_UP T_V8HI
#define v4si_UP T_V4SI
#define v4sf_UP T_V4SF
#define v2di_UP T_V2DI
#define v2df_UP T_V2DF
#define ti_UP T_TI
#define ei_UP T_EI
#define oi_UP T_OI
#define xi_UP T_XI
#define si_UP T_SI
#define hi_UP T_HI
#define qi_UP T_QI
#define UP(X) X##_UP
typedef enum
{
AARCH64_SIMD_BINOP,
AARCH64_SIMD_TERNOP,
AARCH64_SIMD_QUADOP,
AARCH64_SIMD_UNOP,
AARCH64_SIMD_GETLANE,
AARCH64_SIMD_SETLANE,
AARCH64_SIMD_CREATE,
AARCH64_SIMD_DUP,
AARCH64_SIMD_DUPLANE,
AARCH64_SIMD_COMBINE,
AARCH64_SIMD_SPLIT,
AARCH64_SIMD_LANEMUL,
AARCH64_SIMD_LANEMULL,
AARCH64_SIMD_LANEMULH,
AARCH64_SIMD_LANEMAC,
AARCH64_SIMD_SCALARMUL,
AARCH64_SIMD_SCALARMULL,
AARCH64_SIMD_SCALARMULH,
AARCH64_SIMD_SCALARMAC,
AARCH64_SIMD_CONVERT,
AARCH64_SIMD_FIXCONV,
AARCH64_SIMD_SELECT,
AARCH64_SIMD_RESULTPAIR,
AARCH64_SIMD_REINTERP,
AARCH64_SIMD_VTBL,
AARCH64_SIMD_VTBX,
AARCH64_SIMD_LOAD1,
AARCH64_SIMD_LOAD1LANE,
AARCH64_SIMD_STORE1,
AARCH64_SIMD_STORE1LANE,
AARCH64_SIMD_LOADSTRUCT,
AARCH64_SIMD_LOADSTRUCTLANE,
AARCH64_SIMD_STORESTRUCT,
AARCH64_SIMD_STORESTRUCTLANE,
AARCH64_SIMD_LOGICBINOP,
AARCH64_SIMD_SHIFTINSERT,
AARCH64_SIMD_SHIFTIMM,
AARCH64_SIMD_SHIFTACC
} aarch64_simd_itype;
typedef struct
{
const char *name;
const aarch64_simd_itype itype;
enum aarch64_simd_builtin_type_mode mode;
const enum insn_code code;
unsigned int fcode;
} aarch64_simd_builtin_datum;
#define CF(N, X) CODE_FOR_aarch64_##N##X
#define VAR1(T, N, A) \
{#N, AARCH64_SIMD_##T, UP (A), CF (N, A), 0},
#define VAR2(T, N, A, B) \
VAR1 (T, N, A) \
VAR1 (T, N, B)
#define VAR3(T, N, A, B, C) \
VAR2 (T, N, A, B) \
VAR1 (T, N, C)
#define VAR4(T, N, A, B, C, D) \
VAR3 (T, N, A, B, C) \
VAR1 (T, N, D)
#define VAR5(T, N, A, B, C, D, E) \
VAR4 (T, N, A, B, C, D) \
VAR1 (T, N, E)
#define VAR6(T, N, A, B, C, D, E, F) \
VAR5 (T, N, A, B, C, D, E) \
VAR1 (T, N, F)
#define VAR7(T, N, A, B, C, D, E, F, G) \
VAR6 (T, N, A, B, C, D, E, F) \
VAR1 (T, N, G)
#define VAR8(T, N, A, B, C, D, E, F, G, H) \
VAR7 (T, N, A, B, C, D, E, F, G) \
VAR1 (T, N, H)
#define VAR9(T, N, A, B, C, D, E, F, G, H, I) \
VAR8 (T, N, A, B, C, D, E, F, G, H) \
VAR1 (T, N, I)
#define VAR10(T, N, A, B, C, D, E, F, G, H, I, J) \
VAR9 (T, N, A, B, C, D, E, F, G, H, I) \
VAR1 (T, N, J)
#define VAR11(T, N, A, B, C, D, E, F, G, H, I, J, K) \
VAR10 (T, N, A, B, C, D, E, F, G, H, I, J) \
VAR1 (T, N, K)
#define VAR12(T, N, A, B, C, D, E, F, G, H, I, J, K, L) \
VAR11 (T, N, A, B, C, D, E, F, G, H, I, J, K) \
VAR1 (T, N, L)
/* BUILTIN_<ITERATOR> macros should expand to cover the same range of
modes as is given for each define_mode_iterator in
config/aarch64/iterators.md. */
#define BUILTIN_DX(T, N) \
VAR2 (T, N, di, df)
#define BUILTIN_SDQ_I(T, N) \
VAR4 (T, N, qi, hi, si, di)
#define BUILTIN_SD_HSI(T, N) \
VAR2 (T, N, hi, si)
#define BUILTIN_V2F(T, N) \
VAR2 (T, N, v2sf, v2df)
#define BUILTIN_VALL(T, N) \
VAR10 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si, v2di, v2sf, v4sf, v2df)
#define BUILTIN_VB(T, N) \
VAR2 (T, N, v8qi, v16qi)
#define BUILTIN_VD(T, N) \
VAR4 (T, N, v8qi, v4hi, v2si, v2sf)
#define BUILTIN_VDC(T, N) \
VAR6 (T, N, v8qi, v4hi, v2si, v2sf, di, df)
#define BUILTIN_VDIC(T, N) \
VAR3 (T, N, v8qi, v4hi, v2si)
#define BUILTIN_VDN(T, N) \
VAR3 (T, N, v4hi, v2si, di)
#define BUILTIN_VDQ(T, N) \
VAR7 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si, v2di)
#define BUILTIN_VDQF(T, N) \
VAR3 (T, N, v2sf, v4sf, v2df)
#define BUILTIN_VDQHS(T, N) \
VAR4 (T, N, v4hi, v8hi, v2si, v4si)
#define BUILTIN_VDQIF(T, N) \
VAR9 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si, v2sf, v4sf, v2df)
#define BUILTIN_VDQM(T, N) \
VAR6 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si)
#define BUILTIN_VDQV(T, N) \
VAR5 (T, N, v8qi, v16qi, v4hi, v8hi, v4si)
#define BUILTIN_VDQ_BHSI(T, N) \
VAR6 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si)
#define BUILTIN_VDQ_I(T, N) \
VAR7 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si, v2di)
#define BUILTIN_VDW(T, N) \
VAR3 (T, N, v8qi, v4hi, v2si)
#define BUILTIN_VD_BHSI(T, N) \
VAR3 (T, N, v8qi, v4hi, v2si)
#define BUILTIN_VD_HSI(T, N) \
VAR2 (T, N, v4hi, v2si)
#define BUILTIN_VD_RE(T, N) \
VAR6 (T, N, v8qi, v4hi, v2si, v2sf, di, df)
#define BUILTIN_VQ(T, N) \
VAR6 (T, N, v16qi, v8hi, v4si, v2di, v4sf, v2df)
#define BUILTIN_VQN(T, N) \
VAR3 (T, N, v8hi, v4si, v2di)
#define BUILTIN_VQW(T, N) \
VAR3 (T, N, v16qi, v8hi, v4si)
#define BUILTIN_VQ_HSI(T, N) \
VAR2 (T, N, v8hi, v4si)
#define BUILTIN_VQ_S(T, N) \
VAR6 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si)
#define BUILTIN_VSDQ_HSI(T, N) \
VAR6 (T, N, v4hi, v8hi, v2si, v4si, hi, si)
#define BUILTIN_VSDQ_I(T, N) \
VAR11 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si, v2di, qi, hi, si, di)
#define BUILTIN_VSDQ_I_BHSI(T, N) \
VAR10 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si, v2di, qi, hi, si)
#define BUILTIN_VSDQ_I_DI(T, N) \
VAR8 (T, N, v8qi, v16qi, v4hi, v8hi, v2si, v4si, v2di, di)
#define BUILTIN_VSD_HSI(T, N) \
VAR4 (T, N, v4hi, v2si, hi, si)
#define BUILTIN_VSQN_HSDI(T, N) \
VAR6 (T, N, v8hi, v4si, v2di, hi, si, di)
#define BUILTIN_VSTRUCT(T, N) \
VAR3 (T, N, oi, ci, xi)
static aarch64_simd_builtin_datum aarch64_simd_builtin_data[] = {
#include "aarch64-simd-builtins.def"
};
#undef VAR1
#define VAR1(T, N, A) \
AARCH64_SIMD_BUILTIN_##N##A,
enum aarch64_builtins
{
AARCH64_BUILTIN_MIN,
AARCH64_SIMD_BUILTIN_BASE,
#include "aarch64-simd-builtins.def"
AARCH64_SIMD_BUILTIN_MAX = AARCH64_SIMD_BUILTIN_BASE
+ ARRAY_SIZE (aarch64_simd_builtin_data),
AARCH64_BUILTIN_MAX
};
#undef BUILTIN_DX
#undef BUILTIN_SDQ_I
#undef BUILTIN_SD_HSI
#undef BUILTIN_V2F
#undef BUILTIN_VALL
#undef BUILTIN_VB
#undef BUILTIN_VD
#undef BUILTIN_VDC
#undef BUILTIN_VDIC
#undef BUILTIN_VDN
#undef BUILTIN_VDQ
#undef BUILTIN_VDQF
#undef BUILTIN_VDQHS
#undef BUILTIN_VDQIF
#undef BUILTIN_VDQM
#undef BUILTIN_VDQV
#undef BUILTIN_VDQ_BHSI
#undef BUILTIN_VDQ_I
#undef BUILTIN_VDW
#undef BUILTIN_VD_BHSI
#undef BUILTIN_VD_HSI
#undef BUILTIN_VD_RE
#undef BUILTIN_VQ
#undef BUILTIN_VQN
#undef BUILTIN_VQW
#undef BUILTIN_VQ_HSI
#undef BUILTIN_VQ_S
#undef BUILTIN_VSDQ_HSI
#undef BUILTIN_VSDQ_I
#undef BUILTIN_VSDQ_I_BHSI
#undef BUILTIN_VSDQ_I_DI
#undef BUILTIN_VSD_HSI
#undef BUILTIN_VSQN_HSDI
#undef BUILTIN_VSTRUCT
#undef CF
#undef VAR1
#undef VAR2
#undef VAR3
#undef VAR4
#undef VAR5
#undef VAR6
#undef VAR7
#undef VAR8
#undef VAR9
#undef VAR10
#undef VAR11
static GTY(()) tree aarch64_builtin_decls[AARCH64_BUILTIN_MAX];
#define NUM_DREG_TYPES 6
#define NUM_QREG_TYPES 6
static void
aarch64_init_simd_builtins (void)
{
unsigned int i, fcode = AARCH64_SIMD_BUILTIN_BASE + 1;
/* Scalar type nodes. */
tree aarch64_simd_intQI_type_node;
tree aarch64_simd_intHI_type_node;
tree aarch64_simd_polyQI_type_node;
tree aarch64_simd_polyHI_type_node;
tree aarch64_simd_intSI_type_node;
tree aarch64_simd_intDI_type_node;
tree aarch64_simd_float_type_node;
tree aarch64_simd_double_type_node;
/* Pointer to scalar type nodes. */
tree intQI_pointer_node;
tree intHI_pointer_node;
tree intSI_pointer_node;
tree intDI_pointer_node;
tree float_pointer_node;
tree double_pointer_node;
/* Const scalar type nodes. */
tree const_intQI_node;
tree const_intHI_node;
tree const_intSI_node;
tree const_intDI_node;
tree const_float_node;
tree const_double_node;
/* Pointer to const scalar type nodes. */
tree const_intQI_pointer_node;
tree const_intHI_pointer_node;
tree const_intSI_pointer_node;
tree const_intDI_pointer_node;
tree const_float_pointer_node;
tree const_double_pointer_node;
/* Vector type nodes. */
tree V8QI_type_node;
tree V4HI_type_node;
tree V2SI_type_node;
tree V2SF_type_node;
tree V16QI_type_node;
tree V8HI_type_node;
tree V4SI_type_node;
tree V4SF_type_node;
tree V2DI_type_node;
tree V2DF_type_node;
/* Scalar unsigned type nodes. */
tree intUQI_type_node;
tree intUHI_type_node;
tree intUSI_type_node;
tree intUDI_type_node;
/* Opaque integer types for structures of vectors. */
tree intEI_type_node;
tree intOI_type_node;
tree intCI_type_node;
tree intXI_type_node;
/* Pointer to vector type nodes. */
tree V8QI_pointer_node;
tree V4HI_pointer_node;
tree V2SI_pointer_node;
tree V2SF_pointer_node;
tree V16QI_pointer_node;
tree V8HI_pointer_node;
tree V4SI_pointer_node;
tree V4SF_pointer_node;
tree V2DI_pointer_node;
tree V2DF_pointer_node;
/* Operations which return results as pairs. */
tree void_ftype_pv8qi_v8qi_v8qi;
tree void_ftype_pv4hi_v4hi_v4hi;
tree void_ftype_pv2si_v2si_v2si;
tree void_ftype_pv2sf_v2sf_v2sf;
tree void_ftype_pdi_di_di;
tree void_ftype_pv16qi_v16qi_v16qi;
tree void_ftype_pv8hi_v8hi_v8hi;
tree void_ftype_pv4si_v4si_v4si;
tree void_ftype_pv4sf_v4sf_v4sf;
tree void_ftype_pv2di_v2di_v2di;
tree void_ftype_pv2df_v2df_v2df;
tree reinterp_ftype_dreg[NUM_DREG_TYPES][NUM_DREG_TYPES];
tree reinterp_ftype_qreg[NUM_QREG_TYPES][NUM_QREG_TYPES];
tree dreg_types[NUM_DREG_TYPES], qreg_types[NUM_QREG_TYPES];
/* Create distinguished type nodes for AARCH64_SIMD vector element types,
and pointers to values of such types, so we can detect them later. */
aarch64_simd_intQI_type_node =
make_signed_type (GET_MODE_PRECISION (QImode));
aarch64_simd_intHI_type_node =
make_signed_type (GET_MODE_PRECISION (HImode));
aarch64_simd_polyQI_type_node =
make_signed_type (GET_MODE_PRECISION (QImode));
aarch64_simd_polyHI_type_node =
make_signed_type (GET_MODE_PRECISION (HImode));
aarch64_simd_intSI_type_node =
make_signed_type (GET_MODE_PRECISION (SImode));
aarch64_simd_intDI_type_node =
make_signed_type (GET_MODE_PRECISION (DImode));
aarch64_simd_float_type_node = make_node (REAL_TYPE);
aarch64_simd_double_type_node = make_node (REAL_TYPE);
TYPE_PRECISION (aarch64_simd_float_type_node) = FLOAT_TYPE_SIZE;
TYPE_PRECISION (aarch64_simd_double_type_node) = DOUBLE_TYPE_SIZE;
layout_type (aarch64_simd_float_type_node);
layout_type (aarch64_simd_double_type_node);
/* Define typedefs which exactly correspond to the modes we are basing vector
types on. If you change these names you'll need to change
the table used by aarch64_mangle_type too. */
(*lang_hooks.types.register_builtin_type) (aarch64_simd_intQI_type_node,
"__builtin_aarch64_simd_qi");
(*lang_hooks.types.register_builtin_type) (aarch64_simd_intHI_type_node,
"__builtin_aarch64_simd_hi");
(*lang_hooks.types.register_builtin_type) (aarch64_simd_intSI_type_node,
"__builtin_aarch64_simd_si");
(*lang_hooks.types.register_builtin_type) (aarch64_simd_float_type_node,
"__builtin_aarch64_simd_sf");
(*lang_hooks.types.register_builtin_type) (aarch64_simd_intDI_type_node,
"__builtin_aarch64_simd_di");
(*lang_hooks.types.register_builtin_type) (aarch64_simd_double_type_node,
"__builtin_aarch64_simd_df");
(*lang_hooks.types.register_builtin_type) (aarch64_simd_polyQI_type_node,
"__builtin_aarch64_simd_poly8");
(*lang_hooks.types.register_builtin_type) (aarch64_simd_polyHI_type_node,
"__builtin_aarch64_simd_poly16");
intQI_pointer_node = build_pointer_type (aarch64_simd_intQI_type_node);
intHI_pointer_node = build_pointer_type (aarch64_simd_intHI_type_node);
intSI_pointer_node = build_pointer_type (aarch64_simd_intSI_type_node);
intDI_pointer_node = build_pointer_type (aarch64_simd_intDI_type_node);
float_pointer_node = build_pointer_type (aarch64_simd_float_type_node);
double_pointer_node = build_pointer_type (aarch64_simd_double_type_node);
/* Next create constant-qualified versions of the above types. */
const_intQI_node = build_qualified_type (aarch64_simd_intQI_type_node,
TYPE_QUAL_CONST);
const_intHI_node = build_qualified_type (aarch64_simd_intHI_type_node,
TYPE_QUAL_CONST);
const_intSI_node = build_qualified_type (aarch64_simd_intSI_type_node,
TYPE_QUAL_CONST);
const_intDI_node = build_qualified_type (aarch64_simd_intDI_type_node,
TYPE_QUAL_CONST);
const_float_node = build_qualified_type (aarch64_simd_float_type_node,
TYPE_QUAL_CONST);
const_double_node = build_qualified_type (aarch64_simd_double_type_node,
TYPE_QUAL_CONST);
const_intQI_pointer_node = build_pointer_type (const_intQI_node);
const_intHI_pointer_node = build_pointer_type (const_intHI_node);
const_intSI_pointer_node = build_pointer_type (const_intSI_node);
const_intDI_pointer_node = build_pointer_type (const_intDI_node);
const_float_pointer_node = build_pointer_type (const_float_node);
const_double_pointer_node = build_pointer_type (const_double_node);
/* Now create vector types based on our AARCH64 SIMD element types. */
/* 64-bit vectors. */
V8QI_type_node =
build_vector_type_for_mode (aarch64_simd_intQI_type_node, V8QImode);
V4HI_type_node =
build_vector_type_for_mode (aarch64_simd_intHI_type_node, V4HImode);
V2SI_type_node =
build_vector_type_for_mode (aarch64_simd_intSI_type_node, V2SImode);
V2SF_type_node =
build_vector_type_for_mode (aarch64_simd_float_type_node, V2SFmode);
/* 128-bit vectors. */
V16QI_type_node =
build_vector_type_for_mode (aarch64_simd_intQI_type_node, V16QImode);
V8HI_type_node =
build_vector_type_for_mode (aarch64_simd_intHI_type_node, V8HImode);
V4SI_type_node =
build_vector_type_for_mode (aarch64_simd_intSI_type_node, V4SImode);
V4SF_type_node =
build_vector_type_for_mode (aarch64_simd_float_type_node, V4SFmode);
V2DI_type_node =
build_vector_type_for_mode (aarch64_simd_intDI_type_node, V2DImode);
V2DF_type_node =
build_vector_type_for_mode (aarch64_simd_double_type_node, V2DFmode);
/* Unsigned integer types for various mode sizes. */
intUQI_type_node = make_unsigned_type (GET_MODE_PRECISION (QImode));
intUHI_type_node = make_unsigned_type (GET_MODE_PRECISION (HImode));
intUSI_type_node = make_unsigned_type (GET_MODE_PRECISION (SImode));
intUDI_type_node = make_unsigned_type (GET_MODE_PRECISION (DImode));
(*lang_hooks.types.register_builtin_type) (intUQI_type_node,
"__builtin_aarch64_simd_uqi");
(*lang_hooks.types.register_builtin_type) (intUHI_type_node,
"__builtin_aarch64_simd_uhi");
(*lang_hooks.types.register_builtin_type) (intUSI_type_node,
"__builtin_aarch64_simd_usi");
(*lang_hooks.types.register_builtin_type) (intUDI_type_node,
"__builtin_aarch64_simd_udi");
/* Opaque integer types for structures of vectors. */
intEI_type_node = make_signed_type (GET_MODE_PRECISION (EImode));
intOI_type_node = make_signed_type (GET_MODE_PRECISION (OImode));
intCI_type_node = make_signed_type (GET_MODE_PRECISION (CImode));
intXI_type_node = make_signed_type (GET_MODE_PRECISION (XImode));
(*lang_hooks.types.register_builtin_type) (intTI_type_node,
"__builtin_aarch64_simd_ti");
(*lang_hooks.types.register_builtin_type) (intEI_type_node,
"__builtin_aarch64_simd_ei");
(*lang_hooks.types.register_builtin_type) (intOI_type_node,
"__builtin_aarch64_simd_oi");
(*lang_hooks.types.register_builtin_type) (intCI_type_node,
"__builtin_aarch64_simd_ci");
(*lang_hooks.types.register_builtin_type) (intXI_type_node,
"__builtin_aarch64_simd_xi");
/* Pointers to vector types. */
V8QI_pointer_node = build_pointer_type (V8QI_type_node);
V4HI_pointer_node = build_pointer_type (V4HI_type_node);
V2SI_pointer_node = build_pointer_type (V2SI_type_node);
V2SF_pointer_node = build_pointer_type (V2SF_type_node);
V16QI_pointer_node = build_pointer_type (V16QI_type_node);
V8HI_pointer_node = build_pointer_type (V8HI_type_node);
V4SI_pointer_node = build_pointer_type (V4SI_type_node);
V4SF_pointer_node = build_pointer_type (V4SF_type_node);
V2DI_pointer_node = build_pointer_type (V2DI_type_node);
V2DF_pointer_node = build_pointer_type (V2DF_type_node);
/* Operations which return results as pairs. */
void_ftype_pv8qi_v8qi_v8qi =
build_function_type_list (void_type_node, V8QI_pointer_node,
V8QI_type_node, V8QI_type_node, NULL);
void_ftype_pv4hi_v4hi_v4hi =
build_function_type_list (void_type_node, V4HI_pointer_node,
V4HI_type_node, V4HI_type_node, NULL);
void_ftype_pv2si_v2si_v2si =
build_function_type_list (void_type_node, V2SI_pointer_node,
V2SI_type_node, V2SI_type_node, NULL);
void_ftype_pv2sf_v2sf_v2sf =
build_function_type_list (void_type_node, V2SF_pointer_node,
V2SF_type_node, V2SF_type_node, NULL);
void_ftype_pdi_di_di =
build_function_type_list (void_type_node, intDI_pointer_node,
aarch64_simd_intDI_type_node,
aarch64_simd_intDI_type_node, NULL);
void_ftype_pv16qi_v16qi_v16qi =
build_function_type_list (void_type_node, V16QI_pointer_node,
V16QI_type_node, V16QI_type_node, NULL);
void_ftype_pv8hi_v8hi_v8hi =
build_function_type_list (void_type_node, V8HI_pointer_node,
V8HI_type_node, V8HI_type_node, NULL);
void_ftype_pv4si_v4si_v4si =
build_function_type_list (void_type_node, V4SI_pointer_node,
V4SI_type_node, V4SI_type_node, NULL);
void_ftype_pv4sf_v4sf_v4sf =
build_function_type_list (void_type_node, V4SF_pointer_node,
V4SF_type_node, V4SF_type_node, NULL);
void_ftype_pv2di_v2di_v2di =
build_function_type_list (void_type_node, V2DI_pointer_node,
V2DI_type_node, V2DI_type_node, NULL);
void_ftype_pv2df_v2df_v2df =
build_function_type_list (void_type_node, V2DF_pointer_node,
V2DF_type_node, V2DF_type_node, NULL);
dreg_types[0] = V8QI_type_node;
dreg_types[1] = V4HI_type_node;
dreg_types[2] = V2SI_type_node;
dreg_types[3] = V2SF_type_node;
dreg_types[4] = aarch64_simd_intDI_type_node;
dreg_types[5] = aarch64_simd_double_type_node;
qreg_types[0] = V16QI_type_node;
qreg_types[1] = V8HI_type_node;
qreg_types[2] = V4SI_type_node;
qreg_types[3] = V4SF_type_node;
qreg_types[4] = V2DI_type_node;
qreg_types[5] = V2DF_type_node;
/* If NUM_DREG_TYPES != NUM_QREG_TYPES, we will need separate nested loops
for qreg and dreg reinterp inits. */
for (i = 0; i < NUM_DREG_TYPES; i++)
{
int j;
for (j = 0; j < NUM_DREG_TYPES; j++)
{
reinterp_ftype_dreg[i][j]
= build_function_type_list (dreg_types[i], dreg_types[j], NULL);
reinterp_ftype_qreg[i][j]
= build_function_type_list (qreg_types[i], qreg_types[j], NULL);
}
}
for (i = 0; i < ARRAY_SIZE (aarch64_simd_builtin_data); i++, fcode++)
{
aarch64_simd_builtin_datum *d = &aarch64_simd_builtin_data[i];
const char *const modenames[] =
{
"v8qi", "v4hi", "v2si", "v2sf", "di", "df",
"v16qi", "v8hi", "v4si", "v4sf", "v2di", "v2df",
"ti", "ei", "oi", "xi", "si", "hi", "qi"
};
char namebuf[60];
tree ftype = NULL;
tree fndecl = NULL;
int is_load = 0;
int is_store = 0;
gcc_assert (ARRAY_SIZE (modenames) == T_MAX);
d->fcode = fcode;
switch (d->itype)
{
case AARCH64_SIMD_LOAD1:
case AARCH64_SIMD_LOAD1LANE:
case AARCH64_SIMD_LOADSTRUCT:
case AARCH64_SIMD_LOADSTRUCTLANE:
is_load = 1;
/* Fall through. */
case AARCH64_SIMD_STORE1:
case AARCH64_SIMD_STORE1LANE:
case AARCH64_SIMD_STORESTRUCT:
case AARCH64_SIMD_STORESTRUCTLANE:
if (!is_load)
is_store = 1;
/* Fall through. */
case AARCH64_SIMD_UNOP:
case AARCH64_SIMD_BINOP:
case AARCH64_SIMD_TERNOP:
case AARCH64_SIMD_QUADOP:
case AARCH64_SIMD_COMBINE:
case AARCH64_SIMD_CONVERT:
case AARCH64_SIMD_CREATE:
case AARCH64_SIMD_DUP:
case AARCH64_SIMD_DUPLANE:
case AARCH64_SIMD_FIXCONV:
case AARCH64_SIMD_GETLANE:
case AARCH64_SIMD_LANEMAC:
case AARCH64_SIMD_LANEMUL:
case AARCH64_SIMD_LANEMULH:
case AARCH64_SIMD_LANEMULL:
case AARCH64_SIMD_LOGICBINOP:
case AARCH64_SIMD_SCALARMAC:
case AARCH64_SIMD_SCALARMUL:
case AARCH64_SIMD_SCALARMULH:
case AARCH64_SIMD_SCALARMULL:
case AARCH64_SIMD_SELECT:
case AARCH64_SIMD_SETLANE:
case AARCH64_SIMD_SHIFTACC:
case AARCH64_SIMD_SHIFTIMM:
case AARCH64_SIMD_SHIFTINSERT:
case AARCH64_SIMD_SPLIT:
case AARCH64_SIMD_VTBL:
case AARCH64_SIMD_VTBX:
{
int k;
tree return_type = void_type_node, args = void_list_node;
tree eltype;
/* Build a function type directly from the insn_data for this
builtin. The build_function_type () function takes care of
removing duplicates for us. */
for (k = insn_data[d->code].n_operands -1; k >= 0; k--)
{
/* Skip an internal operand for vget_{low, high}. */
if (k == 2 && d->itype == AARCH64_SIMD_SPLIT)
continue;
if (is_load && k == 1)
{
/* AdvSIMD load patterns always have the memory operand
(a DImode pointer) in the operand 1 position. We
want a const pointer to the element type in that
position. */
gcc_assert (insn_data[d->code].operand[k].mode == DImode);
switch (d->mode)
{
case T_V8QI:
case T_V16QI:
eltype = const_intQI_pointer_node;
break;
case T_V4HI:
case T_V8HI:
eltype = const_intHI_pointer_node;
break;
case T_V2SI:
case T_V4SI:
eltype = const_intSI_pointer_node;
break;
case T_V2SF:
case T_V4SF:
eltype = const_float_pointer_node;
break;
case T_DI:
case T_V2DI:
eltype = const_intDI_pointer_node;
break;
case T_DF:
case T_V2DF:
eltype = const_double_pointer_node;
break;
default:
gcc_unreachable ();
}
}
else if (is_store && k == 0)
{
/* Similarly, AdvSIMD store patterns use operand 0 as
the memory location to store to (a DImode pointer).
Use a pointer to the element type of the store in
that position. */
gcc_assert (insn_data[d->code].operand[k].mode == DImode);
switch (d->mode)
{
case T_V8QI:
case T_V16QI:
eltype = intQI_pointer_node;
break;
case T_V4HI:
case T_V8HI:
eltype = intHI_pointer_node;
break;
case T_V2SI:
case T_V4SI:
eltype = intSI_pointer_node;
break;
case T_V2SF:
case T_V4SF:
eltype = float_pointer_node;
break;
case T_DI:
case T_V2DI:
eltype = intDI_pointer_node;
break;
case T_DF:
case T_V2DF:
eltype = double_pointer_node;
break;
default:
gcc_unreachable ();
}
}
else
{
switch (insn_data[d->code].operand[k].mode)
{
case VOIDmode:
eltype = void_type_node;
break;
/* Scalars. */
case QImode:
eltype = aarch64_simd_intQI_type_node;
break;
case HImode:
eltype = aarch64_simd_intHI_type_node;
break;
case SImode:
eltype = aarch64_simd_intSI_type_node;
break;
case SFmode:
eltype = aarch64_simd_float_type_node;
break;
case DFmode:
eltype = aarch64_simd_double_type_node;
break;
case DImode:
eltype = aarch64_simd_intDI_type_node;
break;
case TImode:
eltype = intTI_type_node;
break;
case EImode:
eltype = intEI_type_node;
break;
case OImode:
eltype = intOI_type_node;
break;
case CImode:
eltype = intCI_type_node;
break;
case XImode:
eltype = intXI_type_node;
break;
/* 64-bit vectors. */
case V8QImode:
eltype = V8QI_type_node;
break;
case V4HImode:
eltype = V4HI_type_node;
break;
case V2SImode:
eltype = V2SI_type_node;
break;
case V2SFmode:
eltype = V2SF_type_node;
break;
/* 128-bit vectors. */
case V16QImode:
eltype = V16QI_type_node;
break;
case V8HImode:
eltype = V8HI_type_node;
break;
case V4SImode:
eltype = V4SI_type_node;
break;
case V4SFmode:
eltype = V4SF_type_node;
break;
case V2DImode:
eltype = V2DI_type_node;
break;
case V2DFmode:
eltype = V2DF_type_node;
break;
default:
gcc_unreachable ();
}
}
if (k == 0 && !is_store)
return_type = eltype;
else
args = tree_cons (NULL_TREE, eltype, args);
}
ftype = build_function_type (return_type, args);
}
break;
case AARCH64_SIMD_RESULTPAIR:
{
switch (insn_data[d->code].operand[1].mode)
{
case V8QImode:
ftype = void_ftype_pv8qi_v8qi_v8qi;
break;
case V4HImode:
ftype = void_ftype_pv4hi_v4hi_v4hi;
break;
case V2SImode:
ftype = void_ftype_pv2si_v2si_v2si;
break;
case V2SFmode:
ftype = void_ftype_pv2sf_v2sf_v2sf;
break;
case DImode:
ftype = void_ftype_pdi_di_di;
break;
case V16QImode:
ftype = void_ftype_pv16qi_v16qi_v16qi;
break;
case V8HImode:
ftype = void_ftype_pv8hi_v8hi_v8hi;
break;
case V4SImode:
ftype = void_ftype_pv4si_v4si_v4si;
break;
case V4SFmode:
ftype = void_ftype_pv4sf_v4sf_v4sf;
break;
case V2DImode:
ftype = void_ftype_pv2di_v2di_v2di;
break;
case V2DFmode:
ftype = void_ftype_pv2df_v2df_v2df;
break;
default:
gcc_unreachable ();
}
}
break;
case AARCH64_SIMD_REINTERP:
{
/* We iterate over 6 doubleword types, then 6 quadword
types. */
int rhs_d = d->mode % NUM_DREG_TYPES;
int rhs_q = (d->mode - NUM_DREG_TYPES) % NUM_QREG_TYPES;
switch (insn_data[d->code].operand[0].mode)
{
case V8QImode:
ftype = reinterp_ftype_dreg[0][rhs_d];
break;
case V4HImode:
ftype = reinterp_ftype_dreg[1][rhs_d];
break;
case V2SImode:
ftype = reinterp_ftype_dreg[2][rhs_d];
break;
case V2SFmode:
ftype = reinterp_ftype_dreg[3][rhs_d];
break;
case DImode:
ftype = reinterp_ftype_dreg[4][rhs_d];
break;
case DFmode:
ftype = reinterp_ftype_dreg[5][rhs_d];
break;
case V16QImode:
ftype = reinterp_ftype_qreg[0][rhs_q];
break;
case V8HImode:
ftype = reinterp_ftype_qreg[1][rhs_q];
break;
case V4SImode:
ftype = reinterp_ftype_qreg[2][rhs_q];
break;
case V4SFmode:
ftype = reinterp_ftype_qreg[3][rhs_q];
break;
case V2DImode:
ftype = reinterp_ftype_qreg[4][rhs_q];
break;
case V2DFmode:
ftype = reinterp_ftype_qreg[5][rhs_q];
break;
default:
gcc_unreachable ();
}
}
break;
default:
gcc_unreachable ();
}
gcc_assert (ftype != NULL);
snprintf (namebuf, sizeof (namebuf), "__builtin_aarch64_%s%s",
d->name, modenames[d->mode]);
fndecl = add_builtin_function (namebuf, ftype, fcode, BUILT_IN_MD,
NULL, NULL_TREE);
aarch64_builtin_decls[fcode] = fndecl;
}
}
void
aarch64_init_builtins (void)
{
if (TARGET_SIMD)
aarch64_init_simd_builtins ();
}
tree
aarch64_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
{
if (code >= AARCH64_BUILTIN_MAX)
return error_mark_node;
return aarch64_builtin_decls[code];
}
typedef enum
{
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_CONSTANT,
SIMD_ARG_STOP
} builtin_simd_arg;
#define SIMD_MAX_BUILTIN_ARGS 5
static rtx
aarch64_simd_expand_args (rtx target, int icode, int have_retval,
tree exp, ...)
{
va_list ap;
rtx pat;
tree arg[SIMD_MAX_BUILTIN_ARGS];
rtx op[SIMD_MAX_BUILTIN_ARGS];
enum machine_mode tmode = insn_data[icode].operand[0].mode;
enum machine_mode mode[SIMD_MAX_BUILTIN_ARGS];
int argc = 0;
if (have_retval
&& (!target
|| GET_MODE (target) != tmode
|| !(*insn_data[icode].operand[0].predicate) (target, tmode)))
target = gen_reg_rtx (tmode);
va_start (ap, exp);
for (;;)
{
builtin_simd_arg thisarg = (builtin_simd_arg) va_arg (ap, int);
if (thisarg == SIMD_ARG_STOP)
break;
else
{
arg[argc] = CALL_EXPR_ARG (exp, argc);
op[argc] = expand_normal (arg[argc]);
mode[argc] = insn_data[icode].operand[argc + have_retval].mode;
switch (thisarg)
{
case SIMD_ARG_COPY_TO_REG:
/*gcc_assert (GET_MODE (op[argc]) == mode[argc]); */
if (!(*insn_data[icode].operand[argc + have_retval].predicate)
(op[argc], mode[argc]))
op[argc] = copy_to_mode_reg (mode[argc], op[argc]);
break;
case SIMD_ARG_CONSTANT:
if (!(*insn_data[icode].operand[argc + have_retval].predicate)
(op[argc], mode[argc]))
error_at (EXPR_LOCATION (exp), "incompatible type for argument %d, "
"expected %<const int%>", argc + 1);
break;
case SIMD_ARG_STOP:
gcc_unreachable ();
}
argc++;
}
}
va_end (ap);
if (have_retval)
switch (argc)
{
case 1:
pat = GEN_FCN (icode) (target, op[0]);
break;
case 2:
pat = GEN_FCN (icode) (target, op[0], op[1]);
break;
case 3:
pat = GEN_FCN (icode) (target, op[0], op[1], op[2]);
break;
case 4:
pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]);
break;
case 5:
pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3], op[4]);
break;
default:
gcc_unreachable ();
}
else
switch (argc)
{
case 1:
pat = GEN_FCN (icode) (op[0]);
break;
case 2:
pat = GEN_FCN (icode) (op[0], op[1]);
break;
case 3:
pat = GEN_FCN (icode) (op[0], op[1], op[2]);
break;
case 4:
pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
break;
case 5:
pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3], op[4]);
break;
default:
gcc_unreachable ();
}
if (!pat)
return 0;
emit_insn (pat);
return target;
}
/* Expand an AArch64 AdvSIMD builtin(intrinsic). */
rtx
aarch64_simd_expand_builtin (int fcode, tree exp, rtx target)
{
aarch64_simd_builtin_datum *d =
&aarch64_simd_builtin_data[fcode - (AARCH64_SIMD_BUILTIN_BASE + 1)];
aarch64_simd_itype itype = d->itype;
enum insn_code icode = d->code;
switch (itype)
{
case AARCH64_SIMD_UNOP:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_STOP);
case AARCH64_SIMD_BINOP:
{
rtx arg2 = expand_normal (CALL_EXPR_ARG (exp, 1));
/* Handle constants only if the predicate allows it. */
bool op1_const_int_p =
(CONST_INT_P (arg2)
&& (*insn_data[icode].operand[2].predicate)
(arg2, insn_data[icode].operand[2].mode));
return aarch64_simd_expand_args
(target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
op1_const_int_p ? SIMD_ARG_CONSTANT : SIMD_ARG_COPY_TO_REG,
SIMD_ARG_STOP);
}
case AARCH64_SIMD_TERNOP:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_STOP);
case AARCH64_SIMD_QUADOP:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_STOP);
case AARCH64_SIMD_LOAD1:
case AARCH64_SIMD_LOADSTRUCT:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG, SIMD_ARG_STOP);
case AARCH64_SIMD_STORE1:
case AARCH64_SIMD_STORESTRUCT:
return aarch64_simd_expand_args (target, icode, 0, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG, SIMD_ARG_STOP);
case AARCH64_SIMD_REINTERP:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG, SIMD_ARG_STOP);
case AARCH64_SIMD_CREATE:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG, SIMD_ARG_STOP);
case AARCH64_SIMD_COMBINE:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG, SIMD_ARG_STOP);
case AARCH64_SIMD_GETLANE:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_CONSTANT,
SIMD_ARG_STOP);
case AARCH64_SIMD_SETLANE:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_CONSTANT,
SIMD_ARG_STOP);
case AARCH64_SIMD_SHIFTIMM:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_CONSTANT,
SIMD_ARG_STOP);
case AARCH64_SIMD_SHIFTACC:
case AARCH64_SIMD_SHIFTINSERT:
return aarch64_simd_expand_args (target, icode, 1, exp,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_COPY_TO_REG,
SIMD_ARG_CONSTANT,
SIMD_ARG_STOP);
default:
gcc_unreachable ();
}
}
/* Expand an expression EXP that calls a built-in function,
with result going to TARGET if that's convenient. */
rtx
aarch64_expand_builtin (tree exp,
rtx target,
rtx subtarget ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
int ignore ATTRIBUTE_UNUSED)
{
tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
int fcode = DECL_FUNCTION_CODE (fndecl);
if (fcode >= AARCH64_SIMD_BUILTIN_BASE)
return aarch64_simd_expand_builtin (fcode, exp, target);
return NULL_RTX;
}
tree
aarch64_builtin_vectorized_function (tree fndecl, tree type_out, tree type_in)
{
enum machine_mode in_mode, out_mode;
int in_n, out_n;
if (TREE_CODE (type_out) != VECTOR_TYPE
|| TREE_CODE (type_in) != VECTOR_TYPE)
return NULL_TREE;
out_mode = TYPE_MODE (TREE_TYPE (type_out));
out_n = TYPE_VECTOR_SUBPARTS (type_out);
in_mode = TYPE_MODE (TREE_TYPE (type_in));
in_n = TYPE_VECTOR_SUBPARTS (type_in);
#undef AARCH64_CHECK_BUILTIN_MODE
#define AARCH64_CHECK_BUILTIN_MODE(C, N) 1
#define AARCH64_FIND_FRINT_VARIANT(N) \
(AARCH64_CHECK_BUILTIN_MODE (2, D) \
? aarch64_builtin_decls[AARCH64_SIMD_BUILTIN_##N##v2df] \
: (AARCH64_CHECK_BUILTIN_MODE (4, S) \
? aarch64_builtin_decls[AARCH64_SIMD_BUILTIN_##N##v4sf] \
: (AARCH64_CHECK_BUILTIN_MODE (2, S) \
? aarch64_builtin_decls[AARCH64_SIMD_BUILTIN_##N##v2sf] \
: NULL_TREE)))
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
{
enum built_in_function fn = DECL_FUNCTION_CODE (fndecl);
switch (fn)
{
#undef AARCH64_CHECK_BUILTIN_MODE
#define AARCH64_CHECK_BUILTIN_MODE(C, N) \
(out_mode == N##Fmode && out_n == C \
&& in_mode == N##Fmode && in_n == C)
case BUILT_IN_FLOOR:
case BUILT_IN_FLOORF:
return AARCH64_FIND_FRINT_VARIANT (frintm);
case BUILT_IN_CEIL:
case BUILT_IN_CEILF:
return AARCH64_FIND_FRINT_VARIANT (frintp);
case BUILT_IN_TRUNC:
case BUILT_IN_TRUNCF:
return AARCH64_FIND_FRINT_VARIANT (frintz);
case BUILT_IN_ROUND:
case BUILT_IN_ROUNDF:
return AARCH64_FIND_FRINT_VARIANT (frinta);
case BUILT_IN_NEARBYINT:
case BUILT_IN_NEARBYINTF:
return AARCH64_FIND_FRINT_VARIANT (frinti);
case BUILT_IN_SQRT:
case BUILT_IN_SQRTF:
return AARCH64_FIND_FRINT_VARIANT (sqrt);
#undef AARCH64_CHECK_BUILTIN_MODE
#define AARCH64_CHECK_BUILTIN_MODE(C, N) \
(out_mode == N##Imode && out_n == C \
&& in_mode == N##Fmode && in_n == C)
case BUILT_IN_LFLOOR:
return AARCH64_FIND_FRINT_VARIANT (fcvtms);
case BUILT_IN_LCEIL:
return AARCH64_FIND_FRINT_VARIANT (fcvtps);
default:
return NULL_TREE;
}
}
return NULL_TREE;
}
#undef AARCH64_CHECK_BUILTIN_MODE
#undef AARCH64_FIND_FRINT_VARIANT