| /* Description of builtins used by the ARM backend. |
| Copyright (C) 2014-2015 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/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "rtl.h" |
| #include "hash-set.h" |
| #include "machmode.h" |
| #include "vec.h" |
| #include "double-int.h" |
| #include "input.h" |
| #include "alias.h" |
| #include "symtab.h" |
| #include "wide-int.h" |
| #include "inchash.h" |
| #include "tree.h" |
| #include "fold-const.h" |
| #include "stor-layout.h" |
| #include "hashtab.h" |
| #include "hard-reg-set.h" |
| #include "function.h" |
| #include "flags.h" |
| #include "statistics.h" |
| #include "real.h" |
| #include "fixed-value.h" |
| #include "insn-config.h" |
| #include "expmed.h" |
| #include "dojump.h" |
| #include "explow.h" |
| #include "calls.h" |
| #include "emit-rtl.h" |
| #include "varasm.h" |
| #include "stmt.h" |
| #include "expr.h" |
| #include "tm_p.h" |
| #include "recog.h" |
| #include "langhooks.h" |
| #include "diagnostic-core.h" |
| #include "optabs.h" |
| #include "gimple-expr.h" |
| #include "target.h" |
| #include "ggc.h" |
| #include "arm-protos.h" |
| |
| #define SIMD_MAX_BUILTIN_ARGS 5 |
| |
| enum arm_type_qualifiers |
| { |
| /* T foo. */ |
| qualifier_none = 0x0, |
| /* unsigned T foo. */ |
| qualifier_unsigned = 0x1, /* 1 << 0 */ |
| /* const T foo. */ |
| qualifier_const = 0x2, /* 1 << 1 */ |
| /* T *foo. */ |
| qualifier_pointer = 0x4, /* 1 << 2 */ |
| /* Used when expanding arguments if an operand could |
| be an immediate. */ |
| qualifier_immediate = 0x8, /* 1 << 3 */ |
| qualifier_maybe_immediate = 0x10, /* 1 << 4 */ |
| /* void foo (...). */ |
| qualifier_void = 0x20, /* 1 << 5 */ |
| /* Some patterns may have internal operands, this qualifier is an |
| instruction to the initialisation code to skip this operand. */ |
| qualifier_internal = 0x40, /* 1 << 6 */ |
| /* Some builtins should use the T_*mode* encoded in a simd_builtin_datum |
| rather than using the type of the operand. */ |
| qualifier_map_mode = 0x80, /* 1 << 7 */ |
| /* qualifier_pointer | qualifier_map_mode */ |
| qualifier_pointer_map_mode = 0x84, |
| /* qualifier_const_pointer | qualifier_map_mode */ |
| qualifier_const_pointer_map_mode = 0x86, |
| /* Polynomial types. */ |
| qualifier_poly = 0x100 |
| }; |
| |
| /* The qualifier_internal allows generation of a unary builtin from |
| a pattern with a third pseudo-operand such as a match_scratch. |
| T (T). */ |
| static enum arm_type_qualifiers |
| arm_unop_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_none, qualifier_internal }; |
| #define UNOP_QUALIFIERS (arm_unop_qualifiers) |
| |
| /* unsigned T (unsigned T). */ |
| static enum arm_type_qualifiers |
| arm_bswap_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_unsigned, qualifier_unsigned }; |
| #define BSWAP_QUALIFIERS (arm_bswap_qualifiers) |
| |
| /* T (T, T [maybe_immediate]). */ |
| static enum arm_type_qualifiers |
| arm_binop_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_none, qualifier_maybe_immediate }; |
| #define BINOP_QUALIFIERS (arm_binop_qualifiers) |
| |
| /* T (T, T, T). */ |
| static enum arm_type_qualifiers |
| arm_ternop_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_none, qualifier_none, qualifier_none }; |
| #define TERNOP_QUALIFIERS (arm_ternop_qualifiers) |
| |
| /* T (T, immediate). */ |
| static enum arm_type_qualifiers |
| arm_getlane_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_none, qualifier_immediate }; |
| #define GETLANE_QUALIFIERS (arm_getlane_qualifiers) |
| |
| /* T (T, T, T, immediate). */ |
| static enum arm_type_qualifiers |
| arm_lanemac_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_none, qualifier_none, |
| qualifier_none, qualifier_immediate }; |
| #define LANEMAC_QUALIFIERS (arm_lanemac_qualifiers) |
| |
| /* T (T, T, immediate). */ |
| static enum arm_type_qualifiers |
| arm_setlane_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_none, qualifier_none, qualifier_immediate }; |
| #define SETLANE_QUALIFIERS (arm_setlane_qualifiers) |
| |
| /* T (T, T). */ |
| static enum arm_type_qualifiers |
| arm_combine_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_none, qualifier_none }; |
| #define COMBINE_QUALIFIERS (arm_combine_qualifiers) |
| |
| /* T ([T element type] *). */ |
| static enum arm_type_qualifiers |
| arm_load1_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_const_pointer_map_mode }; |
| #define LOAD1_QUALIFIERS (arm_load1_qualifiers) |
| |
| /* T ([T element type] *, T, immediate). */ |
| static enum arm_type_qualifiers |
| arm_load1_lane_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_none, qualifier_const_pointer_map_mode, |
| qualifier_none, qualifier_immediate }; |
| #define LOAD1LANE_QUALIFIERS (arm_load1_lane_qualifiers) |
| |
| /* The first argument (return type) of a store should be void type, |
| which we represent with qualifier_void. Their first operand will be |
| a DImode pointer to the location to store to, so we must use |
| qualifier_map_mode | qualifier_pointer to build a pointer to the |
| element type of the vector. |
| |
| void ([T element type] *, T). */ |
| static enum arm_type_qualifiers |
| arm_store1_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_void, qualifier_pointer_map_mode, qualifier_none }; |
| #define STORE1_QUALIFIERS (arm_store1_qualifiers) |
| |
| /* void ([T element type] *, T, immediate). */ |
| static enum arm_type_qualifiers |
| arm_storestruct_lane_qualifiers[SIMD_MAX_BUILTIN_ARGS] |
| = { qualifier_void, qualifier_pointer_map_mode, |
| qualifier_none, qualifier_immediate }; |
| #define STORE1LANE_QUALIFIERS (arm_storestruct_lane_qualifiers) |
| |
| #define v8qi_UP V8QImode |
| #define v4hi_UP V4HImode |
| #define v4hf_UP V4HFmode |
| #define v2si_UP V2SImode |
| #define v2sf_UP V2SFmode |
| #define di_UP DImode |
| #define v16qi_UP V16QImode |
| #define v8hi_UP V8HImode |
| #define v4si_UP V4SImode |
| #define v4sf_UP V4SFmode |
| #define v2di_UP V2DImode |
| #define ti_UP TImode |
| #define ei_UP EImode |
| #define oi_UP OImode |
| |
| #define UP(X) X##_UP |
| |
| typedef struct { |
| const char *name; |
| machine_mode mode; |
| const enum insn_code code; |
| unsigned int fcode; |
| enum arm_type_qualifiers *qualifiers; |
| } neon_builtin_datum; |
| |
| #define CF(N,X) CODE_FOR_neon_##N##X |
| |
| #define VAR1(T, N, A) \ |
| {#N #A, UP (A), CF (N, A), 0, T##_QUALIFIERS}, |
| #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) |
| |
| /* The NEON builtin data can be found in arm_neon_builtins.def. |
| The mode entries in the following table correspond to the "key" type of the |
| instruction variant, i.e. equivalent to that which would be specified after |
| the assembler mnemonic, which usually refers to the last vector operand. |
| The modes listed per instruction should be the same as those defined for |
| that instruction's pattern in neon.md. */ |
| |
| static neon_builtin_datum neon_builtin_data[] = |
| { |
| #include "arm_neon_builtins.def" |
| }; |
| |
| #undef CF |
| #undef VAR1 |
| |
| #define VAR1(T, N, X) \ |
| ARM_BUILTIN_NEON_##N##X, |
| |
| enum arm_builtins |
| { |
| ARM_BUILTIN_GETWCGR0, |
| ARM_BUILTIN_GETWCGR1, |
| ARM_BUILTIN_GETWCGR2, |
| ARM_BUILTIN_GETWCGR3, |
| |
| ARM_BUILTIN_SETWCGR0, |
| ARM_BUILTIN_SETWCGR1, |
| ARM_BUILTIN_SETWCGR2, |
| ARM_BUILTIN_SETWCGR3, |
| |
| ARM_BUILTIN_WZERO, |
| |
| ARM_BUILTIN_WAVG2BR, |
| ARM_BUILTIN_WAVG2HR, |
| ARM_BUILTIN_WAVG2B, |
| ARM_BUILTIN_WAVG2H, |
| |
| ARM_BUILTIN_WACCB, |
| ARM_BUILTIN_WACCH, |
| ARM_BUILTIN_WACCW, |
| |
| ARM_BUILTIN_WMACS, |
| ARM_BUILTIN_WMACSZ, |
| ARM_BUILTIN_WMACU, |
| ARM_BUILTIN_WMACUZ, |
| |
| ARM_BUILTIN_WSADB, |
| ARM_BUILTIN_WSADBZ, |
| ARM_BUILTIN_WSADH, |
| ARM_BUILTIN_WSADHZ, |
| |
| ARM_BUILTIN_WALIGNI, |
| ARM_BUILTIN_WALIGNR0, |
| ARM_BUILTIN_WALIGNR1, |
| ARM_BUILTIN_WALIGNR2, |
| ARM_BUILTIN_WALIGNR3, |
| |
| ARM_BUILTIN_TMIA, |
| ARM_BUILTIN_TMIAPH, |
| ARM_BUILTIN_TMIABB, |
| ARM_BUILTIN_TMIABT, |
| ARM_BUILTIN_TMIATB, |
| ARM_BUILTIN_TMIATT, |
| |
| ARM_BUILTIN_TMOVMSKB, |
| ARM_BUILTIN_TMOVMSKH, |
| ARM_BUILTIN_TMOVMSKW, |
| |
| ARM_BUILTIN_TBCSTB, |
| ARM_BUILTIN_TBCSTH, |
| ARM_BUILTIN_TBCSTW, |
| |
| ARM_BUILTIN_WMADDS, |
| ARM_BUILTIN_WMADDU, |
| |
| ARM_BUILTIN_WPACKHSS, |
| ARM_BUILTIN_WPACKWSS, |
| ARM_BUILTIN_WPACKDSS, |
| ARM_BUILTIN_WPACKHUS, |
| ARM_BUILTIN_WPACKWUS, |
| ARM_BUILTIN_WPACKDUS, |
| |
| ARM_BUILTIN_WADDB, |
| ARM_BUILTIN_WADDH, |
| ARM_BUILTIN_WADDW, |
| ARM_BUILTIN_WADDSSB, |
| ARM_BUILTIN_WADDSSH, |
| ARM_BUILTIN_WADDSSW, |
| ARM_BUILTIN_WADDUSB, |
| ARM_BUILTIN_WADDUSH, |
| ARM_BUILTIN_WADDUSW, |
| ARM_BUILTIN_WSUBB, |
| ARM_BUILTIN_WSUBH, |
| ARM_BUILTIN_WSUBW, |
| ARM_BUILTIN_WSUBSSB, |
| ARM_BUILTIN_WSUBSSH, |
| ARM_BUILTIN_WSUBSSW, |
| ARM_BUILTIN_WSUBUSB, |
| ARM_BUILTIN_WSUBUSH, |
| ARM_BUILTIN_WSUBUSW, |
| |
| ARM_BUILTIN_WAND, |
| ARM_BUILTIN_WANDN, |
| ARM_BUILTIN_WOR, |
| ARM_BUILTIN_WXOR, |
| |
| ARM_BUILTIN_WCMPEQB, |
| ARM_BUILTIN_WCMPEQH, |
| ARM_BUILTIN_WCMPEQW, |
| ARM_BUILTIN_WCMPGTUB, |
| ARM_BUILTIN_WCMPGTUH, |
| ARM_BUILTIN_WCMPGTUW, |
| ARM_BUILTIN_WCMPGTSB, |
| ARM_BUILTIN_WCMPGTSH, |
| ARM_BUILTIN_WCMPGTSW, |
| |
| ARM_BUILTIN_TEXTRMSB, |
| ARM_BUILTIN_TEXTRMSH, |
| ARM_BUILTIN_TEXTRMSW, |
| ARM_BUILTIN_TEXTRMUB, |
| ARM_BUILTIN_TEXTRMUH, |
| ARM_BUILTIN_TEXTRMUW, |
| ARM_BUILTIN_TINSRB, |
| ARM_BUILTIN_TINSRH, |
| ARM_BUILTIN_TINSRW, |
| |
| ARM_BUILTIN_WMAXSW, |
| ARM_BUILTIN_WMAXSH, |
| ARM_BUILTIN_WMAXSB, |
| ARM_BUILTIN_WMAXUW, |
| ARM_BUILTIN_WMAXUH, |
| ARM_BUILTIN_WMAXUB, |
| ARM_BUILTIN_WMINSW, |
| ARM_BUILTIN_WMINSH, |
| ARM_BUILTIN_WMINSB, |
| ARM_BUILTIN_WMINUW, |
| ARM_BUILTIN_WMINUH, |
| ARM_BUILTIN_WMINUB, |
| |
| ARM_BUILTIN_WMULUM, |
| ARM_BUILTIN_WMULSM, |
| ARM_BUILTIN_WMULUL, |
| |
| ARM_BUILTIN_PSADBH, |
| ARM_BUILTIN_WSHUFH, |
| |
| ARM_BUILTIN_WSLLH, |
| ARM_BUILTIN_WSLLW, |
| ARM_BUILTIN_WSLLD, |
| ARM_BUILTIN_WSRAH, |
| ARM_BUILTIN_WSRAW, |
| ARM_BUILTIN_WSRAD, |
| ARM_BUILTIN_WSRLH, |
| ARM_BUILTIN_WSRLW, |
| ARM_BUILTIN_WSRLD, |
| ARM_BUILTIN_WRORH, |
| ARM_BUILTIN_WRORW, |
| ARM_BUILTIN_WRORD, |
| ARM_BUILTIN_WSLLHI, |
| ARM_BUILTIN_WSLLWI, |
| ARM_BUILTIN_WSLLDI, |
| ARM_BUILTIN_WSRAHI, |
| ARM_BUILTIN_WSRAWI, |
| ARM_BUILTIN_WSRADI, |
| ARM_BUILTIN_WSRLHI, |
| ARM_BUILTIN_WSRLWI, |
| ARM_BUILTIN_WSRLDI, |
| ARM_BUILTIN_WRORHI, |
| ARM_BUILTIN_WRORWI, |
| ARM_BUILTIN_WRORDI, |
| |
| ARM_BUILTIN_WUNPCKIHB, |
| ARM_BUILTIN_WUNPCKIHH, |
| ARM_BUILTIN_WUNPCKIHW, |
| ARM_BUILTIN_WUNPCKILB, |
| ARM_BUILTIN_WUNPCKILH, |
| ARM_BUILTIN_WUNPCKILW, |
| |
| ARM_BUILTIN_WUNPCKEHSB, |
| ARM_BUILTIN_WUNPCKEHSH, |
| ARM_BUILTIN_WUNPCKEHSW, |
| ARM_BUILTIN_WUNPCKEHUB, |
| ARM_BUILTIN_WUNPCKEHUH, |
| ARM_BUILTIN_WUNPCKEHUW, |
| ARM_BUILTIN_WUNPCKELSB, |
| ARM_BUILTIN_WUNPCKELSH, |
| ARM_BUILTIN_WUNPCKELSW, |
| ARM_BUILTIN_WUNPCKELUB, |
| ARM_BUILTIN_WUNPCKELUH, |
| ARM_BUILTIN_WUNPCKELUW, |
| |
| ARM_BUILTIN_WABSB, |
| ARM_BUILTIN_WABSH, |
| ARM_BUILTIN_WABSW, |
| |
| ARM_BUILTIN_WADDSUBHX, |
| ARM_BUILTIN_WSUBADDHX, |
| |
| ARM_BUILTIN_WABSDIFFB, |
| ARM_BUILTIN_WABSDIFFH, |
| ARM_BUILTIN_WABSDIFFW, |
| |
| ARM_BUILTIN_WADDCH, |
| ARM_BUILTIN_WADDCW, |
| |
| ARM_BUILTIN_WAVG4, |
| ARM_BUILTIN_WAVG4R, |
| |
| ARM_BUILTIN_WMADDSX, |
| ARM_BUILTIN_WMADDUX, |
| |
| ARM_BUILTIN_WMADDSN, |
| ARM_BUILTIN_WMADDUN, |
| |
| ARM_BUILTIN_WMULWSM, |
| ARM_BUILTIN_WMULWUM, |
| |
| ARM_BUILTIN_WMULWSMR, |
| ARM_BUILTIN_WMULWUMR, |
| |
| ARM_BUILTIN_WMULWL, |
| |
| ARM_BUILTIN_WMULSMR, |
| ARM_BUILTIN_WMULUMR, |
| |
| ARM_BUILTIN_WQMULM, |
| ARM_BUILTIN_WQMULMR, |
| |
| ARM_BUILTIN_WQMULWM, |
| ARM_BUILTIN_WQMULWMR, |
| |
| ARM_BUILTIN_WADDBHUSM, |
| ARM_BUILTIN_WADDBHUSL, |
| |
| ARM_BUILTIN_WQMIABB, |
| ARM_BUILTIN_WQMIABT, |
| ARM_BUILTIN_WQMIATB, |
| ARM_BUILTIN_WQMIATT, |
| |
| ARM_BUILTIN_WQMIABBN, |
| ARM_BUILTIN_WQMIABTN, |
| ARM_BUILTIN_WQMIATBN, |
| ARM_BUILTIN_WQMIATTN, |
| |
| ARM_BUILTIN_WMIABB, |
| ARM_BUILTIN_WMIABT, |
| ARM_BUILTIN_WMIATB, |
| ARM_BUILTIN_WMIATT, |
| |
| ARM_BUILTIN_WMIABBN, |
| ARM_BUILTIN_WMIABTN, |
| ARM_BUILTIN_WMIATBN, |
| ARM_BUILTIN_WMIATTN, |
| |
| ARM_BUILTIN_WMIAWBB, |
| ARM_BUILTIN_WMIAWBT, |
| ARM_BUILTIN_WMIAWTB, |
| ARM_BUILTIN_WMIAWTT, |
| |
| ARM_BUILTIN_WMIAWBBN, |
| ARM_BUILTIN_WMIAWBTN, |
| ARM_BUILTIN_WMIAWTBN, |
| ARM_BUILTIN_WMIAWTTN, |
| |
| ARM_BUILTIN_WMERGE, |
| |
| ARM_BUILTIN_CRC32B, |
| ARM_BUILTIN_CRC32H, |
| ARM_BUILTIN_CRC32W, |
| ARM_BUILTIN_CRC32CB, |
| ARM_BUILTIN_CRC32CH, |
| ARM_BUILTIN_CRC32CW, |
| |
| ARM_BUILTIN_GET_FPSCR, |
| ARM_BUILTIN_SET_FPSCR, |
| |
| #undef CRYPTO1 |
| #undef CRYPTO2 |
| #undef CRYPTO3 |
| |
| #define CRYPTO1(L, U, M1, M2) \ |
| ARM_BUILTIN_CRYPTO_##U, |
| #define CRYPTO2(L, U, M1, M2, M3) \ |
| ARM_BUILTIN_CRYPTO_##U, |
| #define CRYPTO3(L, U, M1, M2, M3, M4) \ |
| ARM_BUILTIN_CRYPTO_##U, |
| |
| #include "crypto.def" |
| |
| #undef CRYPTO1 |
| #undef CRYPTO2 |
| #undef CRYPTO3 |
| |
| #include "arm_neon_builtins.def" |
| |
| ARM_BUILTIN_MAX |
| }; |
| |
| #define ARM_BUILTIN_NEON_BASE (ARM_BUILTIN_MAX - ARRAY_SIZE (neon_builtin_data)) |
| |
| #undef CF |
| #undef VAR1 |
| #undef VAR2 |
| #undef VAR3 |
| #undef VAR4 |
| #undef VAR5 |
| #undef VAR6 |
| #undef VAR7 |
| #undef VAR8 |
| #undef VAR9 |
| #undef VAR10 |
| |
| static GTY(()) tree arm_builtin_decls[ARM_BUILTIN_MAX]; |
| |
| #define NUM_DREG_TYPES 5 |
| #define NUM_QREG_TYPES 6 |
| |
| /* Internal scalar builtin types. These types are used to support |
| neon intrinsic builtins. They are _not_ user-visible types. Therefore |
| the mangling for these types are implementation defined. */ |
| const char *arm_scalar_builtin_types[] = { |
| "__builtin_neon_qi", |
| "__builtin_neon_hi", |
| "__builtin_neon_si", |
| "__builtin_neon_sf", |
| "__builtin_neon_di", |
| "__builtin_neon_df", |
| "__builtin_neon_ti", |
| "__builtin_neon_uqi", |
| "__builtin_neon_uhi", |
| "__builtin_neon_usi", |
| "__builtin_neon_udi", |
| "__builtin_neon_ei", |
| "__builtin_neon_oi", |
| "__builtin_neon_ci", |
| "__builtin_neon_xi", |
| NULL |
| }; |
| |
| #define ENTRY(E, M, Q, S, T, G) E, |
| enum arm_simd_type |
| { |
| #include "arm-simd-builtin-types.def" |
| __TYPE_FINAL |
| }; |
| #undef ENTRY |
| |
| struct arm_simd_type_info |
| { |
| enum arm_simd_type type; |
| |
| /* Internal type name. */ |
| const char *name; |
| |
| /* Internal type name(mangled). The mangled names conform to the |
| AAPCS (see "Procedure Call Standard for the ARM Architecture", |
| Appendix A). To qualify for emission with the mangled names defined in |
| that document, a vector type must not only be of the correct mode but also |
| be of the correct internal Neon vector type (e.g. __simd64_int8_t); |
| these types are registered by arm_init_simd_builtin_types (). In other |
| words, vector types defined in other ways e.g. via vector_size attribute |
| will get default mangled names. */ |
| const char *mangle; |
| |
| /* Internal type. */ |
| tree itype; |
| |
| /* Element type. */ |
| tree eltype; |
| |
| /* Machine mode the internal type maps to. */ |
| machine_mode mode; |
| |
| /* Qualifiers. */ |
| enum arm_type_qualifiers q; |
| }; |
| |
| #define ENTRY(E, M, Q, S, T, G) \ |
| {E, \ |
| "__simd" #S "_" #T "_t", \ |
| #G "__simd" #S "_" #T "_t", \ |
| NULL_TREE, NULL_TREE, M##mode, qualifier_##Q}, |
| static struct arm_simd_type_info arm_simd_types [] = { |
| #include "arm-simd-builtin-types.def" |
| }; |
| #undef ENTRY |
| |
| static tree arm_simd_floatHF_type_node = NULL_TREE; |
| static tree arm_simd_intOI_type_node = NULL_TREE; |
| static tree arm_simd_intEI_type_node = NULL_TREE; |
| static tree arm_simd_intCI_type_node = NULL_TREE; |
| static tree arm_simd_intXI_type_node = NULL_TREE; |
| static tree arm_simd_polyQI_type_node = NULL_TREE; |
| static tree arm_simd_polyHI_type_node = NULL_TREE; |
| static tree arm_simd_polyDI_type_node = NULL_TREE; |
| static tree arm_simd_polyTI_type_node = NULL_TREE; |
| |
| static const char * |
| arm_mangle_builtin_scalar_type (const_tree type) |
| { |
| int i = 0; |
| |
| while (arm_scalar_builtin_types[i] != NULL) |
| { |
| const char *name = arm_scalar_builtin_types[i]; |
| |
| if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL |
| && DECL_NAME (TYPE_NAME (type)) |
| && !strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))), name)) |
| return arm_scalar_builtin_types[i]; |
| i++; |
| } |
| return NULL; |
| } |
| |
| static const char * |
| arm_mangle_builtin_vector_type (const_tree type) |
| { |
| int i; |
| int nelts = sizeof (arm_simd_types) / sizeof (arm_simd_types[0]); |
| |
| for (i = 0; i < nelts; i++) |
| if (arm_simd_types[i].mode == TYPE_MODE (type) |
| && TYPE_NAME (type) |
| && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL |
| && DECL_NAME (TYPE_NAME (type)) |
| && !strcmp |
| (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))), |
| arm_simd_types[i].name)) |
| return arm_simd_types[i].mangle; |
| |
| return NULL; |
| } |
| |
| const char * |
| arm_mangle_builtin_type (const_tree type) |
| { |
| const char *mangle; |
| /* Walk through all the AArch64 builtins types tables to filter out the |
| incoming type. */ |
| if ((mangle = arm_mangle_builtin_vector_type (type)) |
| || (mangle = arm_mangle_builtin_scalar_type (type))) |
| return mangle; |
| |
| return NULL; |
| } |
| |
| static tree |
| arm_simd_builtin_std_type (enum machine_mode mode, |
| enum arm_type_qualifiers q) |
| { |
| #define QUAL_TYPE(M) \ |
| ((q == qualifier_none) ? int##M##_type_node : unsigned_int##M##_type_node); |
| switch (mode) |
| { |
| case QImode: |
| return QUAL_TYPE (QI); |
| case HImode: |
| return QUAL_TYPE (HI); |
| case SImode: |
| return QUAL_TYPE (SI); |
| case DImode: |
| return QUAL_TYPE (DI); |
| case TImode: |
| return QUAL_TYPE (TI); |
| case OImode: |
| return arm_simd_intOI_type_node; |
| case EImode: |
| return arm_simd_intEI_type_node; |
| case CImode: |
| return arm_simd_intCI_type_node; |
| case XImode: |
| return arm_simd_intXI_type_node; |
| case HFmode: |
| return arm_simd_floatHF_type_node; |
| case SFmode: |
| return float_type_node; |
| case DFmode: |
| return double_type_node; |
| default: |
| gcc_unreachable (); |
| } |
| #undef QUAL_TYPE |
| } |
| |
| static tree |
| arm_lookup_simd_builtin_type (enum machine_mode mode, |
| enum arm_type_qualifiers q) |
| { |
| int i; |
| int nelts = sizeof (arm_simd_types) / sizeof (arm_simd_types[0]); |
| |
| /* Non-poly scalar modes map to standard types not in the table. */ |
| if (q != qualifier_poly && !VECTOR_MODE_P (mode)) |
| return arm_simd_builtin_std_type (mode, q); |
| |
| for (i = 0; i < nelts; i++) |
| if (arm_simd_types[i].mode == mode |
| && arm_simd_types[i].q == q) |
| return arm_simd_types[i].itype; |
| |
| /* Note that we won't have caught the underlying type for poly64x2_t |
| in the above table. This gets default mangling. */ |
| |
| return NULL_TREE; |
| } |
| |
| static tree |
| arm_simd_builtin_type (enum machine_mode mode, |
| bool unsigned_p, bool poly_p) |
| { |
| if (poly_p) |
| return arm_lookup_simd_builtin_type (mode, qualifier_poly); |
| else if (unsigned_p) |
| return arm_lookup_simd_builtin_type (mode, qualifier_unsigned); |
| else |
| return arm_lookup_simd_builtin_type (mode, qualifier_none); |
| } |
| |
| static void |
| arm_init_simd_builtin_types (void) |
| { |
| int i; |
| int nelts = sizeof (arm_simd_types) / sizeof (arm_simd_types[0]); |
| tree tdecl; |
| |
| /* Initialize the HFmode scalar type. */ |
| arm_simd_floatHF_type_node = make_node (REAL_TYPE); |
| TYPE_PRECISION (arm_simd_floatHF_type_node) = GET_MODE_PRECISION (HFmode); |
| layout_type (arm_simd_floatHF_type_node); |
| (*lang_hooks.types.register_builtin_type) (arm_simd_floatHF_type_node, |
| "__builtin_neon_hf"); |
| |
| /* Poly types are a world of their own. In order to maintain legacy |
| ABI, they get initialized using the old interface, and don't get |
| an entry in our mangling table, consequently, they get default |
| mangling. As a further gotcha, poly8_t and poly16_t are signed |
| types, poly64_t and poly128_t are unsigned types. */ |
| arm_simd_polyQI_type_node |
| = build_distinct_type_copy (intQI_type_node); |
| (*lang_hooks.types.register_builtin_type) (arm_simd_polyQI_type_node, |
| "__builtin_neon_poly8"); |
| arm_simd_polyHI_type_node |
| = build_distinct_type_copy (intHI_type_node); |
| (*lang_hooks.types.register_builtin_type) (arm_simd_polyHI_type_node, |
| "__builtin_neon_poly16"); |
| arm_simd_polyDI_type_node |
| = build_distinct_type_copy (unsigned_intDI_type_node); |
| (*lang_hooks.types.register_builtin_type) (arm_simd_polyDI_type_node, |
| "__builtin_neon_poly64"); |
| arm_simd_polyTI_type_node |
| = build_distinct_type_copy (unsigned_intTI_type_node); |
| (*lang_hooks.types.register_builtin_type) (arm_simd_polyTI_type_node, |
| "__builtin_neon_poly128"); |
| |
| /* Init all the element types built by the front-end. */ |
| arm_simd_types[Int8x8_t].eltype = intQI_type_node; |
| arm_simd_types[Int8x16_t].eltype = intQI_type_node; |
| arm_simd_types[Int16x4_t].eltype = intHI_type_node; |
| arm_simd_types[Int16x8_t].eltype = intHI_type_node; |
| arm_simd_types[Int32x2_t].eltype = intSI_type_node; |
| arm_simd_types[Int32x4_t].eltype = intSI_type_node; |
| arm_simd_types[Int64x2_t].eltype = intDI_type_node; |
| arm_simd_types[Uint8x8_t].eltype = unsigned_intQI_type_node; |
| arm_simd_types[Uint8x16_t].eltype = unsigned_intQI_type_node; |
| arm_simd_types[Uint16x4_t].eltype = unsigned_intHI_type_node; |
| arm_simd_types[Uint16x8_t].eltype = unsigned_intHI_type_node; |
| arm_simd_types[Uint32x2_t].eltype = unsigned_intSI_type_node; |
| arm_simd_types[Uint32x4_t].eltype = unsigned_intSI_type_node; |
| arm_simd_types[Uint64x2_t].eltype = unsigned_intDI_type_node; |
| |
| /* Init poly vector element types with scalar poly types. */ |
| arm_simd_types[Poly8x8_t].eltype = arm_simd_polyQI_type_node; |
| arm_simd_types[Poly8x16_t].eltype = arm_simd_polyQI_type_node; |
| arm_simd_types[Poly16x4_t].eltype = arm_simd_polyHI_type_node; |
| arm_simd_types[Poly16x8_t].eltype = arm_simd_polyHI_type_node; |
| /* Note: poly64x2_t is defined in arm_neon.h, to ensure it gets default |
| mangling. */ |
| |
| /* Continue with standard types. */ |
| arm_simd_types[Float16x4_t].eltype = arm_simd_floatHF_type_node; |
| arm_simd_types[Float32x2_t].eltype = float_type_node; |
| arm_simd_types[Float32x4_t].eltype = float_type_node; |
| |
| for (i = 0; i < nelts; i++) |
| { |
| tree eltype = arm_simd_types[i].eltype; |
| enum machine_mode mode = arm_simd_types[i].mode; |
| |
| if (arm_simd_types[i].itype == NULL) |
| arm_simd_types[i].itype = |
| build_distinct_type_copy |
| (build_vector_type (eltype, GET_MODE_NUNITS (mode))); |
| |
| tdecl = add_builtin_type (arm_simd_types[i].name, |
| arm_simd_types[i].itype); |
| TYPE_NAME (arm_simd_types[i].itype) = tdecl; |
| SET_TYPE_STRUCTURAL_EQUALITY (arm_simd_types[i].itype); |
| } |
| |
| #define AARCH_BUILD_SIGNED_TYPE(mode) \ |
| make_signed_type (GET_MODE_PRECISION (mode)); |
| arm_simd_intOI_type_node = AARCH_BUILD_SIGNED_TYPE (OImode); |
| arm_simd_intEI_type_node = AARCH_BUILD_SIGNED_TYPE (EImode); |
| arm_simd_intCI_type_node = AARCH_BUILD_SIGNED_TYPE (CImode); |
| arm_simd_intXI_type_node = AARCH_BUILD_SIGNED_TYPE (XImode); |
| #undef AARCH_BUILD_SIGNED_TYPE |
| |
| tdecl = add_builtin_type |
| ("__builtin_neon_ei" , arm_simd_intEI_type_node); |
| TYPE_NAME (arm_simd_intEI_type_node) = tdecl; |
| tdecl = add_builtin_type |
| ("__builtin_neon_oi" , arm_simd_intOI_type_node); |
| TYPE_NAME (arm_simd_intOI_type_node) = tdecl; |
| tdecl = add_builtin_type |
| ("__builtin_neon_ci" , arm_simd_intCI_type_node); |
| TYPE_NAME (arm_simd_intCI_type_node) = tdecl; |
| tdecl = add_builtin_type |
| ("__builtin_neon_xi" , arm_simd_intXI_type_node); |
| TYPE_NAME (arm_simd_intXI_type_node) = tdecl; |
| } |
| |
| static void |
| arm_init_simd_builtin_scalar_types (void) |
| { |
| /* Define typedefs for all the standard scalar types. */ |
| (*lang_hooks.types.register_builtin_type) (intQI_type_node, |
| "__builtin_neon_qi"); |
| (*lang_hooks.types.register_builtin_type) (intHI_type_node, |
| "__builtin_neon_hi"); |
| (*lang_hooks.types.register_builtin_type) (intSI_type_node, |
| "__builtin_neon_si"); |
| (*lang_hooks.types.register_builtin_type) (float_type_node, |
| "__builtin_neon_sf"); |
| (*lang_hooks.types.register_builtin_type) (intDI_type_node, |
| "__builtin_neon_di"); |
| (*lang_hooks.types.register_builtin_type) (double_type_node, |
| "__builtin_neon_df"); |
| (*lang_hooks.types.register_builtin_type) (intTI_type_node, |
| "__builtin_neon_ti"); |
| |
| /* Unsigned integer types for various mode sizes. */ |
| (*lang_hooks.types.register_builtin_type) (unsigned_intQI_type_node, |
| "__builtin_neon_uqi"); |
| (*lang_hooks.types.register_builtin_type) (unsigned_intHI_type_node, |
| "__builtin_neon_uhi"); |
| (*lang_hooks.types.register_builtin_type) (unsigned_intSI_type_node, |
| "__builtin_neon_usi"); |
| (*lang_hooks.types.register_builtin_type) (unsigned_intDI_type_node, |
| "__builtin_neon_udi"); |
| (*lang_hooks.types.register_builtin_type) (unsigned_intTI_type_node, |
| "__builtin_neon_uti"); |
| } |
| |
| static void |
| arm_init_neon_builtins (void) |
| { |
| unsigned int i, fcode = ARM_BUILTIN_NEON_BASE; |
| |
| arm_init_simd_builtin_types (); |
| |
| /* Strong-typing hasn't been implemented for all AdvSIMD builtin intrinsics. |
| Therefore we need to preserve the old __builtin scalar types. It can be |
| removed once all the intrinsics become strongly typed using the qualifier |
| system. */ |
| arm_init_simd_builtin_scalar_types (); |
| |
| for (i = 0; i < ARRAY_SIZE (neon_builtin_data); i++, fcode++) |
| { |
| bool print_type_signature_p = false; |
| char type_signature[SIMD_MAX_BUILTIN_ARGS] = { 0 }; |
| neon_builtin_datum *d = &neon_builtin_data[i]; |
| char namebuf[60]; |
| tree ftype = NULL; |
| tree fndecl = NULL; |
| |
| d->fcode = fcode; |
| |
| /* We must track two variables here. op_num is |
| the operand number as in the RTL pattern. This is |
| required to access the mode (e.g. V4SF mode) of the |
| argument, from which the base type can be derived. |
| arg_num is an index in to the qualifiers data, which |
| gives qualifiers to the type (e.g. const unsigned). |
| The reason these two variables may differ by one is the |
| void return type. While all return types take the 0th entry |
| in the qualifiers array, there is no operand for them in the |
| RTL pattern. */ |
| int op_num = insn_data[d->code].n_operands - 1; |
| int arg_num = d->qualifiers[0] & qualifier_void |
| ? op_num + 1 |
| : op_num; |
| 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 (; op_num >= 0; arg_num--, op_num--) |
| { |
| machine_mode op_mode = insn_data[d->code].operand[op_num].mode; |
| enum arm_type_qualifiers qualifiers = d->qualifiers[arg_num]; |
| |
| if (qualifiers & qualifier_unsigned) |
| { |
| type_signature[arg_num] = 'u'; |
| print_type_signature_p = true; |
| } |
| else if (qualifiers & qualifier_poly) |
| { |
| type_signature[arg_num] = 'p'; |
| print_type_signature_p = true; |
| } |
| else |
| type_signature[arg_num] = 's'; |
| |
| /* Skip an internal operand for vget_{low, high}. */ |
| if (qualifiers & qualifier_internal) |
| continue; |
| |
| /* Some builtins have different user-facing types |
| for certain arguments, encoded in d->mode. */ |
| if (qualifiers & qualifier_map_mode) |
| op_mode = d->mode; |
| |
| /* For pointers, we want a pointer to the basic type |
| of the vector. */ |
| if (qualifiers & qualifier_pointer && VECTOR_MODE_P (op_mode)) |
| op_mode = GET_MODE_INNER (op_mode); |
| |
| eltype = arm_simd_builtin_type |
| (op_mode, |
| (qualifiers & qualifier_unsigned) != 0, |
| (qualifiers & qualifier_poly) != 0); |
| gcc_assert (eltype != NULL); |
| |
| /* Add qualifiers. */ |
| if (qualifiers & qualifier_const) |
| eltype = build_qualified_type (eltype, TYPE_QUAL_CONST); |
| |
| if (qualifiers & qualifier_pointer) |
| eltype = build_pointer_type (eltype); |
| |
| /* If we have reached arg_num == 0, we are at a non-void |
| return type. Otherwise, we are still processing |
| arguments. */ |
| if (arg_num == 0) |
| return_type = eltype; |
| else |
| args = tree_cons (NULL_TREE, eltype, args); |
| } |
| |
| ftype = build_function_type (return_type, args); |
| |
| gcc_assert (ftype != NULL); |
| |
| if (print_type_signature_p) |
| snprintf (namebuf, sizeof (namebuf), "__builtin_neon_%s_%s", |
| d->name, type_signature); |
| else |
| snprintf (namebuf, sizeof (namebuf), "__builtin_neon_%s", |
| d->name); |
| |
| fndecl = add_builtin_function (namebuf, ftype, fcode, BUILT_IN_MD, |
| NULL, NULL_TREE); |
| arm_builtin_decls[fcode] = fndecl; |
| } |
| |
| if (TARGET_CRYPTO && TARGET_HARD_FLOAT) |
| { |
| tree V16UQI_type_node = arm_simd_builtin_type (V16QImode, |
| true, |
| false); |
| |
| tree V4USI_type_node = arm_simd_builtin_type (V4SImode, |
| true, |
| false); |
| |
| tree v16uqi_ftype_v16uqi |
| = build_function_type_list (V16UQI_type_node, V16UQI_type_node, |
| NULL_TREE); |
| |
| tree v16uqi_ftype_v16uqi_v16uqi |
| = build_function_type_list (V16UQI_type_node, V16UQI_type_node, |
| V16UQI_type_node, NULL_TREE); |
| |
| tree v4usi_ftype_v4usi |
| = build_function_type_list (V4USI_type_node, V4USI_type_node, |
| NULL_TREE); |
| |
| tree v4usi_ftype_v4usi_v4usi |
| = build_function_type_list (V4USI_type_node, V4USI_type_node, |
| V4USI_type_node, NULL_TREE); |
| |
| tree v4usi_ftype_v4usi_v4usi_v4usi |
| = build_function_type_list (V4USI_type_node, V4USI_type_node, |
| V4USI_type_node, V4USI_type_node, |
| NULL_TREE); |
| |
| tree uti_ftype_udi_udi |
| = build_function_type_list (unsigned_intTI_type_node, |
| unsigned_intDI_type_node, |
| unsigned_intDI_type_node, |
| NULL_TREE); |
| |
| #undef CRYPTO1 |
| #undef CRYPTO2 |
| #undef CRYPTO3 |
| #undef C |
| #undef N |
| #undef CF |
| #undef FT1 |
| #undef FT2 |
| #undef FT3 |
| |
| #define C(U) \ |
| ARM_BUILTIN_CRYPTO_##U |
| #define N(L) \ |
| "__builtin_arm_crypto_"#L |
| #define FT1(R, A) \ |
| R##_ftype_##A |
| #define FT2(R, A1, A2) \ |
| R##_ftype_##A1##_##A2 |
| #define FT3(R, A1, A2, A3) \ |
| R##_ftype_##A1##_##A2##_##A3 |
| #define CRYPTO1(L, U, R, A) \ |
| arm_builtin_decls[C (U)] \ |
| = add_builtin_function (N (L), FT1 (R, A), \ |
| C (U), BUILT_IN_MD, NULL, NULL_TREE); |
| #define CRYPTO2(L, U, R, A1, A2) \ |
| arm_builtin_decls[C (U)] \ |
| = add_builtin_function (N (L), FT2 (R, A1, A2), \ |
| C (U), BUILT_IN_MD, NULL, NULL_TREE); |
| |
| #define CRYPTO3(L, U, R, A1, A2, A3) \ |
| arm_builtin_decls[C (U)] \ |
| = add_builtin_function (N (L), FT3 (R, A1, A2, A3), \ |
| C (U), BUILT_IN_MD, NULL, NULL_TREE); |
| #include "crypto.def" |
| |
| #undef CRYPTO1 |
| #undef CRYPTO2 |
| #undef CRYPTO3 |
| #undef C |
| #undef N |
| #undef FT1 |
| #undef FT2 |
| #undef FT3 |
| } |
| } |
| |
| #undef NUM_DREG_TYPES |
| #undef NUM_QREG_TYPES |
| |
| #define def_mbuiltin(MASK, NAME, TYPE, CODE) \ |
| do \ |
| { \ |
| if ((MASK) & insn_flags) \ |
| { \ |
| tree bdecl; \ |
| bdecl = add_builtin_function ((NAME), (TYPE), (CODE), \ |
| BUILT_IN_MD, NULL, NULL_TREE); \ |
| arm_builtin_decls[CODE] = bdecl; \ |
| } \ |
| } \ |
| while (0) |
| |
| struct builtin_description |
| { |
| const unsigned int mask; |
| const enum insn_code icode; |
| const char * const name; |
| const enum arm_builtins code; |
| const enum rtx_code comparison; |
| const unsigned int flag; |
| }; |
| |
| static const struct builtin_description bdesc_2arg[] = |
| { |
| #define IWMMXT_BUILTIN(code, string, builtin) \ |
| { FL_IWMMXT, CODE_FOR_##code, "__builtin_arm_" string, \ |
| ARM_BUILTIN_##builtin, UNKNOWN, 0 }, |
| |
| #define IWMMXT2_BUILTIN(code, string, builtin) \ |
| { FL_IWMMXT2, CODE_FOR_##code, "__builtin_arm_" string, \ |
| ARM_BUILTIN_##builtin, UNKNOWN, 0 }, |
| |
| IWMMXT_BUILTIN (addv8qi3, "waddb", WADDB) |
| IWMMXT_BUILTIN (addv4hi3, "waddh", WADDH) |
| IWMMXT_BUILTIN (addv2si3, "waddw", WADDW) |
| IWMMXT_BUILTIN (subv8qi3, "wsubb", WSUBB) |
| IWMMXT_BUILTIN (subv4hi3, "wsubh", WSUBH) |
| IWMMXT_BUILTIN (subv2si3, "wsubw", WSUBW) |
| IWMMXT_BUILTIN (ssaddv8qi3, "waddbss", WADDSSB) |
| IWMMXT_BUILTIN (ssaddv4hi3, "waddhss", WADDSSH) |
| IWMMXT_BUILTIN (ssaddv2si3, "waddwss", WADDSSW) |
| IWMMXT_BUILTIN (sssubv8qi3, "wsubbss", WSUBSSB) |
| IWMMXT_BUILTIN (sssubv4hi3, "wsubhss", WSUBSSH) |
| IWMMXT_BUILTIN (sssubv2si3, "wsubwss", WSUBSSW) |
| IWMMXT_BUILTIN (usaddv8qi3, "waddbus", WADDUSB) |
| IWMMXT_BUILTIN (usaddv4hi3, "waddhus", WADDUSH) |
| IWMMXT_BUILTIN (usaddv2si3, "waddwus", WADDUSW) |
| IWMMXT_BUILTIN (ussubv8qi3, "wsubbus", WSUBUSB) |
| IWMMXT_BUILTIN (ussubv4hi3, "wsubhus", WSUBUSH) |
| IWMMXT_BUILTIN (ussubv2si3, "wsubwus", WSUBUSW) |
| IWMMXT_BUILTIN (mulv4hi3, "wmulul", WMULUL) |
| IWMMXT_BUILTIN (smulv4hi3_highpart, "wmulsm", WMULSM) |
| IWMMXT_BUILTIN (umulv4hi3_highpart, "wmulum", WMULUM) |
| IWMMXT_BUILTIN (eqv8qi3, "wcmpeqb", WCMPEQB) |
| IWMMXT_BUILTIN (eqv4hi3, "wcmpeqh", WCMPEQH) |
| IWMMXT_BUILTIN (eqv2si3, "wcmpeqw", WCMPEQW) |
| IWMMXT_BUILTIN (gtuv8qi3, "wcmpgtub", WCMPGTUB) |
| IWMMXT_BUILTIN (gtuv4hi3, "wcmpgtuh", WCMPGTUH) |
| IWMMXT_BUILTIN (gtuv2si3, "wcmpgtuw", WCMPGTUW) |
| IWMMXT_BUILTIN (gtv8qi3, "wcmpgtsb", WCMPGTSB) |
| IWMMXT_BUILTIN (gtv4hi3, "wcmpgtsh", WCMPGTSH) |
| IWMMXT_BUILTIN (gtv2si3, "wcmpgtsw", WCMPGTSW) |
| IWMMXT_BUILTIN (umaxv8qi3, "wmaxub", WMAXUB) |
| IWMMXT_BUILTIN (smaxv8qi3, "wmaxsb", WMAXSB) |
| IWMMXT_BUILTIN (umaxv4hi3, "wmaxuh", WMAXUH) |
| IWMMXT_BUILTIN (smaxv4hi3, "wmaxsh", WMAXSH) |
| IWMMXT_BUILTIN (umaxv2si3, "wmaxuw", WMAXUW) |
| IWMMXT_BUILTIN (smaxv2si3, "wmaxsw", WMAXSW) |
| IWMMXT_BUILTIN (uminv8qi3, "wminub", WMINUB) |
| IWMMXT_BUILTIN (sminv8qi3, "wminsb", WMINSB) |
| IWMMXT_BUILTIN (uminv4hi3, "wminuh", WMINUH) |
| IWMMXT_BUILTIN (sminv4hi3, "wminsh", WMINSH) |
| IWMMXT_BUILTIN (uminv2si3, "wminuw", WMINUW) |
| IWMMXT_BUILTIN (sminv2si3, "wminsw", WMINSW) |
| IWMMXT_BUILTIN (iwmmxt_anddi3, "wand", WAND) |
| IWMMXT_BUILTIN (iwmmxt_nanddi3, "wandn", WANDN) |
| IWMMXT_BUILTIN (iwmmxt_iordi3, "wor", WOR) |
| IWMMXT_BUILTIN (iwmmxt_xordi3, "wxor", WXOR) |
| IWMMXT_BUILTIN (iwmmxt_uavgv8qi3, "wavg2b", WAVG2B) |
| IWMMXT_BUILTIN (iwmmxt_uavgv4hi3, "wavg2h", WAVG2H) |
| IWMMXT_BUILTIN (iwmmxt_uavgrndv8qi3, "wavg2br", WAVG2BR) |
| IWMMXT_BUILTIN (iwmmxt_uavgrndv4hi3, "wavg2hr", WAVG2HR) |
| IWMMXT_BUILTIN (iwmmxt_wunpckilb, "wunpckilb", WUNPCKILB) |
| IWMMXT_BUILTIN (iwmmxt_wunpckilh, "wunpckilh", WUNPCKILH) |
| IWMMXT_BUILTIN (iwmmxt_wunpckilw, "wunpckilw", WUNPCKILW) |
| IWMMXT_BUILTIN (iwmmxt_wunpckihb, "wunpckihb", WUNPCKIHB) |
| IWMMXT_BUILTIN (iwmmxt_wunpckihh, "wunpckihh", WUNPCKIHH) |
| IWMMXT_BUILTIN (iwmmxt_wunpckihw, "wunpckihw", WUNPCKIHW) |
| IWMMXT2_BUILTIN (iwmmxt_waddsubhx, "waddsubhx", WADDSUBHX) |
| IWMMXT2_BUILTIN (iwmmxt_wsubaddhx, "wsubaddhx", WSUBADDHX) |
| IWMMXT2_BUILTIN (iwmmxt_wabsdiffb, "wabsdiffb", WABSDIFFB) |
| IWMMXT2_BUILTIN (iwmmxt_wabsdiffh, "wabsdiffh", WABSDIFFH) |
| IWMMXT2_BUILTIN (iwmmxt_wabsdiffw, "wabsdiffw", WABSDIFFW) |
| IWMMXT2_BUILTIN (iwmmxt_avg4, "wavg4", WAVG4) |
| IWMMXT2_BUILTIN (iwmmxt_avg4r, "wavg4r", WAVG4R) |
| IWMMXT2_BUILTIN (iwmmxt_wmulwsm, "wmulwsm", WMULWSM) |
| IWMMXT2_BUILTIN (iwmmxt_wmulwum, "wmulwum", WMULWUM) |
| IWMMXT2_BUILTIN (iwmmxt_wmulwsmr, "wmulwsmr", WMULWSMR) |
| IWMMXT2_BUILTIN (iwmmxt_wmulwumr, "wmulwumr", WMULWUMR) |
| IWMMXT2_BUILTIN (iwmmxt_wmulwl, "wmulwl", WMULWL) |
| IWMMXT2_BUILTIN (iwmmxt_wmulsmr, "wmulsmr", WMULSMR) |
| IWMMXT2_BUILTIN (iwmmxt_wmulumr, "wmulumr", WMULUMR) |
| IWMMXT2_BUILTIN (iwmmxt_wqmulm, "wqmulm", WQMULM) |
| IWMMXT2_BUILTIN (iwmmxt_wqmulmr, "wqmulmr", WQMULMR) |
| IWMMXT2_BUILTIN (iwmmxt_wqmulwm, "wqmulwm", WQMULWM) |
| IWMMXT2_BUILTIN (iwmmxt_wqmulwmr, "wqmulwmr", WQMULWMR) |
| IWMMXT_BUILTIN (iwmmxt_walignr0, "walignr0", WALIGNR0) |
| IWMMXT_BUILTIN (iwmmxt_walignr1, "walignr1", WALIGNR1) |
| IWMMXT_BUILTIN (iwmmxt_walignr2, "walignr2", WALIGNR2) |
| IWMMXT_BUILTIN (iwmmxt_walignr3, "walignr3", WALIGNR3) |
| |
| #define IWMMXT_BUILTIN2(code, builtin) \ |
| { FL_IWMMXT, CODE_FOR_##code, NULL, ARM_BUILTIN_##builtin, UNKNOWN, 0 }, |
| |
| #define IWMMXT2_BUILTIN2(code, builtin) \ |
| { FL_IWMMXT2, CODE_FOR_##code, NULL, ARM_BUILTIN_##builtin, UNKNOWN, 0 }, |
| |
| IWMMXT2_BUILTIN2 (iwmmxt_waddbhusm, WADDBHUSM) |
| IWMMXT2_BUILTIN2 (iwmmxt_waddbhusl, WADDBHUSL) |
| IWMMXT_BUILTIN2 (iwmmxt_wpackhss, WPACKHSS) |
| IWMMXT_BUILTIN2 (iwmmxt_wpackwss, WPACKWSS) |
| IWMMXT_BUILTIN2 (iwmmxt_wpackdss, WPACKDSS) |
| IWMMXT_BUILTIN2 (iwmmxt_wpackhus, WPACKHUS) |
| IWMMXT_BUILTIN2 (iwmmxt_wpackwus, WPACKWUS) |
| IWMMXT_BUILTIN2 (iwmmxt_wpackdus, WPACKDUS) |
| IWMMXT_BUILTIN2 (iwmmxt_wmacuz, WMACUZ) |
| IWMMXT_BUILTIN2 (iwmmxt_wmacsz, WMACSZ) |
| |
| |
| #define FP_BUILTIN(L, U) \ |
| {0, CODE_FOR_##L, "__builtin_arm_"#L, ARM_BUILTIN_##U, \ |
| UNKNOWN, 0}, |
| |
| FP_BUILTIN (get_fpscr, GET_FPSCR) |
| FP_BUILTIN (set_fpscr, SET_FPSCR) |
| #undef FP_BUILTIN |
| |
| #define CRC32_BUILTIN(L, U) \ |
| {0, CODE_FOR_##L, "__builtin_arm_"#L, ARM_BUILTIN_##U, \ |
| UNKNOWN, 0}, |
| CRC32_BUILTIN (crc32b, CRC32B) |
| CRC32_BUILTIN (crc32h, CRC32H) |
| CRC32_BUILTIN (crc32w, CRC32W) |
| CRC32_BUILTIN (crc32cb, CRC32CB) |
| CRC32_BUILTIN (crc32ch, CRC32CH) |
| CRC32_BUILTIN (crc32cw, CRC32CW) |
| #undef CRC32_BUILTIN |
| |
| |
| #define CRYPTO_BUILTIN(L, U) \ |
| {0, CODE_FOR_crypto_##L, "__builtin_arm_crypto_"#L, ARM_BUILTIN_CRYPTO_##U, \ |
| UNKNOWN, 0}, |
| #undef CRYPTO1 |
| #undef CRYPTO2 |
| #undef CRYPTO3 |
| #define CRYPTO2(L, U, R, A1, A2) CRYPTO_BUILTIN (L, U) |
| #define CRYPTO1(L, U, R, A) |
| #define CRYPTO3(L, U, R, A1, A2, A3) |
| #include "crypto.def" |
| #undef CRYPTO1 |
| #undef CRYPTO2 |
| #undef CRYPTO3 |
| |
| }; |
| |
| static const struct builtin_description bdesc_1arg[] = |
| { |
| IWMMXT_BUILTIN (iwmmxt_tmovmskb, "tmovmskb", TMOVMSKB) |
| IWMMXT_BUILTIN (iwmmxt_tmovmskh, "tmovmskh", TMOVMSKH) |
| IWMMXT_BUILTIN (iwmmxt_tmovmskw, "tmovmskw", TMOVMSKW) |
| IWMMXT_BUILTIN (iwmmxt_waccb, "waccb", WACCB) |
| IWMMXT_BUILTIN (iwmmxt_wacch, "wacch", WACCH) |
| IWMMXT_BUILTIN (iwmmxt_waccw, "waccw", WACCW) |
| IWMMXT_BUILTIN (iwmmxt_wunpckehub, "wunpckehub", WUNPCKEHUB) |
| IWMMXT_BUILTIN (iwmmxt_wunpckehuh, "wunpckehuh", WUNPCKEHUH) |
| IWMMXT_BUILTIN (iwmmxt_wunpckehuw, "wunpckehuw", WUNPCKEHUW) |
| IWMMXT_BUILTIN (iwmmxt_wunpckehsb, "wunpckehsb", WUNPCKEHSB) |
| IWMMXT_BUILTIN (iwmmxt_wunpckehsh, "wunpckehsh", WUNPCKEHSH) |
| IWMMXT_BUILTIN (iwmmxt_wunpckehsw, "wunpckehsw", WUNPCKEHSW) |
| IWMMXT_BUILTIN (iwmmxt_wunpckelub, "wunpckelub", WUNPCKELUB) |
| IWMMXT_BUILTIN (iwmmxt_wunpckeluh, "wunpckeluh", WUNPCKELUH) |
| IWMMXT_BUILTIN (iwmmxt_wunpckeluw, "wunpckeluw", WUNPCKELUW) |
| IWMMXT_BUILTIN (iwmmxt_wunpckelsb, "wunpckelsb", WUNPCKELSB) |
| IWMMXT_BUILTIN (iwmmxt_wunpckelsh, "wunpckelsh", WUNPCKELSH) |
| IWMMXT_BUILTIN (iwmmxt_wunpckelsw, "wunpckelsw", WUNPCKELSW) |
| IWMMXT2_BUILTIN (iwmmxt_wabsv8qi3, "wabsb", WABSB) |
| IWMMXT2_BUILTIN (iwmmxt_wabsv4hi3, "wabsh", WABSH) |
| IWMMXT2_BUILTIN (iwmmxt_wabsv2si3, "wabsw", WABSW) |
| IWMMXT_BUILTIN (tbcstv8qi, "tbcstb", TBCSTB) |
| IWMMXT_BUILTIN (tbcstv4hi, "tbcsth", TBCSTH) |
| IWMMXT_BUILTIN (tbcstv2si, "tbcstw", TBCSTW) |
| |
| #define CRYPTO1(L, U, R, A) CRYPTO_BUILTIN (L, U) |
| #define CRYPTO2(L, U, R, A1, A2) |
| #define CRYPTO3(L, U, R, A1, A2, A3) |
| #include "crypto.def" |
| #undef CRYPTO1 |
| #undef CRYPTO2 |
| #undef CRYPTO3 |
| }; |
| |
| static const struct builtin_description bdesc_3arg[] = |
| { |
| #define CRYPTO3(L, U, R, A1, A2, A3) CRYPTO_BUILTIN (L, U) |
| #define CRYPTO1(L, U, R, A) |
| #define CRYPTO2(L, U, R, A1, A2) |
| #include "crypto.def" |
| #undef CRYPTO1 |
| #undef CRYPTO2 |
| #undef CRYPTO3 |
| }; |
| #undef CRYPTO_BUILTIN |
| |
| /* Set up all the iWMMXt builtins. This is not called if |
| TARGET_IWMMXT is zero. */ |
| |
| static void |
| arm_init_iwmmxt_builtins (void) |
| { |
| const struct builtin_description * d; |
| size_t i; |
| |
| tree V2SI_type_node = build_vector_type_for_mode (intSI_type_node, V2SImode); |
| tree V4HI_type_node = build_vector_type_for_mode (intHI_type_node, V4HImode); |
| tree V8QI_type_node = build_vector_type_for_mode (intQI_type_node, V8QImode); |
| |
| tree v8qi_ftype_v8qi_v8qi_int |
| = build_function_type_list (V8QI_type_node, |
| V8QI_type_node, V8QI_type_node, |
| integer_type_node, NULL_TREE); |
| tree v4hi_ftype_v4hi_int |
| = build_function_type_list (V4HI_type_node, |
| V4HI_type_node, integer_type_node, NULL_TREE); |
| tree v2si_ftype_v2si_int |
| = build_function_type_list (V2SI_type_node, |
| V2SI_type_node, integer_type_node, NULL_TREE); |
| tree v2si_ftype_di_di |
| = build_function_type_list (V2SI_type_node, |
| long_long_integer_type_node, |
| long_long_integer_type_node, |
| NULL_TREE); |
| tree di_ftype_di_int |
| = build_function_type_list (long_long_integer_type_node, |
| long_long_integer_type_node, |
| integer_type_node, NULL_TREE); |
| tree di_ftype_di_int_int |
| = build_function_type_list (long_long_integer_type_node, |
| long_long_integer_type_node, |
| integer_type_node, |
| integer_type_node, NULL_TREE); |
| tree int_ftype_v8qi |
| = build_function_type_list (integer_type_node, |
| V8QI_type_node, NULL_TREE); |
| tree int_ftype_v4hi |
| = build_function_type_list (integer_type_node, |
| V4HI_type_node, NULL_TREE); |
| tree int_ftype_v2si |
| = build_function_type_list (integer_type_node, |
| V2SI_type_node, NULL_TREE); |
| tree int_ftype_v8qi_int |
| = build_function_type_list (integer_type_node, |
| V8QI_type_node, integer_type_node, NULL_TREE); |
| tree int_ftype_v4hi_int |
| = build_function_type_list (integer_type_node, |
| V4HI_type_node, integer_type_node, NULL_TREE); |
| tree int_ftype_v2si_int |
| = build_function_type_list (integer_type_node, |
| V2SI_type_node, integer_type_node, NULL_TREE); |
| tree v8qi_ftype_v8qi_int_int |
| = build_function_type_list (V8QI_type_node, |
| V8QI_type_node, integer_type_node, |
| integer_type_node, NULL_TREE); |
| tree v4hi_ftype_v4hi_int_int |
| = build_function_type_list (V4HI_type_node, |
| V4HI_type_node, integer_type_node, |
| integer_type_node, NULL_TREE); |
| tree v2si_ftype_v2si_int_int |
| = build_function_type_list (V2SI_type_node, |
| V2SI_type_node, integer_type_node, |
| integer_type_node, NULL_TREE); |
| /* Miscellaneous. */ |
| tree v8qi_ftype_v4hi_v4hi |
| = build_function_type_list (V8QI_type_node, |
| V4HI_type_node, V4HI_type_node, NULL_TREE); |
| tree v4hi_ftype_v2si_v2si |
| = build_function_type_list (V4HI_type_node, |
| V2SI_type_node, V2SI_type_node, NULL_TREE); |
| tree v8qi_ftype_v4hi_v8qi |
| = build_function_type_list (V8QI_type_node, |
| V4HI_type_node, V8QI_type_node, NULL_TREE); |
| tree v2si_ftype_v4hi_v4hi |
| = build_function_type_list (V2SI_type_node, |
| V4HI_type_node, V4HI_type_node, NULL_TREE); |
| tree v2si_ftype_v8qi_v8qi |
| = build_function_type_list (V2SI_type_node, |
| V8QI_type_node, V8QI_type_node, NULL_TREE); |
| tree v4hi_ftype_v4hi_di |
| = build_function_type_list (V4HI_type_node, |
| V4HI_type_node, long_long_integer_type_node, |
| NULL_TREE); |
| tree v2si_ftype_v2si_di |
| = build_function_type_list (V2SI_type_node, |
| V2SI_type_node, long_long_integer_type_node, |
| NULL_TREE); |
| tree di_ftype_void |
| = build_function_type_list (long_long_unsigned_type_node, NULL_TREE); |
| tree int_ftype_void |
| = build_function_type_list (integer_type_node, NULL_TREE); |
| tree di_ftype_v8qi |
| = build_function_type_list (long_long_integer_type_node, |
| V8QI_type_node, NULL_TREE); |
| tree di_ftype_v4hi |
| = build_function_type_list (long_long_integer_type_node, |
| V4HI_type_node, NULL_TREE); |
| tree di_ftype_v2si |
| = build_function_type_list (long_long_integer_type_node, |
| V2SI_type_node, NULL_TREE); |
| tree v2si_ftype_v4hi |
| = build_function_type_list (V2SI_type_node, |
| V4HI_type_node, NULL_TREE); |
| tree v4hi_ftype_v8qi |
| = build_function_type_list (V4HI_type_node, |
| V8QI_type_node, NULL_TREE); |
| tree v8qi_ftype_v8qi |
| = build_function_type_list (V8QI_type_node, |
| V8QI_type_node, NULL_TREE); |
| tree v4hi_ftype_v4hi |
| = build_function_type_list (V4HI_type_node, |
| V4HI_type_node, NULL_TREE); |
| tree v2si_ftype_v2si |
| = build_function_type_list (V2SI_type_node, |
| V2SI_type_node, NULL_TREE); |
| |
| tree di_ftype_di_v4hi_v4hi |
| = build_function_type_list (long_long_unsigned_type_node, |
| long_long_unsigned_type_node, |
| V4HI_type_node, V4HI_type_node, |
| NULL_TREE); |
| |
| tree di_ftype_v4hi_v4hi |
| = build_function_type_list (long_long_unsigned_type_node, |
| V4HI_type_node,V4HI_type_node, |
| NULL_TREE); |
| |
| tree v2si_ftype_v2si_v4hi_v4hi |
| = build_function_type_list (V2SI_type_node, |
| V2SI_type_node, V4HI_type_node, |
| V4HI_type_node, NULL_TREE); |
| |
| tree v2si_ftype_v2si_v8qi_v8qi |
| = build_function_type_list (V2SI_type_node, |
| V2SI_type_node, V8QI_type_node, |
| V8QI_type_node, NULL_TREE); |
| |
| tree di_ftype_di_v2si_v2si |
| = build_function_type_list (long_long_unsigned_type_node, |
| long_long_unsigned_type_node, |
| V2SI_type_node, V2SI_type_node, |
| NULL_TREE); |
| |
| tree di_ftype_di_di_int |
| = build_function_type_list (long_long_unsigned_type_node, |
| long_long_unsigned_type_node, |
| long_long_unsigned_type_node, |
| integer_type_node, NULL_TREE); |
| |
| tree void_ftype_int |
| = build_function_type_list (void_type_node, |
| integer_type_node, NULL_TREE); |
| |
| tree v8qi_ftype_char |
| = build_function_type_list (V8QI_type_node, |
| signed_char_type_node, NULL_TREE); |
| |
| tree v4hi_ftype_short |
| = build_function_type_list (V4HI_type_node, |
| short_integer_type_node, NULL_TREE); |
| |
| tree v2si_ftype_int |
| = build_function_type_list (V2SI_type_node, |
| integer_type_node, NULL_TREE); |
| |
| /* Normal vector binops. */ |
| tree v8qi_ftype_v8qi_v8qi |
| = build_function_type_list (V8QI_type_node, |
| V8QI_type_node, V8QI_type_node, NULL_TREE); |
| tree v4hi_ftype_v4hi_v4hi |
| = build_function_type_list (V4HI_type_node, |
| V4HI_type_node,V4HI_type_node, NULL_TREE); |
| tree v2si_ftype_v2si_v2si |
| = build_function_type_list (V2SI_type_node, |
| V2SI_type_node, V2SI_type_node, NULL_TREE); |
| tree di_ftype_di_di |
| = build_function_type_list (long_long_unsigned_type_node, |
| long_long_unsigned_type_node, |
| long_long_unsigned_type_node, |
| NULL_TREE); |
| |
| /* Add all builtins that are more or less simple operations on two |
| operands. */ |
| for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++) |
| { |
| /* Use one of the operands; the target can have a different mode for |
| mask-generating compares. */ |
| machine_mode mode; |
| tree type; |
| |
| if (d->name == 0 || !(d->mask == FL_IWMMXT || d->mask == FL_IWMMXT2)) |
| continue; |
| |
| mode = insn_data[d->icode].operand[1].mode; |
| |
| switch (mode) |
| { |
| case V8QImode: |
| type = v8qi_ftype_v8qi_v8qi; |
| break; |
| case V4HImode: |
| type = v4hi_ftype_v4hi_v4hi; |
| break; |
| case V2SImode: |
| type = v2si_ftype_v2si_v2si; |
| break; |
| case DImode: |
| type = di_ftype_di_di; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| def_mbuiltin (d->mask, d->name, type, d->code); |
| } |
| |
| /* Add the remaining MMX insns with somewhat more complicated types. */ |
| #define iwmmx_mbuiltin(NAME, TYPE, CODE) \ |
| def_mbuiltin (FL_IWMMXT, "__builtin_arm_" NAME, (TYPE), \ |
| ARM_BUILTIN_ ## CODE) |
| |
| #define iwmmx2_mbuiltin(NAME, TYPE, CODE) \ |
| def_mbuiltin (FL_IWMMXT2, "__builtin_arm_" NAME, (TYPE), \ |
| ARM_BUILTIN_ ## CODE) |
| |
| iwmmx_mbuiltin ("wzero", di_ftype_void, WZERO); |
| iwmmx_mbuiltin ("setwcgr0", void_ftype_int, SETWCGR0); |
| iwmmx_mbuiltin ("setwcgr1", void_ftype_int, SETWCGR1); |
| iwmmx_mbuiltin ("setwcgr2", void_ftype_int, SETWCGR2); |
| iwmmx_mbuiltin ("setwcgr3", void_ftype_int, SETWCGR3); |
| iwmmx_mbuiltin ("getwcgr0", int_ftype_void, GETWCGR0); |
| iwmmx_mbuiltin ("getwcgr1", int_ftype_void, GETWCGR1); |
| iwmmx_mbuiltin ("getwcgr2", int_ftype_void, GETWCGR2); |
| iwmmx_mbuiltin ("getwcgr3", int_ftype_void, GETWCGR3); |
| |
| iwmmx_mbuiltin ("wsllh", v4hi_ftype_v4hi_di, WSLLH); |
| iwmmx_mbuiltin ("wsllw", v2si_ftype_v2si_di, WSLLW); |
| iwmmx_mbuiltin ("wslld", di_ftype_di_di, WSLLD); |
| iwmmx_mbuiltin ("wsllhi", v4hi_ftype_v4hi_int, WSLLHI); |
| iwmmx_mbuiltin ("wsllwi", v2si_ftype_v2si_int, WSLLWI); |
| iwmmx_mbuiltin ("wslldi", di_ftype_di_int, WSLLDI); |
| |
| iwmmx_mbuiltin ("wsrlh", v4hi_ftype_v4hi_di, WSRLH); |
| iwmmx_mbuiltin ("wsrlw", v2si_ftype_v2si_di, WSRLW); |
| iwmmx_mbuiltin ("wsrld", di_ftype_di_di, WSRLD); |
| iwmmx_mbuiltin ("wsrlhi", v4hi_ftype_v4hi_int, WSRLHI); |
| iwmmx_mbuiltin ("wsrlwi", v2si_ftype_v2si_int, WSRLWI); |
| iwmmx_mbuiltin ("wsrldi", di_ftype_di_int, WSRLDI); |
| |
| iwmmx_mbuiltin ("wsrah", v4hi_ftype_v4hi_di, WSRAH); |
| iwmmx_mbuiltin ("wsraw", v2si_ftype_v2si_di, WSRAW); |
| iwmmx_mbuiltin ("wsrad", di_ftype_di_di, WSRAD); |
| iwmmx_mbuiltin ("wsrahi", v4hi_ftype_v4hi_int, WSRAHI); |
| iwmmx_mbuiltin ("wsrawi", v2si_ftype_v2si_int, WSRAWI); |
| iwmmx_mbuiltin ("wsradi", di_ftype_di_int, WSRADI); |
| |
| iwmmx_mbuiltin ("wrorh", v4hi_ftype_v4hi_di, WRORH); |
| iwmmx_mbuiltin ("wrorw", v2si_ftype_v2si_di, WRORW); |
| iwmmx_mbuiltin ("wrord", di_ftype_di_di, WRORD); |
| iwmmx_mbuiltin ("wrorhi", v4hi_ftype_v4hi_int, WRORHI); |
| iwmmx_mbuiltin ("wrorwi", v2si_ftype_v2si_int, WRORWI); |
| iwmmx_mbuiltin ("wrordi", di_ftype_di_int, WRORDI); |
| |
| iwmmx_mbuiltin ("wshufh", v4hi_ftype_v4hi_int, WSHUFH); |
| |
| iwmmx_mbuiltin ("wsadb", v2si_ftype_v2si_v8qi_v8qi, WSADB); |
| iwmmx_mbuiltin ("wsadh", v2si_ftype_v2si_v4hi_v4hi, WSADH); |
| iwmmx_mbuiltin ("wmadds", v2si_ftype_v4hi_v4hi, WMADDS); |
| iwmmx2_mbuiltin ("wmaddsx", v2si_ftype_v4hi_v4hi, WMADDSX); |
| iwmmx2_mbuiltin ("wmaddsn", v2si_ftype_v4hi_v4hi, WMADDSN); |
| iwmmx_mbuiltin ("wmaddu", v2si_ftype_v4hi_v4hi, WMADDU); |
| iwmmx2_mbuiltin ("wmaddux", v2si_ftype_v4hi_v4hi, WMADDUX); |
| iwmmx2_mbuiltin ("wmaddun", v2si_ftype_v4hi_v4hi, WMADDUN); |
| iwmmx_mbuiltin ("wsadbz", v2si_ftype_v8qi_v8qi, WSADBZ); |
| iwmmx_mbuiltin ("wsadhz", v2si_ftype_v4hi_v4hi, WSADHZ); |
| |
| iwmmx_mbuiltin ("textrmsb", int_ftype_v8qi_int, TEXTRMSB); |
| iwmmx_mbuiltin ("textrmsh", int_ftype_v4hi_int, TEXTRMSH); |
| iwmmx_mbuiltin ("textrmsw", int_ftype_v2si_int, TEXTRMSW); |
| iwmmx_mbuiltin ("textrmub", int_ftype_v8qi_int, TEXTRMUB); |
| iwmmx_mbuiltin ("textrmuh", int_ftype_v4hi_int, TEXTRMUH); |
| iwmmx_mbuiltin ("textrmuw", int_ftype_v2si_int, TEXTRMUW); |
| iwmmx_mbuiltin ("tinsrb", v8qi_ftype_v8qi_int_int, TINSRB); |
| iwmmx_mbuiltin ("tinsrh", v4hi_ftype_v4hi_int_int, TINSRH); |
| iwmmx_mbuiltin ("tinsrw", v2si_ftype_v2si_int_int, TINSRW); |
| |
| iwmmx_mbuiltin ("waccb", di_ftype_v8qi, WACCB); |
| iwmmx_mbuiltin ("wacch", di_ftype_v4hi, WACCH); |
| iwmmx_mbuiltin ("waccw", di_ftype_v2si, WACCW); |
| |
| iwmmx_mbuiltin ("tmovmskb", int_ftype_v8qi, TMOVMSKB); |
| iwmmx_mbuiltin ("tmovmskh", int_ftype_v4hi, TMOVMSKH); |
| iwmmx_mbuiltin ("tmovmskw", int_ftype_v2si, TMOVMSKW); |
| |
| iwmmx2_mbuiltin ("waddbhusm", v8qi_ftype_v4hi_v8qi, WADDBHUSM); |
| iwmmx2_mbuiltin ("waddbhusl", v8qi_ftype_v4hi_v8qi, WADDBHUSL); |
| |
| iwmmx_mbuiltin ("wpackhss", v8qi_ftype_v4hi_v4hi, WPACKHSS); |
| iwmmx_mbuiltin ("wpackhus", v8qi_ftype_v4hi_v4hi, WPACKHUS); |
| iwmmx_mbuiltin ("wpackwus", v4hi_ftype_v2si_v2si, WPACKWUS); |
| iwmmx_mbuiltin ("wpackwss", v4hi_ftype_v2si_v2si, WPACKWSS); |
| iwmmx_mbuiltin ("wpackdus", v2si_ftype_di_di, WPACKDUS); |
| iwmmx_mbuiltin ("wpackdss", v2si_ftype_di_di, WPACKDSS); |
| |
| iwmmx_mbuiltin ("wunpckehub", v4hi_ftype_v8qi, WUNPCKEHUB); |
| iwmmx_mbuiltin ("wunpckehuh", v2si_ftype_v4hi, WUNPCKEHUH); |
| iwmmx_mbuiltin ("wunpckehuw", di_ftype_v2si, WUNPCKEHUW); |
| iwmmx_mbuiltin ("wunpckehsb", v4hi_ftype_v8qi, WUNPCKEHSB); |
| iwmmx_mbuiltin ("wunpckehsh", v2si_ftype_v4hi, WUNPCKEHSH); |
| iwmmx_mbuiltin ("wunpckehsw", di_ftype_v2si, WUNPCKEHSW); |
| iwmmx_mbuiltin ("wunpckelub", v4hi_ftype_v8qi, WUNPCKELUB); |
| iwmmx_mbuiltin ("wunpckeluh", v2si_ftype_v4hi, WUNPCKELUH); |
| iwmmx_mbuiltin ("wunpckeluw", di_ftype_v2si, WUNPCKELUW); |
| iwmmx_mbuiltin ("wunpckelsb", v4hi_ftype_v8qi, WUNPCKELSB); |
| iwmmx_mbuiltin ("wunpckelsh", v2si_ftype_v4hi, WUNPCKELSH); |
| iwmmx_mbuiltin ("wunpckelsw", di_ftype_v2si, WUNPCKELSW); |
| |
| iwmmx_mbuiltin ("wmacs", di_ftype_di_v4hi_v4hi, WMACS); |
| iwmmx_mbuiltin ("wmacsz", di_ftype_v4hi_v4hi, WMACSZ); |
| iwmmx_mbuiltin ("wmacu", di_ftype_di_v4hi_v4hi, WMACU); |
| iwmmx_mbuiltin ("wmacuz", di_ftype_v4hi_v4hi, WMACUZ); |
| |
| iwmmx_mbuiltin ("walign", v8qi_ftype_v8qi_v8qi_int, WALIGNI); |
| iwmmx_mbuiltin ("tmia", di_ftype_di_int_int, TMIA); |
| iwmmx_mbuiltin ("tmiaph", di_ftype_di_int_int, TMIAPH); |
| iwmmx_mbuiltin ("tmiabb", di_ftype_di_int_int, TMIABB); |
| iwmmx_mbuiltin ("tmiabt", di_ftype_di_int_int, TMIABT); |
| iwmmx_mbuiltin ("tmiatb", di_ftype_di_int_int, TMIATB); |
| iwmmx_mbuiltin ("tmiatt", di_ftype_di_int_int, TMIATT); |
| |
| iwmmx2_mbuiltin ("wabsb", v8qi_ftype_v8qi, WABSB); |
| iwmmx2_mbuiltin ("wabsh", v4hi_ftype_v4hi, WABSH); |
| iwmmx2_mbuiltin ("wabsw", v2si_ftype_v2si, WABSW); |
| |
| iwmmx2_mbuiltin ("wqmiabb", v2si_ftype_v2si_v4hi_v4hi, WQMIABB); |
| iwmmx2_mbuiltin ("wqmiabt", v2si_ftype_v2si_v4hi_v4hi, WQMIABT); |
| iwmmx2_mbuiltin ("wqmiatb", v2si_ftype_v2si_v4hi_v4hi, WQMIATB); |
| iwmmx2_mbuiltin ("wqmiatt", v2si_ftype_v2si_v4hi_v4hi, WQMIATT); |
| |
| iwmmx2_mbuiltin ("wqmiabbn", v2si_ftype_v2si_v4hi_v4hi, WQMIABBN); |
| iwmmx2_mbuiltin ("wqmiabtn", v2si_ftype_v2si_v4hi_v4hi, WQMIABTN); |
| iwmmx2_mbuiltin ("wqmiatbn", v2si_ftype_v2si_v4hi_v4hi, WQMIATBN); |
| iwmmx2_mbuiltin ("wqmiattn", v2si_ftype_v2si_v4hi_v4hi, WQMIATTN); |
| |
| iwmmx2_mbuiltin ("wmiabb", di_ftype_di_v4hi_v4hi, WMIABB); |
| iwmmx2_mbuiltin ("wmiabt", di_ftype_di_v4hi_v4hi, WMIABT); |
| iwmmx2_mbuiltin ("wmiatb", di_ftype_di_v4hi_v4hi, WMIATB); |
| iwmmx2_mbuiltin ("wmiatt", di_ftype_di_v4hi_v4hi, WMIATT); |
| |
| iwmmx2_mbuiltin ("wmiabbn", di_ftype_di_v4hi_v4hi, WMIABBN); |
| iwmmx2_mbuiltin ("wmiabtn", di_ftype_di_v4hi_v4hi, WMIABTN); |
| iwmmx2_mbuiltin ("wmiatbn", di_ftype_di_v4hi_v4hi, WMIATBN); |
| iwmmx2_mbuiltin ("wmiattn", di_ftype_di_v4hi_v4hi, WMIATTN); |
| |
| iwmmx2_mbuiltin ("wmiawbb", di_ftype_di_v2si_v2si, WMIAWBB); |
| iwmmx2_mbuiltin ("wmiawbt", di_ftype_di_v2si_v2si, WMIAWBT); |
| iwmmx2_mbuiltin ("wmiawtb", di_ftype_di_v2si_v2si, WMIAWTB); |
| iwmmx2_mbuiltin ("wmiawtt", di_ftype_di_v2si_v2si, WMIAWTT); |
| |
| iwmmx2_mbuiltin ("wmiawbbn", di_ftype_di_v2si_v2si, WMIAWBBN); |
| iwmmx2_mbuiltin ("wmiawbtn", di_ftype_di_v2si_v2si, WMIAWBTN); |
| iwmmx2_mbuiltin ("wmiawtbn", di_ftype_di_v2si_v2si, WMIAWTBN); |
| iwmmx2_mbuiltin ("wmiawttn", di_ftype_di_v2si_v2si, WMIAWTTN); |
| |
| iwmmx2_mbuiltin ("wmerge", di_ftype_di_di_int, WMERGE); |
| |
| iwmmx_mbuiltin ("tbcstb", v8qi_ftype_char, TBCSTB); |
| iwmmx_mbuiltin ("tbcsth", v4hi_ftype_short, TBCSTH); |
| iwmmx_mbuiltin ("tbcstw", v2si_ftype_int, TBCSTW); |
| |
| #undef iwmmx_mbuiltin |
| #undef iwmmx2_mbuiltin |
| } |
| |
| static void |
| arm_init_fp16_builtins (void) |
| { |
| tree fp16_type = make_node (REAL_TYPE); |
| TYPE_PRECISION (fp16_type) = 16; |
| layout_type (fp16_type); |
| (*lang_hooks.types.register_builtin_type) (fp16_type, "__fp16"); |
| } |
| |
| static void |
| arm_init_crc32_builtins () |
| { |
| tree si_ftype_si_qi |
| = build_function_type_list (unsigned_intSI_type_node, |
| unsigned_intSI_type_node, |
| unsigned_intQI_type_node, NULL_TREE); |
| tree si_ftype_si_hi |
| = build_function_type_list (unsigned_intSI_type_node, |
| unsigned_intSI_type_node, |
| unsigned_intHI_type_node, NULL_TREE); |
| tree si_ftype_si_si |
| = build_function_type_list (unsigned_intSI_type_node, |
| unsigned_intSI_type_node, |
| unsigned_intSI_type_node, NULL_TREE); |
| |
| arm_builtin_decls[ARM_BUILTIN_CRC32B] |
| = add_builtin_function ("__builtin_arm_crc32b", si_ftype_si_qi, |
| ARM_BUILTIN_CRC32B, BUILT_IN_MD, NULL, NULL_TREE); |
| arm_builtin_decls[ARM_BUILTIN_CRC32H] |
| = add_builtin_function ("__builtin_arm_crc32h", si_ftype_si_hi, |
| ARM_BUILTIN_CRC32H, BUILT_IN_MD, NULL, NULL_TREE); |
| arm_builtin_decls[ARM_BUILTIN_CRC32W] |
| = add_builtin_function ("__builtin_arm_crc32w", si_ftype_si_si, |
| ARM_BUILTIN_CRC32W, BUILT_IN_MD, NULL, NULL_TREE); |
| arm_builtin_decls[ARM_BUILTIN_CRC32CB] |
| = add_builtin_function ("__builtin_arm_crc32cb", si_ftype_si_qi, |
| ARM_BUILTIN_CRC32CB, BUILT_IN_MD, NULL, NULL_TREE); |
| arm_builtin_decls[ARM_BUILTIN_CRC32CH] |
| = add_builtin_function ("__builtin_arm_crc32ch", si_ftype_si_hi, |
| ARM_BUILTIN_CRC32CH, BUILT_IN_MD, NULL, NULL_TREE); |
| arm_builtin_decls[ARM_BUILTIN_CRC32CW] |
| = add_builtin_function ("__builtin_arm_crc32cw", si_ftype_si_si, |
| ARM_BUILTIN_CRC32CW, BUILT_IN_MD, NULL, NULL_TREE); |
| } |
| |
| void |
| arm_init_builtins (void) |
| { |
| if (TARGET_REALLY_IWMMXT) |
| arm_init_iwmmxt_builtins (); |
| |
| if (TARGET_NEON) |
| arm_init_neon_builtins (); |
| |
| if (arm_fp16_format) |
| arm_init_fp16_builtins (); |
| |
| if (TARGET_CRC32) |
| arm_init_crc32_builtins (); |
| |
| if (TARGET_VFP && TARGET_HARD_FLOAT) |
| { |
| tree ftype_set_fpscr |
| = build_function_type_list (void_type_node, unsigned_type_node, NULL); |
| tree ftype_get_fpscr |
| = build_function_type_list (unsigned_type_node, NULL); |
| |
| arm_builtin_decls[ARM_BUILTIN_GET_FPSCR] |
| = add_builtin_function ("__builtin_arm_get_fpscr", ftype_get_fpscr, |
| ARM_BUILTIN_GET_FPSCR, BUILT_IN_MD, NULL, NULL_TREE); |
| arm_builtin_decls[ARM_BUILTIN_SET_FPSCR] |
| = add_builtin_function ("__builtin_arm_set_fpscr", ftype_set_fpscr, |
| ARM_BUILTIN_SET_FPSCR, BUILT_IN_MD, NULL, NULL_TREE); |
| } |
| } |
| |
| /* Return the ARM builtin for CODE. */ |
| |
| tree |
| arm_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED) |
| { |
| if (code >= ARM_BUILTIN_MAX) |
| return error_mark_node; |
| |
| return arm_builtin_decls[code]; |
| } |
| |
| /* Errors in the source file can cause expand_expr to return const0_rtx |
| where we expect a vector. To avoid crashing, use one of the vector |
| clear instructions. */ |
| |
| static rtx |
| safe_vector_operand (rtx x, machine_mode mode) |
| { |
| if (x != const0_rtx) |
| return x; |
| x = gen_reg_rtx (mode); |
| |
| emit_insn (gen_iwmmxt_clrdi (mode == DImode ? x |
| : gen_rtx_SUBREG (DImode, x, 0))); |
| return x; |
| } |
| |
| /* Function to expand ternary builtins. */ |
| static rtx |
| arm_expand_ternop_builtin (enum insn_code icode, |
| tree exp, rtx target) |
| { |
| rtx pat; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| tree arg2 = CALL_EXPR_ARG (exp, 2); |
| |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| rtx op2 = expand_normal (arg2); |
| rtx op3 = NULL_RTX; |
| |
| /* The sha1c, sha1p, sha1m crypto builtins require a different vec_select |
| lane operand depending on endianness. */ |
| bool builtin_sha1cpm_p = false; |
| |
| if (insn_data[icode].n_operands == 5) |
| { |
| gcc_assert (icode == CODE_FOR_crypto_sha1c |
| || icode == CODE_FOR_crypto_sha1p |
| || icode == CODE_FOR_crypto_sha1m); |
| builtin_sha1cpm_p = true; |
| } |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| machine_mode mode1 = insn_data[icode].operand[2].mode; |
| machine_mode mode2 = insn_data[icode].operand[3].mode; |
| |
| |
| if (VECTOR_MODE_P (mode0)) |
| op0 = safe_vector_operand (op0, mode0); |
| if (VECTOR_MODE_P (mode1)) |
| op1 = safe_vector_operand (op1, mode1); |
| if (VECTOR_MODE_P (mode2)) |
| op2 = safe_vector_operand (op2, mode2); |
| |
| if (! target |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| gcc_assert ((GET_MODE (op0) == mode0 || GET_MODE (op0) == VOIDmode) |
| && (GET_MODE (op1) == mode1 || GET_MODE (op1) == VOIDmode) |
| && (GET_MODE (op2) == mode2 || GET_MODE (op2) == VOIDmode)); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) |
| op2 = copy_to_mode_reg (mode2, op2); |
| if (builtin_sha1cpm_p) |
| op3 = GEN_INT (TARGET_BIG_END ? 1 : 0); |
| |
| if (builtin_sha1cpm_p) |
| pat = GEN_FCN (icode) (target, op0, op1, op2, op3); |
| else |
| pat = GEN_FCN (icode) (target, op0, op1, op2); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| } |
| |
| /* Subroutine of arm_expand_builtin to take care of binop insns. */ |
| |
| static rtx |
| arm_expand_binop_builtin (enum insn_code icode, |
| tree exp, rtx target) |
| { |
| rtx pat; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| machine_mode mode1 = insn_data[icode].operand[2].mode; |
| |
| if (VECTOR_MODE_P (mode0)) |
| op0 = safe_vector_operand (op0, mode0); |
| if (VECTOR_MODE_P (mode1)) |
| op1 = safe_vector_operand (op1, mode1); |
| |
| if (! target |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| gcc_assert ((GET_MODE (op0) == mode0 || GET_MODE (op0) == VOIDmode) |
| && (GET_MODE (op1) == mode1 || GET_MODE (op1) == VOIDmode)); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| |
| pat = GEN_FCN (icode) (target, op0, op1); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| } |
| |
| /* Subroutine of arm_expand_builtin to take care of unop insns. */ |
| |
| static rtx |
| arm_expand_unop_builtin (enum insn_code icode, |
| tree exp, rtx target, int do_load) |
| { |
| rtx pat; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = NULL_RTX; |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| bool builtin_sha1h_p = false; |
| |
| if (insn_data[icode].n_operands == 3) |
| { |
| gcc_assert (icode == CODE_FOR_crypto_sha1h); |
| builtin_sha1h_p = true; |
| } |
| |
| if (! target |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| if (do_load) |
| op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0)); |
| else |
| { |
| if (VECTOR_MODE_P (mode0)) |
| op0 = safe_vector_operand (op0, mode0); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| } |
| if (builtin_sha1h_p) |
| op1 = GEN_INT (TARGET_BIG_END ? 1 : 0); |
| |
| if (builtin_sha1h_p) |
| pat = GEN_FCN (icode) (target, op0, op1); |
| else |
| pat = GEN_FCN (icode) (target, op0); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| } |
| |
| typedef enum { |
| NEON_ARG_COPY_TO_REG, |
| NEON_ARG_CONSTANT, |
| NEON_ARG_MEMORY, |
| NEON_ARG_STOP |
| } builtin_arg; |
| |
| #define NEON_MAX_BUILTIN_ARGS 5 |
| |
| /* EXP is a pointer argument to a Neon load or store intrinsic. Derive |
| and return an expression for the accessed memory. |
| |
| The intrinsic function operates on a block of registers that has |
| mode REG_MODE. This block contains vectors of type TYPE_MODE. The |
| function references the memory at EXP of type TYPE and in mode |
| MEM_MODE; this mode may be BLKmode if no more suitable mode is |
| available. */ |
| |
| static tree |
| neon_dereference_pointer (tree exp, tree type, machine_mode mem_mode, |
| machine_mode reg_mode, |
| machine_mode vector_mode) |
| { |
| HOST_WIDE_INT reg_size, vector_size, nvectors, nelems; |
| tree elem_type, upper_bound, array_type; |
| |
| /* Work out the size of the register block in bytes. */ |
| reg_size = GET_MODE_SIZE (reg_mode); |
| |
| /* Work out the size of each vector in bytes. */ |
| vector_size = GET_MODE_SIZE (vector_mode); |
| |
| /* Work out how many vectors there are. */ |
| gcc_assert (reg_size % vector_size == 0); |
| nvectors = reg_size / vector_size; |
| |
| /* Work out the type of each element. */ |
| gcc_assert (POINTER_TYPE_P (type)); |
| elem_type = TREE_TYPE (type); |
| |
| /* Work out how many elements are being loaded or stored. |
| MEM_MODE == REG_MODE implies a one-to-one mapping between register |
| and memory elements; anything else implies a lane load or store. */ |
| if (mem_mode == reg_mode) |
| nelems = vector_size * nvectors / int_size_in_bytes (elem_type); |
| else |
| nelems = nvectors; |
| |
| /* Create a type that describes the full access. */ |
| upper_bound = build_int_cst (size_type_node, nelems - 1); |
| array_type = build_array_type (elem_type, build_index_type (upper_bound)); |
| |
| /* Dereference EXP using that type. */ |
| return fold_build2 (MEM_REF, array_type, exp, |
| build_int_cst (build_pointer_type (array_type), 0)); |
| } |
| |
| /* Expand a Neon builtin. */ |
| static rtx |
| arm_expand_neon_args (rtx target, machine_mode map_mode, int fcode, |
| int icode, int have_retval, tree exp, ...) |
| { |
| va_list ap; |
| rtx pat; |
| tree arg[SIMD_MAX_BUILTIN_ARGS]; |
| rtx op[SIMD_MAX_BUILTIN_ARGS]; |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode[SIMD_MAX_BUILTIN_ARGS]; |
| tree formals; |
| 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); |
| |
| formals = TYPE_ARG_TYPES (TREE_TYPE (arm_builtin_decls[fcode])); |
| |
| for (;;) |
| { |
| builtin_arg thisarg = (builtin_arg) va_arg (ap, int); |
| |
| if (thisarg == NEON_ARG_STOP) |
| break; |
| else |
| { |
| int opno = argc + have_retval; |
| arg[argc] = CALL_EXPR_ARG (exp, argc); |
| mode[argc] = insn_data[icode].operand[opno].mode; |
| if (thisarg == NEON_ARG_MEMORY) |
| { |
| machine_mode other_mode |
| = insn_data[icode].operand[1 - opno].mode; |
| arg[argc] = neon_dereference_pointer (arg[argc], |
| TREE_VALUE (formals), |
| mode[argc], other_mode, |
| map_mode); |
| } |
| |
| /* Use EXPAND_MEMORY for NEON_ARG_MEMORY to ensure a MEM_P |
| be returned. */ |
| op[argc] = expand_expr (arg[argc], NULL_RTX, VOIDmode, |
| (thisarg == NEON_ARG_MEMORY |
| ? EXPAND_MEMORY : EXPAND_NORMAL)); |
| |
| switch (thisarg) |
| { |
| case NEON_ARG_COPY_TO_REG: |
| if (POINTER_TYPE_P (TREE_TYPE (arg[argc]))) |
| op[argc] = convert_memory_address (Pmode, op[argc]); |
| /*gcc_assert (GET_MODE (op[argc]) == mode[argc]); */ |
| if (!(*insn_data[icode].operand[opno].predicate) |
| (op[argc], mode[argc])) |
| op[argc] = copy_to_mode_reg (mode[argc], op[argc]); |
| break; |
| |
| case NEON_ARG_CONSTANT: |
| if (!(*insn_data[icode].operand[opno].predicate) |
| (op[argc], mode[argc])) |
| error_at (EXPR_LOCATION (exp), "incompatible type for argument %d, " |
| "expected %<const int%>", argc + 1); |
| break; |
| case NEON_ARG_MEMORY: |
| /* Check if expand failed. */ |
| if (op[argc] == const0_rtx) |
| { |
| va_end (ap); |
| return 0; |
| } |
| gcc_assert (MEM_P (op[argc])); |
| PUT_MODE (op[argc], mode[argc]); |
| /* ??? arm_neon.h uses the same built-in functions for signed |
| and unsigned accesses, casting where necessary. This isn't |
| alias safe. */ |
| set_mem_alias_set (op[argc], 0); |
| if (!(*insn_data[icode].operand[opno].predicate) |
| (op[argc], mode[argc])) |
| op[argc] = (replace_equiv_address |
| (op[argc], |
| copy_to_mode_reg (Pmode, XEXP (op[argc], 0)))); |
| break; |
| |
| case NEON_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 a Neon builtin. These are "special" because they don't have symbolic |
| constants defined per-instruction or per instruction-variant. Instead, the |
| required info is looked up in the table neon_builtin_data. */ |
| static rtx |
| arm_expand_neon_builtin (int fcode, tree exp, rtx target) |
| { |
| neon_builtin_datum *d = |
| &neon_builtin_data[fcode - ARM_BUILTIN_NEON_BASE]; |
| enum insn_code icode = d->code; |
| builtin_arg args[SIMD_MAX_BUILTIN_ARGS + 1]; |
| int num_args = insn_data[d->code].n_operands; |
| int is_void = 0; |
| int k; |
| |
| is_void = !!(d->qualifiers[0] & qualifier_void); |
| |
| num_args += is_void; |
| |
| for (k = 1; k < num_args; k++) |
| { |
| /* We have four arrays of data, each indexed in a different fashion. |
| qualifiers - element 0 always describes the function return type. |
| operands - element 0 is either the operand for return value (if |
| the function has a non-void return type) or the operand for the |
| first argument. |
| expr_args - element 0 always holds the first argument. |
| args - element 0 is always used for the return type. */ |
| int qualifiers_k = k; |
| int operands_k = k - is_void; |
| int expr_args_k = k - 1; |
| |
| if (d->qualifiers[qualifiers_k] & qualifier_immediate) |
| args[k] = NEON_ARG_CONSTANT; |
| else if (d->qualifiers[qualifiers_k] & qualifier_maybe_immediate) |
| { |
| rtx arg |
| = expand_normal (CALL_EXPR_ARG (exp, |
| (expr_args_k))); |
| /* Handle constants only if the predicate allows it. */ |
| bool op_const_int_p = |
| (CONST_INT_P (arg) |
| && (*insn_data[icode].operand[operands_k].predicate) |
| (arg, insn_data[icode].operand[operands_k].mode)); |
| args[k] = op_const_int_p ? NEON_ARG_CONSTANT : NEON_ARG_COPY_TO_REG; |
| } |
| else if (d->qualifiers[qualifiers_k] & qualifier_pointer) |
| args[k] = NEON_ARG_MEMORY; |
| else |
| args[k] = NEON_ARG_COPY_TO_REG; |
| } |
| args[k] = NEON_ARG_STOP; |
| |
| /* The interface to arm_expand_neon_args expects a 0 if |
| the function is void, and a 1 if it is not. */ |
| return arm_expand_neon_args |
| (target, d->mode, fcode, icode, !is_void, exp, |
| args[1], |
| args[2], |
| args[3], |
| args[4], |
| NEON_ARG_STOP); |
| } |
| |
| /* Expand an expression EXP that calls a built-in function, |
| with result going to TARGET if that's convenient |
| (and in mode MODE if that's convenient). |
| SUBTARGET may be used as the target for computing one of EXP's operands. |
| IGNORE is nonzero if the value is to be ignored. */ |
| |
| rtx |
| arm_expand_builtin (tree exp, |
| rtx target, |
| rtx subtarget ATTRIBUTE_UNUSED, |
| machine_mode mode ATTRIBUTE_UNUSED, |
| int ignore ATTRIBUTE_UNUSED) |
| { |
| const struct builtin_description * d; |
| enum insn_code icode; |
| tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); |
| tree arg0; |
| tree arg1; |
| tree arg2; |
| rtx op0; |
| rtx op1; |
| rtx op2; |
| rtx pat; |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| size_t i; |
| machine_mode tmode; |
| machine_mode mode0; |
| machine_mode mode1; |
| machine_mode mode2; |
| int opint; |
| int selector; |
| int mask; |
| int imm; |
| |
| if (fcode >= ARM_BUILTIN_NEON_BASE) |
| return arm_expand_neon_builtin (fcode, exp, target); |
| |
| switch (fcode) |
| { |
| case ARM_BUILTIN_GET_FPSCR: |
| case ARM_BUILTIN_SET_FPSCR: |
| if (fcode == ARM_BUILTIN_GET_FPSCR) |
| { |
| icode = CODE_FOR_get_fpscr; |
| target = gen_reg_rtx (SImode); |
| pat = GEN_FCN (icode) (target); |
| } |
| else |
| { |
| target = NULL_RTX; |
| icode = CODE_FOR_set_fpscr; |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| op0 = expand_normal (arg0); |
| pat = GEN_FCN (icode) (op0); |
| } |
| emit_insn (pat); |
| return target; |
| |
| case ARM_BUILTIN_TEXTRMSB: |
| case ARM_BUILTIN_TEXTRMUB: |
| case ARM_BUILTIN_TEXTRMSH: |
| case ARM_BUILTIN_TEXTRMUH: |
| case ARM_BUILTIN_TEXTRMSW: |
| case ARM_BUILTIN_TEXTRMUW: |
| icode = (fcode == ARM_BUILTIN_TEXTRMSB ? CODE_FOR_iwmmxt_textrmsb |
| : fcode == ARM_BUILTIN_TEXTRMUB ? CODE_FOR_iwmmxt_textrmub |
| : fcode == ARM_BUILTIN_TEXTRMSH ? CODE_FOR_iwmmxt_textrmsh |
| : fcode == ARM_BUILTIN_TEXTRMUH ? CODE_FOR_iwmmxt_textrmuh |
| : CODE_FOR_iwmmxt_textrmw); |
| |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| tmode = insn_data[icode].operand[0].mode; |
| mode0 = insn_data[icode].operand[1].mode; |
| mode1 = insn_data[icode].operand[2].mode; |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| { |
| /* @@@ better error message */ |
| error ("selector must be an immediate"); |
| return gen_reg_rtx (tmode); |
| } |
| |
| opint = INTVAL (op1); |
| if (fcode == ARM_BUILTIN_TEXTRMSB || fcode == ARM_BUILTIN_TEXTRMUB) |
| { |
| if (opint > 7 || opint < 0) |
| error ("the range of selector should be in 0 to 7"); |
| } |
| else if (fcode == ARM_BUILTIN_TEXTRMSH || fcode == ARM_BUILTIN_TEXTRMUH) |
| { |
| if (opint > 3 || opint < 0) |
| error ("the range of selector should be in 0 to 3"); |
| } |
| else /* ARM_BUILTIN_TEXTRMSW || ARM_BUILTIN_TEXTRMUW. */ |
| { |
| if (opint > 1 || opint < 0) |
| error ("the range of selector should be in 0 to 1"); |
| } |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| pat = GEN_FCN (icode) (target, op0, op1); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| |
| case ARM_BUILTIN_WALIGNI: |
| /* If op2 is immediate, call walighi, else call walighr. */ |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| arg2 = CALL_EXPR_ARG (exp, 2); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| op2 = expand_normal (arg2); |
| if (CONST_INT_P (op2)) |
| { |
| icode = CODE_FOR_iwmmxt_waligni; |
| tmode = insn_data[icode].operand[0].mode; |
| mode0 = insn_data[icode].operand[1].mode; |
| mode1 = insn_data[icode].operand[2].mode; |
| mode2 = insn_data[icode].operand[3].mode; |
| if (!(*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (!(*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| gcc_assert ((*insn_data[icode].operand[3].predicate) (op2, mode2)); |
| selector = INTVAL (op2); |
| if (selector > 7 || selector < 0) |
| error ("the range of selector should be in 0 to 7"); |
| } |
| else |
| { |
| icode = CODE_FOR_iwmmxt_walignr; |
| tmode = insn_data[icode].operand[0].mode; |
| mode0 = insn_data[icode].operand[1].mode; |
| mode1 = insn_data[icode].operand[2].mode; |
| mode2 = insn_data[icode].operand[3].mode; |
| if (!(*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (!(*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| if (!(*insn_data[icode].operand[3].predicate) (op2, mode2)) |
| op2 = copy_to_mode_reg (mode2, op2); |
| } |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || !(*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| pat = GEN_FCN (icode) (target, op0, op1, op2); |
| if (!pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| |
| case ARM_BUILTIN_TINSRB: |
| case ARM_BUILTIN_TINSRH: |
| case ARM_BUILTIN_TINSRW: |
| case ARM_BUILTIN_WMERGE: |
| icode = (fcode == ARM_BUILTIN_TINSRB ? CODE_FOR_iwmmxt_tinsrb |
| : fcode == ARM_BUILTIN_TINSRH ? CODE_FOR_iwmmxt_tinsrh |
| : fcode == ARM_BUILTIN_WMERGE ? CODE_FOR_iwmmxt_wmerge |
| : CODE_FOR_iwmmxt_tinsrw); |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| arg2 = CALL_EXPR_ARG (exp, 2); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| op2 = expand_normal (arg2); |
| tmode = insn_data[icode].operand[0].mode; |
| mode0 = insn_data[icode].operand[1].mode; |
| mode1 = insn_data[icode].operand[2].mode; |
| mode2 = insn_data[icode].operand[3].mode; |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) |
| { |
| error ("selector must be an immediate"); |
| return const0_rtx; |
| } |
| if (icode == CODE_FOR_iwmmxt_wmerge) |
| { |
| selector = INTVAL (op2); |
| if (selector > 7 || selector < 0) |
| error ("the range of selector should be in 0 to 7"); |
| } |
| if ((icode == CODE_FOR_iwmmxt_tinsrb) |
| || (icode == CODE_FOR_iwmmxt_tinsrh) |
| || (icode == CODE_FOR_iwmmxt_tinsrw)) |
| { |
| mask = 0x01; |
| selector= INTVAL (op2); |
| if (icode == CODE_FOR_iwmmxt_tinsrb && (selector < 0 || selector > 7)) |
| error ("the range of selector should be in 0 to 7"); |
| else if (icode == CODE_FOR_iwmmxt_tinsrh && (selector < 0 ||selector > 3)) |
| error ("the range of selector should be in 0 to 3"); |
| else if (icode == CODE_FOR_iwmmxt_tinsrw && (selector < 0 ||selector > 1)) |
| error ("the range of selector should be in 0 to 1"); |
| mask <<= selector; |
| op2 = GEN_INT (mask); |
| } |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| pat = GEN_FCN (icode) (target, op0, op1, op2); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| |
| case ARM_BUILTIN_SETWCGR0: |
| case ARM_BUILTIN_SETWCGR1: |
| case ARM_BUILTIN_SETWCGR2: |
| case ARM_BUILTIN_SETWCGR3: |
| icode = (fcode == ARM_BUILTIN_SETWCGR0 ? CODE_FOR_iwmmxt_setwcgr0 |
| : fcode == ARM_BUILTIN_SETWCGR1 ? CODE_FOR_iwmmxt_setwcgr1 |
| : fcode == ARM_BUILTIN_SETWCGR2 ? CODE_FOR_iwmmxt_setwcgr2 |
| : CODE_FOR_iwmmxt_setwcgr3); |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| op0 = expand_normal (arg0); |
| mode0 = insn_data[icode].operand[0].mode; |
| if (!(*insn_data[icode].operand[0].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| pat = GEN_FCN (icode) (op0); |
| if (!pat) |
| return 0; |
| emit_insn (pat); |
| return 0; |
| |
| case ARM_BUILTIN_GETWCGR0: |
| case ARM_BUILTIN_GETWCGR1: |
| case ARM_BUILTIN_GETWCGR2: |
| case ARM_BUILTIN_GETWCGR3: |
| icode = (fcode == ARM_BUILTIN_GETWCGR0 ? CODE_FOR_iwmmxt_getwcgr0 |
| : fcode == ARM_BUILTIN_GETWCGR1 ? CODE_FOR_iwmmxt_getwcgr1 |
| : fcode == ARM_BUILTIN_GETWCGR2 ? CODE_FOR_iwmmxt_getwcgr2 |
| : CODE_FOR_iwmmxt_getwcgr3); |
| tmode = insn_data[icode].operand[0].mode; |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || !(*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| pat = GEN_FCN (icode) (target); |
| if (!pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| |
| case ARM_BUILTIN_WSHUFH: |
| icode = CODE_FOR_iwmmxt_wshufh; |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| tmode = insn_data[icode].operand[0].mode; |
| mode1 = insn_data[icode].operand[1].mode; |
| mode2 = insn_data[icode].operand[2].mode; |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode1)) |
| op0 = copy_to_mode_reg (mode1, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode2)) |
| { |
| error ("mask must be an immediate"); |
| return const0_rtx; |
| } |
| selector = INTVAL (op1); |
| if (selector < 0 || selector > 255) |
| error ("the range of mask should be in 0 to 255"); |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| pat = GEN_FCN (icode) (target, op0, op1); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| |
| case ARM_BUILTIN_WMADDS: |
| return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wmadds, exp, target); |
| case ARM_BUILTIN_WMADDSX: |
| return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wmaddsx, exp, target); |
| case ARM_BUILTIN_WMADDSN: |
| return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wmaddsn, exp, target); |
| case ARM_BUILTIN_WMADDU: |
| return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wmaddu, exp, target); |
| case ARM_BUILTIN_WMADDUX: |
| return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wmaddux, exp, target); |
| case ARM_BUILTIN_WMADDUN: |
| return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wmaddun, exp, target); |
| case ARM_BUILTIN_WSADBZ: |
| return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadbz, exp, target); |
| case ARM_BUILTIN_WSADHZ: |
| return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadhz, exp, target); |
| |
| /* Several three-argument builtins. */ |
| case ARM_BUILTIN_WMACS: |
| case ARM_BUILTIN_WMACU: |
| case ARM_BUILTIN_TMIA: |
| case ARM_BUILTIN_TMIAPH: |
| case ARM_BUILTIN_TMIATT: |
| case ARM_BUILTIN_TMIATB: |
| case ARM_BUILTIN_TMIABT: |
| case ARM_BUILTIN_TMIABB: |
| case ARM_BUILTIN_WQMIABB: |
| case ARM_BUILTIN_WQMIABT: |
| case ARM_BUILTIN_WQMIATB: |
| case ARM_BUILTIN_WQMIATT: |
| case ARM_BUILTIN_WQMIABBN: |
| case ARM_BUILTIN_WQMIABTN: |
| case ARM_BUILTIN_WQMIATBN: |
| case ARM_BUILTIN_WQMIATTN: |
| case ARM_BUILTIN_WMIABB: |
| case ARM_BUILTIN_WMIABT: |
| case ARM_BUILTIN_WMIATB: |
| case ARM_BUILTIN_WMIATT: |
| case ARM_BUILTIN_WMIABBN: |
| case ARM_BUILTIN_WMIABTN: |
| case ARM_BUILTIN_WMIATBN: |
| case ARM_BUILTIN_WMIATTN: |
| case ARM_BUILTIN_WMIAWBB: |
| case ARM_BUILTIN_WMIAWBT: |
| case ARM_BUILTIN_WMIAWTB: |
| case ARM_BUILTIN_WMIAWTT: |
| case ARM_BUILTIN_WMIAWBBN: |
| case ARM_BUILTIN_WMIAWBTN: |
| case ARM_BUILTIN_WMIAWTBN: |
| case ARM_BUILTIN_WMIAWTTN: |
| case ARM_BUILTIN_WSADB: |
| case ARM_BUILTIN_WSADH: |
| icode = (fcode == ARM_BUILTIN_WMACS ? CODE_FOR_iwmmxt_wmacs |
| : fcode == ARM_BUILTIN_WMACU ? CODE_FOR_iwmmxt_wmacu |
| : fcode == ARM_BUILTIN_TMIA ? CODE_FOR_iwmmxt_tmia |
| : fcode == ARM_BUILTIN_TMIAPH ? CODE_FOR_iwmmxt_tmiaph |
| : fcode == ARM_BUILTIN_TMIABB ? CODE_FOR_iwmmxt_tmiabb |
| : fcode == ARM_BUILTIN_TMIABT ? CODE_FOR_iwmmxt_tmiabt |
| : fcode == ARM_BUILTIN_TMIATB ? CODE_FOR_iwmmxt_tmiatb |
| : fcode == ARM_BUILTIN_TMIATT ? CODE_FOR_iwmmxt_tmiatt |
| : fcode == ARM_BUILTIN_WQMIABB ? CODE_FOR_iwmmxt_wqmiabb |
| : fcode == ARM_BUILTIN_WQMIABT ? CODE_FOR_iwmmxt_wqmiabt |
| : fcode == ARM_BUILTIN_WQMIATB ? CODE_FOR_iwmmxt_wqmiatb |
| : fcode == ARM_BUILTIN_WQMIATT ? CODE_FOR_iwmmxt_wqmiatt |
| : fcode == ARM_BUILTIN_WQMIABBN ? CODE_FOR_iwmmxt_wqmiabbn |
| : fcode == ARM_BUILTIN_WQMIABTN ? CODE_FOR_iwmmxt_wqmiabtn |
| : fcode == ARM_BUILTIN_WQMIATBN ? CODE_FOR_iwmmxt_wqmiatbn |
| : fcode == ARM_BUILTIN_WQMIATTN ? CODE_FOR_iwmmxt_wqmiattn |
| : fcode == ARM_BUILTIN_WMIABB ? CODE_FOR_iwmmxt_wmiabb |
| : fcode == ARM_BUILTIN_WMIABT ? CODE_FOR_iwmmxt_wmiabt |
| : fcode == ARM_BUILTIN_WMIATB ? CODE_FOR_iwmmxt_wmiatb |
| : fcode == ARM_BUILTIN_WMIATT ? CODE_FOR_iwmmxt_wmiatt |
| : fcode == ARM_BUILTIN_WMIABBN ? CODE_FOR_iwmmxt_wmiabbn |
| : fcode == ARM_BUILTIN_WMIABTN ? CODE_FOR_iwmmxt_wmiabtn |
| : fcode == ARM_BUILTIN_WMIATBN ? CODE_FOR_iwmmxt_wmiatbn |
| : fcode == ARM_BUILTIN_WMIATTN ? CODE_FOR_iwmmxt_wmiattn |
| : fcode == ARM_BUILTIN_WMIAWBB ? CODE_FOR_iwmmxt_wmiawbb |
| : fcode == ARM_BUILTIN_WMIAWBT ? CODE_FOR_iwmmxt_wmiawbt |
| : fcode == ARM_BUILTIN_WMIAWTB ? CODE_FOR_iwmmxt_wmiawtb |
| : fcode == ARM_BUILTIN_WMIAWTT ? CODE_FOR_iwmmxt_wmiawtt |
| : fcode == ARM_BUILTIN_WMIAWBBN ? CODE_FOR_iwmmxt_wmiawbbn |
| : fcode == ARM_BUILTIN_WMIAWBTN ? CODE_FOR_iwmmxt_wmiawbtn |
| : fcode == ARM_BUILTIN_WMIAWTBN ? CODE_FOR_iwmmxt_wmiawtbn |
| : fcode == ARM_BUILTIN_WMIAWTTN ? CODE_FOR_iwmmxt_wmiawttn |
| : fcode == ARM_BUILTIN_WSADB ? CODE_FOR_iwmmxt_wsadb |
| : CODE_FOR_iwmmxt_wsadh); |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| arg2 = CALL_EXPR_ARG (exp, 2); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| op2 = expand_normal (arg2); |
| tmode = insn_data[icode].operand[0].mode; |
| mode0 = insn_data[icode].operand[1].mode; |
| mode1 = insn_data[icode].operand[2].mode; |
| mode2 = insn_data[icode].operand[3].mode; |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) |
| op2 = copy_to_mode_reg (mode2, op2); |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| pat = GEN_FCN (icode) (target, op0, op1, op2); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| |
| case ARM_BUILTIN_WZERO: |
| target = gen_reg_rtx (DImode); |
| emit_insn (gen_iwmmxt_clrdi (target)); |
| return target; |
| |
| case ARM_BUILTIN_WSRLHI: |
| case ARM_BUILTIN_WSRLWI: |
| case ARM_BUILTIN_WSRLDI: |
| case ARM_BUILTIN_WSLLHI: |
| case ARM_BUILTIN_WSLLWI: |
| case ARM_BUILTIN_WSLLDI: |
| case ARM_BUILTIN_WSRAHI: |
| case ARM_BUILTIN_WSRAWI: |
| case ARM_BUILTIN_WSRADI: |
| case ARM_BUILTIN_WRORHI: |
| case ARM_BUILTIN_WRORWI: |
| case ARM_BUILTIN_WRORDI: |
| case ARM_BUILTIN_WSRLH: |
| case ARM_BUILTIN_WSRLW: |
| case ARM_BUILTIN_WSRLD: |
| case ARM_BUILTIN_WSLLH: |
| case ARM_BUILTIN_WSLLW: |
| case ARM_BUILTIN_WSLLD: |
| case ARM_BUILTIN_WSRAH: |
| case ARM_BUILTIN_WSRAW: |
| case ARM_BUILTIN_WSRAD: |
| case ARM_BUILTIN_WRORH: |
| case ARM_BUILTIN_WRORW: |
| case ARM_BUILTIN_WRORD: |
| icode = (fcode == ARM_BUILTIN_WSRLHI ? CODE_FOR_lshrv4hi3_iwmmxt |
| : fcode == ARM_BUILTIN_WSRLWI ? CODE_FOR_lshrv2si3_iwmmxt |
| : fcode == ARM_BUILTIN_WSRLDI ? CODE_FOR_lshrdi3_iwmmxt |
| : fcode == ARM_BUILTIN_WSLLHI ? CODE_FOR_ashlv4hi3_iwmmxt |
| : fcode == ARM_BUILTIN_WSLLWI ? CODE_FOR_ashlv2si3_iwmmxt |
| : fcode == ARM_BUILTIN_WSLLDI ? CODE_FOR_ashldi3_iwmmxt |
| : fcode == ARM_BUILTIN_WSRAHI ? CODE_FOR_ashrv4hi3_iwmmxt |
| : fcode == ARM_BUILTIN_WSRAWI ? CODE_FOR_ashrv2si3_iwmmxt |
| : fcode == ARM_BUILTIN_WSRADI ? CODE_FOR_ashrdi3_iwmmxt |
| : fcode == ARM_BUILTIN_WRORHI ? CODE_FOR_rorv4hi3 |
| : fcode == ARM_BUILTIN_WRORWI ? CODE_FOR_rorv2si3 |
| : fcode == ARM_BUILTIN_WRORDI ? CODE_FOR_rordi3 |
| : fcode == ARM_BUILTIN_WSRLH ? CODE_FOR_lshrv4hi3_di |
| : fcode == ARM_BUILTIN_WSRLW ? CODE_FOR_lshrv2si3_di |
| : fcode == ARM_BUILTIN_WSRLD ? CODE_FOR_lshrdi3_di |
| : fcode == ARM_BUILTIN_WSLLH ? CODE_FOR_ashlv4hi3_di |
| : fcode == ARM_BUILTIN_WSLLW ? CODE_FOR_ashlv2si3_di |
| : fcode == ARM_BUILTIN_WSLLD ? CODE_FOR_ashldi3_di |
| : fcode == ARM_BUILTIN_WSRAH ? CODE_FOR_ashrv4hi3_di |
| : fcode == ARM_BUILTIN_WSRAW ? CODE_FOR_ashrv2si3_di |
| : fcode == ARM_BUILTIN_WSRAD ? CODE_FOR_ashrdi3_di |
| : fcode == ARM_BUILTIN_WRORH ? CODE_FOR_rorv4hi3_di |
| : fcode == ARM_BUILTIN_WRORW ? CODE_FOR_rorv2si3_di |
| : fcode == ARM_BUILTIN_WRORD ? CODE_FOR_rordi3_di |
| : CODE_FOR_nothing); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| op1 = expand_normal (arg1); |
| if (GET_MODE (op1) == VOIDmode) |
| { |
| imm = INTVAL (op1); |
| if ((fcode == ARM_BUILTIN_WRORHI || fcode == ARM_BUILTIN_WRORWI |
| || fcode == ARM_BUILTIN_WRORH || fcode == ARM_BUILTIN_WRORW) |
| && (imm < 0 || imm > 32)) |
| { |
| if (fcode == ARM_BUILTIN_WRORHI) |
| error ("the range of count should be in 0 to 32. please check the intrinsic _mm_rori_pi16 in code."); |
| else if (fcode == ARM_BUILTIN_WRORWI) |
| error ("the range of count should be in 0 to 32. please check the intrinsic _mm_rori_pi32 in code."); |
| else if (fcode == ARM_BUILTIN_WRORH) |
| error ("the range of count should be in 0 to 32. please check the intrinsic _mm_ror_pi16 in code."); |
| else |
| error ("the range of count should be in 0 to 32. please check the intrinsic _mm_ror_pi32 in code."); |
| } |
| else if ((fcode == ARM_BUILTIN_WRORDI || fcode == ARM_BUILTIN_WRORD) |
| && (imm < 0 || imm > 64)) |
| { |
| if (fcode == ARM_BUILTIN_WRORDI) |
| error ("the range of count should be in 0 to 64. please check the intrinsic _mm_rori_si64 in code."); |
| else |
| error ("the range of count should be in 0 to 64. please check the intrinsic _mm_ror_si64 in code."); |
| } |
| else if (imm < 0) |
| { |
| if (fcode == ARM_BUILTIN_WSRLHI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srli_pi16 in code."); |
| else if (fcode == ARM_BUILTIN_WSRLWI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srli_pi32 in code."); |
| else if (fcode == ARM_BUILTIN_WSRLDI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srli_si64 in code."); |
| else if (fcode == ARM_BUILTIN_WSLLHI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_slli_pi16 in code."); |
| else if (fcode == ARM_BUILTIN_WSLLWI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_slli_pi32 in code."); |
| else if (fcode == ARM_BUILTIN_WSLLDI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_slli_si64 in code."); |
| else if (fcode == ARM_BUILTIN_WSRAHI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srai_pi16 in code."); |
| else if (fcode == ARM_BUILTIN_WSRAWI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srai_pi32 in code."); |
| else if (fcode == ARM_BUILTIN_WSRADI) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srai_si64 in code."); |
| else if (fcode == ARM_BUILTIN_WSRLH) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srl_pi16 in code."); |
| else if (fcode == ARM_BUILTIN_WSRLW) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srl_pi32 in code."); |
| else if (fcode == ARM_BUILTIN_WSRLD) |
| error ("the count should be no less than 0. please check the intrinsic _mm_srl_si64 in code."); |
| else if (fcode == ARM_BUILTIN_WSLLH) |
| error ("the count should be no less than 0. please check the intrinsic _mm_sll_pi16 in code."); |
| else if (fcode == ARM_BUILTIN_WSLLW) |
| error ("the count should be no less than 0. please check the intrinsic _mm_sll_pi32 in code."); |
| else if (fcode == ARM_BUILTIN_WSLLD) |
| error ("the count should be no less than 0. please check the intrinsic _mm_sll_si64 in code."); |
| else if (fcode == ARM_BUILTIN_WSRAH) |
| error ("the count should be no less than 0. please check the intrinsic _mm_sra_pi16 in code."); |
| else if (fcode == ARM_BUILTIN_WSRAW) |
| error ("the count should be no less than 0. please check the intrinsic _mm_sra_pi32 in code."); |
| else |
| error ("the count should be no less than 0. please check the intrinsic _mm_sra_si64 in code."); |
| } |
| } |
| return arm_expand_binop_builtin (icode, exp, target); |
| |
| default: |
| break; |
| } |
| |
| for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++) |
| if (d->code == (const enum arm_builtins) fcode) |
| return arm_expand_binop_builtin (d->icode, exp, target); |
| |
| for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++) |
| if (d->code == (const enum arm_builtins) fcode) |
| return arm_expand_unop_builtin (d->icode, exp, target, 0); |
| |
| for (i = 0, d = bdesc_3arg; i < ARRAY_SIZE (bdesc_3arg); i++, d++) |
| if (d->code == (const enum arm_builtins) fcode) |
| return arm_expand_ternop_builtin (d->icode, exp, target); |
| |
| /* @@@ Should really do something sensible here. */ |
| return NULL_RTX; |
| } |
| |
| tree |
| arm_builtin_vectorized_function (tree fndecl, tree type_out, tree type_in) |
| { |
| machine_mode in_mode, out_mode; |
| int in_n, out_n; |
| bool out_unsigned_p = TYPE_UNSIGNED (type_out); |
| |
| 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); |
| |
| /* ARM_CHECK_BUILTIN_MODE and ARM_FIND_VRINT_VARIANT are used to find the |
| decl of the vectorized builtin for the appropriate vector mode. |
| NULL_TREE is returned if no such builtin is available. */ |
| #undef ARM_CHECK_BUILTIN_MODE |
| #define ARM_CHECK_BUILTIN_MODE(C) \ |
| (TARGET_NEON && TARGET_FPU_ARMV8 \ |
| && flag_unsafe_math_optimizations \ |
| && ARM_CHECK_BUILTIN_MODE_1 (C)) |
| |
| #undef ARM_CHECK_BUILTIN_MODE_1 |
| #define ARM_CHECK_BUILTIN_MODE_1(C) \ |
| (out_mode == SFmode && out_n == C \ |
| && in_mode == SFmode && in_n == C) |
| |
| #undef ARM_FIND_VRINT_VARIANT |
| #define ARM_FIND_VRINT_VARIANT(N) \ |
| (ARM_CHECK_BUILTIN_MODE (2) \ |
| ? arm_builtin_decl(ARM_BUILTIN_NEON_##N##v2sf, false) \ |
| : (ARM_CHECK_BUILTIN_MODE (4) \ |
| ? arm_builtin_decl(ARM_BUILTIN_NEON_##N##v4sf, false) \ |
| : NULL_TREE)) |
| |
| if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) |
| { |
| enum built_in_function fn = DECL_FUNCTION_CODE (fndecl); |
| switch (fn) |
| { |
| case BUILT_IN_FLOORF: |
| return ARM_FIND_VRINT_VARIANT (vrintm); |
| case BUILT_IN_CEILF: |
| return ARM_FIND_VRINT_VARIANT (vrintp); |
| case BUILT_IN_TRUNCF: |
| return ARM_FIND_VRINT_VARIANT (vrintz); |
| case BUILT_IN_ROUNDF: |
| return ARM_FIND_VRINT_VARIANT (vrinta); |
| #undef ARM_CHECK_BUILTIN_MODE_1 |
| #define ARM_CHECK_BUILTIN_MODE_1(C) \ |
| (out_mode == SImode && out_n == C \ |
| && in_mode == SFmode && in_n == C) |
| |
| #define ARM_FIND_VCVT_VARIANT(N) \ |
| (ARM_CHECK_BUILTIN_MODE (2) \ |
| ? arm_builtin_decl(ARM_BUILTIN_NEON_##N##v2sfv2si, false) \ |
| : (ARM_CHECK_BUILTIN_MODE (4) \ |
| ? arm_builtin_decl(ARM_BUILTIN_NEON_##N##v4sfv4si, false) \ |
| : NULL_TREE)) |
| |
| #define ARM_FIND_VCVTU_VARIANT(N) \ |
| (ARM_CHECK_BUILTIN_MODE (2) \ |
| ? arm_builtin_decl(ARM_BUILTIN_NEON_##N##uv2sfv2si, false) \ |
| : (ARM_CHECK_BUILTIN_MODE (4) \ |
| ? arm_builtin_decl(ARM_BUILTIN_NEON_##N##uv4sfv4si, false) \ |
| : NULL_TREE)) |
| case BUILT_IN_LROUNDF: |
| return out_unsigned_p |
| ? ARM_FIND_VCVTU_VARIANT (vcvta) |
| : ARM_FIND_VCVT_VARIANT (vcvta); |
| case BUILT_IN_LCEILF: |
| return out_unsigned_p |
| ? ARM_FIND_VCVTU_VARIANT (vcvtp) |
| : ARM_FIND_VCVT_VARIANT (vcvtp); |
| case BUILT_IN_LFLOORF: |
| return out_unsigned_p |
| ? ARM_FIND_VCVTU_VARIANT (vcvtm) |
| : ARM_FIND_VCVT_VARIANT (vcvtm); |
| #undef ARM_CHECK_BUILTIN_MODE |
| #define ARM_CHECK_BUILTIN_MODE(C, N) \ |
| (out_mode == N##mode && out_n == C \ |
| && in_mode == N##mode && in_n == C) |
| case BUILT_IN_BSWAP16: |
| if (ARM_CHECK_BUILTIN_MODE (4, HI)) |
| return arm_builtin_decl (ARM_BUILTIN_NEON_bswapv4hi, false); |
| else if (ARM_CHECK_BUILTIN_MODE (8, HI)) |
| return arm_builtin_decl (ARM_BUILTIN_NEON_bswapv8hi, false); |
| else |
| return NULL_TREE; |
| case BUILT_IN_BSWAP32: |
| if (ARM_CHECK_BUILTIN_MODE (2, SI)) |
| return arm_builtin_decl (ARM_BUILTIN_NEON_bswapv2si, false); |
| else if (ARM_CHECK_BUILTIN_MODE (4, SI)) |
| return arm_builtin_decl (ARM_BUILTIN_NEON_bswapv4si, false); |
| else |
| return NULL_TREE; |
| case BUILT_IN_BSWAP64: |
| if (ARM_CHECK_BUILTIN_MODE (2, DI)) |
| return arm_builtin_decl (ARM_BUILTIN_NEON_bswapv2di, false); |
| else |
| return NULL_TREE; |
| case BUILT_IN_COPYSIGNF: |
| if (ARM_CHECK_BUILTIN_MODE (2, SF)) |
| return arm_builtin_decl (ARM_BUILTIN_NEON_copysignfv2sf, false); |
| else if (ARM_CHECK_BUILTIN_MODE (4, SF)) |
| return arm_builtin_decl (ARM_BUILTIN_NEON_copysignfv4sf, false); |
| else |
| return NULL_TREE; |
| |
| default: |
| return NULL_TREE; |
| } |
| } |
| return NULL_TREE; |
| } |
| #undef ARM_FIND_VCVT_VARIANT |
| #undef ARM_FIND_VCVTU_VARIANT |
| #undef ARM_CHECK_BUILTIN_MODE |
| #undef ARM_FIND_VRINT_VARIANT |
| |
| void |
| arm_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update) |
| { |
| const unsigned ARM_FE_INVALID = 1; |
| const unsigned ARM_FE_DIVBYZERO = 2; |
| const unsigned ARM_FE_OVERFLOW = 4; |
| const unsigned ARM_FE_UNDERFLOW = 8; |
| const unsigned ARM_FE_INEXACT = 16; |
| const unsigned HOST_WIDE_INT ARM_FE_ALL_EXCEPT = (ARM_FE_INVALID |
| | ARM_FE_DIVBYZERO |
| | ARM_FE_OVERFLOW |
| | ARM_FE_UNDERFLOW |
| | ARM_FE_INEXACT); |
| const unsigned HOST_WIDE_INT ARM_FE_EXCEPT_SHIFT = 8; |
| tree fenv_var, get_fpscr, set_fpscr, mask, ld_fenv, masked_fenv; |
| tree new_fenv_var, reload_fenv, restore_fnenv; |
| tree update_call, atomic_feraiseexcept, hold_fnclex; |
| |
| if (!TARGET_VFP || !TARGET_HARD_FLOAT) |
| return; |
| |
| /* Generate the equivalent of : |
| unsigned int fenv_var; |
| fenv_var = __builtin_arm_get_fpscr (); |
| |
| unsigned int masked_fenv; |
| masked_fenv = fenv_var & mask; |
| |
| __builtin_arm_set_fpscr (masked_fenv); */ |
| |
| fenv_var = create_tmp_var (unsigned_type_node); |
| get_fpscr = arm_builtin_decls[ARM_BUILTIN_GET_FPSCR]; |
| set_fpscr = arm_builtin_decls[ARM_BUILTIN_SET_FPSCR]; |
| mask = build_int_cst (unsigned_type_node, |
| ~((ARM_FE_ALL_EXCEPT << ARM_FE_EXCEPT_SHIFT) |
| | ARM_FE_ALL_EXCEPT)); |
| ld_fenv = build2 (MODIFY_EXPR, unsigned_type_node, |
| fenv_var, build_call_expr (get_fpscr, 0)); |
| masked_fenv = build2 (BIT_AND_EXPR, unsigned_type_node, fenv_var, mask); |
| hold_fnclex = build_call_expr (set_fpscr, 1, masked_fenv); |
| *hold = build2 (COMPOUND_EXPR, void_type_node, |
| build2 (COMPOUND_EXPR, void_type_node, masked_fenv, ld_fenv), |
| hold_fnclex); |
| |
| /* Store the value of masked_fenv to clear the exceptions: |
| __builtin_arm_set_fpscr (masked_fenv); */ |
| |
| *clear = build_call_expr (set_fpscr, 1, masked_fenv); |
| |
| /* Generate the equivalent of : |
| unsigned int new_fenv_var; |
| new_fenv_var = __builtin_arm_get_fpscr (); |
| |
| __builtin_arm_set_fpscr (fenv_var); |
| |
| __atomic_feraiseexcept (new_fenv_var); */ |
| |
| new_fenv_var = create_tmp_var (unsigned_type_node); |
| reload_fenv = build2 (MODIFY_EXPR, unsigned_type_node, new_fenv_var, |
| build_call_expr (get_fpscr, 0)); |
| restore_fnenv = build_call_expr (set_fpscr, 1, fenv_var); |
| atomic_feraiseexcept = builtin_decl_implicit (BUILT_IN_ATOMIC_FERAISEEXCEPT); |
| update_call = build_call_expr (atomic_feraiseexcept, 1, |
| fold_convert (integer_type_node, new_fenv_var)); |
| *update = build2 (COMPOUND_EXPR, void_type_node, |
| build2 (COMPOUND_EXPR, void_type_node, |
| reload_fenv, restore_fnenv), update_call); |
| } |
| |
| #include "gt-arm-builtins.h" |