mirror of
https://github.com/Stichting-MINIX-Research-Foundation/netbsd.git
synced 2025-09-17 11:17:04 -04:00
1295 lines
36 KiB
C
1295 lines
36 KiB
C
/* 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
|