|  | /* intrinsics.cc -- D language compiler intrinsics. | 
|  | Copyright (C) 2006-2023 Free Software Foundation, Inc. | 
|  |  | 
|  | 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 "dmd/declaration.h" | 
|  | #include "dmd/expression.h" | 
|  | #include "dmd/identifier.h" | 
|  | #include "dmd/mangle.h" | 
|  | #include "dmd/module.h" | 
|  | #include "dmd/template.h" | 
|  |  | 
|  | #include "tm.h" | 
|  | #include "function.h" | 
|  | #include "tree.h" | 
|  | #include "diagnostic.h" | 
|  | #include "langhooks.h" | 
|  | #include "fold-const.h" | 
|  | #include "stringpool.h" | 
|  | #include "builtins.h" | 
|  | #include "vec-perm-indices.h" | 
|  |  | 
|  | #include "d-tree.h" | 
|  |  | 
|  |  | 
|  | /* An internal struct used to hold information on D intrinsics.  */ | 
|  |  | 
|  | struct intrinsic_decl | 
|  | { | 
|  | /* The DECL_INTRINSIC_CODE of this decl.  */ | 
|  | intrinsic_code code; | 
|  |  | 
|  | /* The DECL_FUNCTION_CODE of this decl, if it directly maps to any.  */ | 
|  | built_in_function built_in; | 
|  |  | 
|  | /* The name of the intrinsic.  */ | 
|  | const char *name; | 
|  |  | 
|  | /* The module where the intrinsic is located.  */ | 
|  | const char *module; | 
|  |  | 
|  | /* The mangled signature decoration of the intrinsic.  */ | 
|  | const char *deco; | 
|  |  | 
|  | /* True if the intrinsic is only handled in CTFE.  */ | 
|  | bool ctfeonly; | 
|  | }; | 
|  |  | 
|  | static const intrinsic_decl intrinsic_decls[] = | 
|  | { | 
|  | #define DEF_D_INTRINSIC(CODE, BUILTIN, NAME, MODULE, DECO, CTFE) \ | 
|  | { CODE, BUILTIN, NAME, MODULE, DECO, CTFE }, | 
|  |  | 
|  | #include "intrinsics.def" | 
|  |  | 
|  | #undef DEF_D_INTRINSIC | 
|  | }; | 
|  |  | 
|  | /* Checks if DECL is an intrinsic or run time library function that requires | 
|  | special processing.  Sets DECL_INTRINSIC_CODE so it can be identified | 
|  | later in maybe_expand_intrinsic.  */ | 
|  |  | 
|  | void | 
|  | maybe_set_intrinsic (FuncDeclaration *decl) | 
|  | { | 
|  | if (!decl->ident || decl->builtin != BUILTIN::unknown) | 
|  | return; | 
|  |  | 
|  | /* The builtin flag is updated only if we can evaluate the intrinsic | 
|  | at compile-time.  Such as the math or bitop intrinsics.  */ | 
|  | decl->builtin = BUILTIN::unimp; | 
|  |  | 
|  | /* Check if it's a compiler intrinsic.  We only require that any | 
|  | internally recognised intrinsics are declared in a module with | 
|  | an explicit module declaration.  */ | 
|  | Module *m = decl->getModule (); | 
|  |  | 
|  | if (!m || !m->md) | 
|  | return; | 
|  |  | 
|  | TemplateInstance *ti = decl->isInstantiated (); | 
|  | TemplateDeclaration *td = ti ? ti->tempdecl->isTemplateDeclaration () : NULL; | 
|  |  | 
|  | const char *tname = decl->ident->toChars (); | 
|  | const char *tmodule = m->md->toChars (); | 
|  | const char *tdeco = (td == NULL) ? decl->type->deco : NULL; | 
|  |  | 
|  | /* Look through all D intrinsics.  */ | 
|  | for (size_t i = 0; i < (int) INTRINSIC_LAST; i++) | 
|  | { | 
|  | if (!intrinsic_decls[i].name) | 
|  | continue; | 
|  |  | 
|  | if (strcmp (intrinsic_decls[i].name, tname) != 0 | 
|  | || strcmp (intrinsic_decls[i].module, tmodule) != 0) | 
|  | continue; | 
|  |  | 
|  | /* Instantiated functions would have the wrong type deco, get it from the | 
|  | template member instead.  */ | 
|  | if (tdeco == NULL) | 
|  | { | 
|  | if (!td || !td->onemember) | 
|  | return; | 
|  |  | 
|  | FuncDeclaration *fd = td->onemember->isFuncDeclaration (); | 
|  | if (fd == NULL) | 
|  | return; | 
|  |  | 
|  | OutBuffer buf; | 
|  | mangleToBuffer (fd->type, &buf); | 
|  | tdeco = buf.extractChars (); | 
|  | } | 
|  |  | 
|  | /* Matching the type deco may be a bit too strict, as it means that all | 
|  | function attributes that end up in the signature must be kept aligned | 
|  | between the compiler and library declaration.  */ | 
|  | if (strcmp (intrinsic_decls[i].deco, tdeco) == 0) | 
|  | { | 
|  | intrinsic_code code = intrinsic_decls[i].code; | 
|  |  | 
|  | if (decl->csym == NULL) | 
|  | get_symbol_decl (decl); | 
|  |  | 
|  | /* If there is no function body, then the implementation is always | 
|  | provided by the compiler.  */ | 
|  | if (!decl->fbody) | 
|  | set_decl_built_in_function (decl->csym, BUILT_IN_FRONTEND, code); | 
|  |  | 
|  | /* Infer whether the intrinsic can be used for CTFE, let the | 
|  | front-end know that it can be evaluated at compile-time.  */ | 
|  | switch (code) | 
|  | { | 
|  | case INTRINSIC_VA_ARG: | 
|  | case INTRINSIC_C_VA_ARG: | 
|  | case INTRINSIC_VASTART: | 
|  | case INTRINSIC_ADDS: | 
|  | case INTRINSIC_ADDSL: | 
|  | case INTRINSIC_ADDU: | 
|  | case INTRINSIC_ADDUL: | 
|  | case INTRINSIC_SUBS: | 
|  | case INTRINSIC_SUBSL: | 
|  | case INTRINSIC_SUBU: | 
|  | case INTRINSIC_SUBUL: | 
|  | case INTRINSIC_MULS: | 
|  | case INTRINSIC_MULSL: | 
|  | case INTRINSIC_MULU: | 
|  | case INTRINSIC_MULUI: | 
|  | case INTRINSIC_MULUL: | 
|  | case INTRINSIC_NEGS: | 
|  | case INTRINSIC_NEGSL: | 
|  | case INTRINSIC_LOADUNALIGNED: | 
|  | case INTRINSIC_STOREUNALIGNED: | 
|  | case INTRINSIC_SHUFFLE: | 
|  | case INTRINSIC_SHUFFLEVECTOR: | 
|  | case INTRINSIC_CONVERTVECTOR: | 
|  | case INTRINSIC_BLENDVECTOR: | 
|  | case INTRINSIC_VLOAD8: | 
|  | case INTRINSIC_VLOAD16: | 
|  | case INTRINSIC_VLOAD32: | 
|  | case INTRINSIC_VLOAD64: | 
|  | case INTRINSIC_VSTORE8: | 
|  | case INTRINSIC_VSTORE16: | 
|  | case INTRINSIC_VSTORE32: | 
|  | case INTRINSIC_VSTORE64: | 
|  | /* Cannot interpret function during CTFE.  If the library | 
|  | provides a definition, its body will be used instead.  */ | 
|  | break; | 
|  |  | 
|  | case INTRINSIC_POW: | 
|  | { | 
|  | /* Check that this overload of pow() is has an equivalent | 
|  | built-in function.  It could be `int pow(int, int)'.  */ | 
|  | tree rettype = TREE_TYPE (TREE_TYPE (decl->csym)); | 
|  | if (mathfn_built_in (rettype, BUILT_IN_POW) != NULL_TREE) | 
|  | decl->builtin = BUILTIN::gcc; | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | decl->builtin = BUILTIN::gcc; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* The intrinsic was marked as CTFE-only.  */ | 
|  | if (intrinsic_decls[i].ctfeonly) | 
|  | DECL_BUILT_IN_CTFE (decl->csym) = 1; | 
|  |  | 
|  | DECL_INTRINSIC_CODE (decl->csym) = code; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Helper function for maybe_warn_intrinsic_mismatch.  Issue warning about | 
|  | mismatch in the EXPECTED return type in call to the intrinsic function in | 
|  | CALLEXP, and return TRUE.  */ | 
|  |  | 
|  | static bool | 
|  | warn_mismatched_return_type (tree callexp, const char *expected) | 
|  | { | 
|  | warning_at (EXPR_LOCATION (callexp), OPT_Wbuiltin_declaration_mismatch, | 
|  | "mismatch in return type of intrinsic function %qD " | 
|  | "(%qT, should be %qs)", get_callee_fndecl (callexp), | 
|  | TREE_TYPE (callexp), expected); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Helper function for maybe_warn_intrinsic_mismatch.  Issue warning or error | 
|  | about mismatch in the EXPECTED argument type at ARGNO in call to the | 
|  | intrinsic function in CALLEXP, and return TRUE.  */ | 
|  |  | 
|  | static bool | 
|  | warn_mismatched_argument (tree callexp, unsigned argno, const char *expected) | 
|  | { | 
|  | warning_at (EXPR_LOCATION (callexp), OPT_Wbuiltin_declaration_mismatch, | 
|  | "mismatch in argument %u type of intrinsic function %qD " | 
|  | "(%qT, should be %qs)", argno + 1, get_callee_fndecl (callexp), | 
|  | TREE_TYPE (CALL_EXPR_ARG (callexp, argno)), expected); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool | 
|  | warn_mismatched_argument (tree callexp, unsigned argno, tree expected, | 
|  | bool error_p = false) | 
|  | { | 
|  | if (error_p) | 
|  | error_at (EXPR_LOCATION (callexp), | 
|  | "mismatch in argument %u type of intrinsic function %qD " | 
|  | "(%qT, should be %qT)", argno + 1, get_callee_fndecl (callexp), | 
|  | TREE_TYPE (CALL_EXPR_ARG (callexp, argno)), expected); | 
|  | else | 
|  | warning_at (EXPR_LOCATION (callexp), OPT_Wbuiltin_declaration_mismatch, | 
|  | "mismatch in argument %u type of intrinsic function %qD " | 
|  | "(%qT, should be %qT)", argno + 1, get_callee_fndecl (callexp), | 
|  | TREE_TYPE (CALL_EXPR_ARG (callexp, argno)), expected); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Helper function for maybe_warn_intrinsic_mismatch.  Builds a vector integer | 
|  | type suitable for the mask argument of INTRINSIC_SHUFFLE from the given | 
|  | input argument TYPE.  */ | 
|  |  | 
|  | static tree | 
|  | build_shuffle_mask_type (tree type) | 
|  | { | 
|  | const unsigned bits = GET_MODE_BITSIZE (SCALAR_TYPE_MODE (TREE_TYPE (type))); | 
|  | const int unsignedp = TYPE_UNSIGNED (TREE_TYPE (type)); | 
|  | tree inner = lang_hooks.types.type_for_size (bits, unsignedp); | 
|  | gcc_assert (inner && TREE_CODE (inner) == INTEGER_TYPE); | 
|  |  | 
|  | /* %% Get the front-end type for the vector so the D type will be | 
|  | printed (this should really be handled by a D tree printer).  */ | 
|  | Type *t = build_frontend_type (inner); | 
|  | gcc_assert (t != NULL); | 
|  | unsigned HOST_WIDE_INT nunits = TYPE_VECTOR_SUBPARTS (type).to_constant (); | 
|  |  | 
|  | return build_ctype (TypeVector::create (t->sarrayOf (nunits))); | 
|  | } | 
|  |  | 
|  | /* Checks if call to intrinsic FUNCTION in CALLEXP matches the internal | 
|  | type and value constraints that we expect from the library definitions. | 
|  | Returns TRUE and issues a warning if there is a mismatch. | 
|  |  | 
|  | Note: The return type and parameters are encoded into the signature `deco' | 
|  | string that we match on in maybe_set_intrinsic(), so if the deco mangle | 
|  | string has 'i' in the part that specifies the return type, then the matched | 
|  | intrinsic will always have the return type `int'. | 
|  |  | 
|  | For templated intrinsics however, we rely on template constraints to ensure | 
|  | that the generic type matches what we expect it to be.  There is still an | 
|  | enforced relationship between a template argument and its instantiated type. | 
|  | For example: `T func(T)(T*)' would have the generic return type `@1T' and | 
|  | generic parameter type `@1PT', so it can be assumed that if the return type | 
|  | matches what we expect then all parameters are fine as well.  Otherwise it | 
|  | can be assumed that some internal_error has occurred for this to be the case. | 
|  | Where a templated intrinsic has multiple template arguments, each generic | 
|  | type will need to be checked for its validity.  */ | 
|  |  | 
|  | static bool | 
|  | maybe_warn_intrinsic_mismatch (tree function, tree callexp) | 
|  | { | 
|  | switch (DECL_INTRINSIC_CODE (function)) | 
|  | { | 
|  | case INTRINSIC_NONE: | 
|  | default: | 
|  | return false; | 
|  |  | 
|  | case INTRINSIC_LOADUNALIGNED: | 
|  | { | 
|  | /* Expects the signature: | 
|  | vector(T) loadUnaligned (vector(T)*);  */ | 
|  | gcc_assert (call_expr_nargs (callexp) == 1); | 
|  |  | 
|  | tree ptr = TREE_TYPE (CALL_EXPR_ARG (callexp, 0)); | 
|  | if (!VECTOR_TYPE_P (TREE_TYPE (callexp)) | 
|  | || !POINTER_TYPE_P (ptr) || !VECTOR_TYPE_P (TREE_TYPE (ptr))) | 
|  | return warn_mismatched_return_type (callexp, "__vector(T)"); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | case INTRINSIC_STOREUNALIGNED: | 
|  | { | 
|  | /* Expects the signature: | 
|  | vector(T) storeUnaligned (vector(T)*, vector(T));  */ | 
|  | gcc_assert (call_expr_nargs (callexp) == 2); | 
|  |  | 
|  | tree ptr = TREE_TYPE (CALL_EXPR_ARG (callexp, 0)); | 
|  | tree val = TREE_TYPE (CALL_EXPR_ARG (callexp, 1)); | 
|  | if (!VECTOR_TYPE_P (TREE_TYPE (callexp)) | 
|  | || !POINTER_TYPE_P (ptr) || !VECTOR_TYPE_P (TREE_TYPE (ptr)) | 
|  | || !VECTOR_TYPE_P (val)) | 
|  | return warn_mismatched_return_type (callexp, "__vector(T)"); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | case INTRINSIC_SHUFFLE: | 
|  | case INTRINSIC_BLENDVECTOR: | 
|  | { | 
|  | /* Expects the signature: | 
|  | vector(T) shuffle (vector(T), vector(U), vector(V)); | 
|  | vector(T) blendvector (vector(T), vector(U), vector(V));  */ | 
|  | gcc_assert (call_expr_nargs (callexp) == 3); | 
|  |  | 
|  | tree vec0 = TREE_TYPE (CALL_EXPR_ARG (callexp, 0)); | 
|  | if (!VECTOR_TYPE_P (TREE_TYPE (callexp)) | 
|  | || !VECTOR_TYPE_P (vec0)) | 
|  | return warn_mismatched_return_type (callexp, "__vector(T)"); | 
|  |  | 
|  | tree vec1 = TREE_TYPE (CALL_EXPR_ARG (callexp, 1)); | 
|  | if (!VECTOR_TYPE_P (vec1)) | 
|  | return warn_mismatched_argument (callexp, 1, vec0); | 
|  |  | 
|  | tree mask = TREE_TYPE (CALL_EXPR_ARG (callexp, 2)); | 
|  | if (!VECTOR_TYPE_P (mask) || !VECTOR_INTEGER_TYPE_P (mask)) | 
|  | { | 
|  | tree expected = build_shuffle_mask_type (vec0); | 
|  | return warn_mismatched_argument (callexp, 2, expected, | 
|  | VECTOR_TYPE_P (mask)); | 
|  | } | 
|  |  | 
|  | /* Types have been validated, now issue errors about violations on the | 
|  | constraints of the intrinsic.  */ | 
|  | if (TYPE_MAIN_VARIANT (vec0) != TYPE_MAIN_VARIANT (vec1)) | 
|  | return warn_mismatched_argument (callexp, 1, vec0, true); | 
|  |  | 
|  | /* Vector element sizes should be equal between arguments and mask.  */ | 
|  | if (GET_MODE_BITSIZE (SCALAR_TYPE_MODE (TREE_TYPE (vec0))) | 
|  | != GET_MODE_BITSIZE (SCALAR_TYPE_MODE (TREE_TYPE (mask))) | 
|  | || maybe_ne (TYPE_VECTOR_SUBPARTS (vec0), | 
|  | TYPE_VECTOR_SUBPARTS (mask)) | 
|  | || maybe_ne (TYPE_VECTOR_SUBPARTS (vec1), | 
|  | TYPE_VECTOR_SUBPARTS (mask))) | 
|  | { | 
|  | tree expected = build_shuffle_mask_type (vec0); | 
|  | return warn_mismatched_argument (callexp, 2, expected, true); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | case INTRINSIC_SHUFFLEVECTOR: | 
|  | { | 
|  | /* Expects the signature: | 
|  | vector(T[N]) shufflevector (vector(T), vector(U), N...);  */ | 
|  | gcc_assert (call_expr_nargs (callexp) >= 3); | 
|  | gcc_assert (VECTOR_TYPE_P (TREE_TYPE (callexp))); | 
|  |  | 
|  | tree vec0 = TREE_TYPE (CALL_EXPR_ARG (callexp, 0)); | 
|  | if (!VECTOR_TYPE_P (vec0)) | 
|  | return warn_mismatched_argument (callexp, 0, "__vector(T)"); | 
|  |  | 
|  | tree vec1 = TREE_TYPE (CALL_EXPR_ARG (callexp, 1)); | 
|  | if (!VECTOR_TYPE_P (vec1)) | 
|  | return warn_mismatched_argument (callexp, 1, vec0); | 
|  |  | 
|  | for (int i = 2; i < call_expr_nargs (callexp); i++) | 
|  | { | 
|  | tree idx = TREE_TYPE (CALL_EXPR_ARG (callexp, i)); | 
|  | if (TREE_CODE (idx) != INTEGER_TYPE) | 
|  | return warn_mismatched_argument (callexp, i, d_int_type); | 
|  | } | 
|  |  | 
|  | /* Types have been validated, now issue errors about violations on the | 
|  | constraints of the intrinsic.  */ | 
|  | if (TYPE_MAIN_VARIANT (TREE_TYPE (vec0)) | 
|  | != TYPE_MAIN_VARIANT (TREE_TYPE (vec1))) | 
|  | { | 
|  | /* %% Get the front-end type for the vector so the D type will be | 
|  | printed (this should really be handled by a D tree printer).  */ | 
|  | unsigned HOST_WIDE_INT nunits; | 
|  | if (!TYPE_VECTOR_SUBPARTS (vec1).is_constant (&nunits)) | 
|  | break; | 
|  |  | 
|  | Type *inner = build_frontend_type (TREE_TYPE (vec0)); | 
|  | Type *vector = TypeVector::create (inner->sarrayOf (nunits)); | 
|  | return warn_mismatched_argument (callexp, 1, | 
|  | build_ctype (vector), true); | 
|  | } | 
|  |  | 
|  | /* Vector sizes should be known, and number of indices a power of 2.  */ | 
|  | unsigned HOST_WIDE_INT vec0_length; | 
|  | unsigned HOST_WIDE_INT vec1_length; | 
|  | if (!TYPE_VECTOR_SUBPARTS (vec0).is_constant (&vec0_length) | 
|  | || !TYPE_VECTOR_SUBPARTS (vec1).is_constant (&vec1_length) | 
|  | || !pow2p_hwi (call_expr_nargs (callexp) - 2)) | 
|  | break; | 
|  |  | 
|  | /* All index arguments must be valid constants as well.  */ | 
|  | for (int i = 2; i < call_expr_nargs (callexp); i++) | 
|  | { | 
|  | tree idx = CALL_EXPR_ARG (callexp, i); | 
|  | if (!tree_fits_shwi_p (idx)) | 
|  | { | 
|  | error_at (EXPR_LOCATION (callexp), | 
|  | "argument %qE cannot be read at compile time", idx); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HOST_WIDE_INT iidx = tree_to_shwi (idx); | 
|  | if (iidx < 0 | 
|  | || (unsigned HOST_WIDE_INT) iidx >= vec0_length + vec1_length) | 
|  | { | 
|  | error_at (EXPR_LOCATION (callexp), | 
|  | "element index %qE is out of bounds %<[0 .. %E]%>", | 
|  | idx, build_integer_cst (vec0_length + vec1_length)); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | case INTRINSIC_CONVERTVECTOR: | 
|  | { | 
|  | /* Expects the signature: | 
|  | vector(T) convertvector (vector(U));  */ | 
|  | gcc_assert (call_expr_nargs (callexp) == 1); | 
|  |  | 
|  | tree ret = TREE_TYPE (callexp); | 
|  | if (!VECTOR_TYPE_P (ret) | 
|  | || (!VECTOR_INTEGER_TYPE_P (ret) && !VECTOR_FLOAT_TYPE_P (ret))) | 
|  | return warn_mismatched_return_type (callexp, "__vector(T)"); | 
|  |  | 
|  | tree arg = TREE_TYPE (CALL_EXPR_ARG (callexp, 0)); | 
|  | if (!VECTOR_TYPE_P (arg) | 
|  | || (!VECTOR_INTEGER_TYPE_P (arg) && !VECTOR_FLOAT_TYPE_P (arg))) | 
|  | return warn_mismatched_argument (callexp, 0, "__vector(T)"); | 
|  |  | 
|  | /* Types have been validated, now issue errors about violations on the | 
|  | constraints of the intrinsic.  */ | 
|  | if (maybe_ne (TYPE_VECTOR_SUBPARTS (ret), TYPE_VECTOR_SUBPARTS (arg))) | 
|  | { | 
|  | /* %% Get the front-end type for the vector so the D type will be | 
|  | printed (this should really be handled by a D tree printer).  */ | 
|  | unsigned HOST_WIDE_INT nunits; | 
|  | if (!TYPE_VECTOR_SUBPARTS (ret).is_constant (&nunits)) | 
|  | break; | 
|  |  | 
|  | Type *inner = build_frontend_type (TREE_TYPE (arg)); | 
|  | Type *vector = TypeVector::create (inner->sarrayOf (nunits)); | 
|  | return warn_mismatched_argument (callexp, 0, | 
|  | build_ctype (vector), true); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Generic mismatch warning if it hasn't already been handled.  */ | 
|  | warning_at (EXPR_LOCATION (callexp), OPT_Wbuiltin_declaration_mismatch, | 
|  | "mismatch in call of intrinsic function %qD",  function); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Construct a function call to the built-in function CODE, N is the number of | 
|  | arguments, and the `...' parameters are the argument expressions. | 
|  | The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | call_builtin_fn (tree callexp, built_in_function code, int n, ...) | 
|  | { | 
|  | tree *argarray = XALLOCAVEC (tree, n); | 
|  | va_list ap; | 
|  |  | 
|  | va_start (ap, n); | 
|  | for (int i = 0; i < n; i++) | 
|  | argarray[i] = va_arg (ap, tree); | 
|  | va_end (ap); | 
|  |  | 
|  | tree exp = build_call_expr_loc_array (EXPR_LOCATION (callexp), | 
|  | builtin_decl_explicit (code), | 
|  | n, argarray); | 
|  | return convert (TREE_TYPE (callexp), fold (exp)); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to bsf().  This takes one argument, | 
|  | the signature to which can be either: | 
|  |  | 
|  | int bsf (uint arg); | 
|  | int bsf (ulong arg); | 
|  |  | 
|  | This scans all bits in the given argument starting with the first, | 
|  | returning the bit number of the first bit set.  The original call | 
|  | expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_bsf (tree callexp) | 
|  | { | 
|  | /* The bsf() intrinsic gets turned into __builtin_ctz(arg). | 
|  | The return value is supposed to be undefined if arg is zero.  */ | 
|  | tree arg = CALL_EXPR_ARG (callexp, 0); | 
|  | int argsize = TYPE_PRECISION (TREE_TYPE (arg)); | 
|  |  | 
|  | /* Which variant of __builtin_ctz* should we call?  */ | 
|  | built_in_function code = (argsize <= INT_TYPE_SIZE) ? BUILT_IN_CTZ | 
|  | : (argsize <= LONG_TYPE_SIZE) ? BUILT_IN_CTZL | 
|  | : (argsize <= LONG_LONG_TYPE_SIZE) ? BUILT_IN_CTZLL | 
|  | : END_BUILTINS; | 
|  |  | 
|  | gcc_assert (code != END_BUILTINS); | 
|  |  | 
|  | return call_builtin_fn (callexp, code, 1, arg); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to bsr().  This takes one argument, | 
|  | the signature to which can be either: | 
|  |  | 
|  | int bsr (uint arg); | 
|  | int bsr (ulong arg); | 
|  |  | 
|  | This scans all bits in the given argument from the most significant bit | 
|  | to the least significant, returning the bit number of the first bit set. | 
|  | The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_bsr (tree callexp) | 
|  | { | 
|  | /* The bsr() intrinsic gets turned into __builtin_clz(arg) ^ (size - 1). | 
|  | The return value is supposed to be undefined if arg is zero.  */ | 
|  | tree arg = CALL_EXPR_ARG (callexp, 0); | 
|  | tree type = TREE_TYPE (callexp); | 
|  | int argsize = TYPE_PRECISION (TREE_TYPE (arg)); | 
|  |  | 
|  | /* Which variant of __builtin_clz* should we call?  */ | 
|  | built_in_function code = (argsize <= INT_TYPE_SIZE) ? BUILT_IN_CLZ | 
|  | : (argsize <= LONG_TYPE_SIZE) ? BUILT_IN_CLZL | 
|  | : (argsize <= LONG_LONG_TYPE_SIZE) ? BUILT_IN_CLZLL | 
|  | : END_BUILTINS; | 
|  |  | 
|  | gcc_assert (code != END_BUILTINS); | 
|  |  | 
|  | tree result = call_builtin_fn (callexp, code, 1, arg); | 
|  |  | 
|  | return fold_build2 (BIT_XOR_EXPR, type, result, | 
|  | build_integer_cst (argsize - 1, type)); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end intrinsic call to INTRINSIC, which is either a call to | 
|  | bt(), btc(), btr(), or bts().  These intrinsics expect to take two arguments, | 
|  | the signature to which is: | 
|  |  | 
|  | int bt (size_t* ptr, size_t bitnum); | 
|  |  | 
|  | All intrinsics test if a bit is set and return the result of that condition. | 
|  | Variants of `bt' will then update that bit. `btc' compliments the bit, `bts' | 
|  | sets the bit, and `btr' resets the bit.  The original call expression is | 
|  | held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_bt (intrinsic_code intrinsic, tree callexp) | 
|  | { | 
|  | tree ptr = CALL_EXPR_ARG (callexp, 0); | 
|  | tree bitnum = CALL_EXPR_ARG (callexp, 1); | 
|  | tree type = TREE_TYPE (TREE_TYPE (ptr)); | 
|  |  | 
|  | /* size_t bitsize = sizeof(*ptr) * BITS_PER_UNIT;  */ | 
|  | tree bitsize = fold_convert (type, TYPE_SIZE (TREE_TYPE (ptr))); | 
|  |  | 
|  | /* ptr[bitnum / bitsize]  */ | 
|  | ptr = build_pointer_index (ptr, fold_build2 (TRUNC_DIV_EXPR, type, | 
|  | bitnum, bitsize)); | 
|  | ptr = indirect_ref (type, ptr); | 
|  |  | 
|  | /* mask = 1 << (bitnum % bitsize);  */ | 
|  | bitnum = fold_build2 (TRUNC_MOD_EXPR, type, bitnum, bitsize); | 
|  | bitnum = fold_build2 (LSHIFT_EXPR, type, build_one_cst (type), bitnum); | 
|  |  | 
|  | /* cond = ptr[bitnum / size] & mask;  */ | 
|  | tree cond = fold_build2 (BIT_AND_EXPR, type, ptr, bitnum); | 
|  |  | 
|  | /* cond ? -1 : 0;  */ | 
|  | cond = build_condition (TREE_TYPE (callexp), d_truthvalue_conversion (cond), | 
|  | build_minus_one_cst (TREE_TYPE (callexp)), | 
|  | build_zero_cst (TREE_TYPE (callexp))); | 
|  |  | 
|  | /* Update the bit as needed, only testing the bit for bt().  */ | 
|  | tree_code code; | 
|  |  | 
|  | switch (intrinsic) | 
|  | { | 
|  | case INTRINSIC_BT: | 
|  | case INTRINSIC_BT64: | 
|  | return cond; | 
|  |  | 
|  | case INTRINSIC_BTC: | 
|  | case INTRINSIC_BTC64: | 
|  | code = BIT_XOR_EXPR; | 
|  | break; | 
|  |  | 
|  | case INTRINSIC_BTR: | 
|  | case INTRINSIC_BTR64: | 
|  | bitnum = fold_build1 (BIT_NOT_EXPR, TREE_TYPE (bitnum), bitnum); | 
|  | code = BIT_AND_EXPR; | 
|  | break; | 
|  |  | 
|  | case INTRINSIC_BTS: | 
|  | case INTRINSIC_BTS64: | 
|  | code = BIT_IOR_EXPR; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | gcc_unreachable (); | 
|  | } | 
|  |  | 
|  | /* ptr[bitnum / size] op= mask;  */ | 
|  | ptr = modify_expr (ptr, fold_build2 (code, TREE_TYPE (ptr), ptr, bitnum)); | 
|  |  | 
|  | /* Store the condition result in a temporary, and return expressions in | 
|  | correct order of evaluation.  */ | 
|  | tree tmp = build_local_temp (TREE_TYPE (callexp)); | 
|  | cond = modify_expr (tmp, cond); | 
|  |  | 
|  | return compound_expr (cond, compound_expr (ptr, tmp)); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end intrinsic call to popcnt().  This takes one argument, the | 
|  | signature to which can be either: | 
|  |  | 
|  | int popcnt (uint arg); | 
|  | int popcnt (ulong arg); | 
|  |  | 
|  | Calculates the number of set bits in an integer.  The original call | 
|  | expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_popcnt (tree callexp) | 
|  | { | 
|  | tree arg = CALL_EXPR_ARG (callexp, 0); | 
|  | int argsize = TYPE_PRECISION (TREE_TYPE (arg)); | 
|  |  | 
|  | /* Which variant of __builtin_popcount* should we call?  */ | 
|  | built_in_function code = (argsize <= INT_TYPE_SIZE) ? BUILT_IN_POPCOUNT | 
|  | : (argsize <= LONG_TYPE_SIZE) ? BUILT_IN_POPCOUNTL | 
|  | : (argsize <= LONG_LONG_TYPE_SIZE) ? BUILT_IN_POPCOUNTLL | 
|  | : END_BUILTINS; | 
|  |  | 
|  | gcc_assert (code != END_BUILTINS); | 
|  |  | 
|  | return call_builtin_fn (callexp, code, 1, arg); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end intrinsic call to INTRINSIC, which is either a call to | 
|  | rol() or ror().  These intrinsics expect to take one or two arguments, | 
|  | the signature to which can be either: | 
|  |  | 
|  | T rol(T) (const T value, const uint count); | 
|  | T rol(uint count, T) (const T value); | 
|  | T ror(T) (const T value, const uint count); | 
|  | T ror(uint count, T) (const T value); | 
|  |  | 
|  | This bitwise rotates VALUE left or right by COUNT bit positions.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_rotate (intrinsic_code intrinsic, tree callexp) | 
|  | { | 
|  | tree type = TREE_TYPE (callexp); | 
|  | tree value = CALL_EXPR_ARG (callexp, 0); | 
|  | tree count; | 
|  | tree_code code; | 
|  |  | 
|  | /* Get the equivalent tree code for the intrinsic.  */ | 
|  | if (intrinsic == INTRINSIC_ROL || intrinsic == INTRINSIC_ROL_TIARG) | 
|  | code = LROTATE_EXPR; | 
|  | else if (intrinsic == INTRINSIC_ROR || intrinsic == INTRINSIC_ROR_TIARG) | 
|  | code = RROTATE_EXPR; | 
|  | else | 
|  | gcc_unreachable (); | 
|  |  | 
|  | /* Get the COUNT parameter.  Either from the call expression arguments or the | 
|  | template instantiation arguments.  */ | 
|  | if (intrinsic == INTRINSIC_ROL || intrinsic == INTRINSIC_ROR) | 
|  | count = CALL_EXPR_ARG (callexp, 1); | 
|  | else | 
|  | { | 
|  | /* Retrieve from the encoded template instantation.  */ | 
|  | tree callee = get_callee_fndecl (callexp); | 
|  | TemplateInstance *ti = DECL_LANG_FRONTEND (callee)->isInstantiated (); | 
|  | gcc_assert (ti && ti->tiargs && ti->tiargs->length == 2); | 
|  |  | 
|  | Expression *e = isExpression ((*ti->tiargs)[0]); | 
|  | gcc_assert (e && e->op == EXP::int64); | 
|  | count = build_expr (e, true); | 
|  | } | 
|  |  | 
|  | return fold_build2 (code, type, value, count); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end intrinsic call to copysign().  This takes two arguments, | 
|  | the signature to which can be either: | 
|  |  | 
|  | float copysign (T to, float from); | 
|  | double copysign (T to, double from); | 
|  | real copysign (T to, real from); | 
|  |  | 
|  | This computes a value composed of TO with the sign bit of FROM.  The original | 
|  | call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_copysign (tree callexp) | 
|  | { | 
|  | tree to = CALL_EXPR_ARG (callexp, 0); | 
|  | tree from = CALL_EXPR_ARG (callexp, 1); | 
|  | tree type = TREE_TYPE (to); | 
|  |  | 
|  | /* Convert parameters to the same type.  Prefer the first parameter unless it | 
|  | is an integral type.  */ | 
|  | if (INTEGRAL_TYPE_P (type)) | 
|  | { | 
|  | to = fold_convert (TREE_TYPE (from), to); | 
|  | type = TREE_TYPE (to); | 
|  | } | 
|  | else | 
|  | from = fold_convert (type, from); | 
|  |  | 
|  | /* Which variant of __builtin_copysign* should we call?  */ | 
|  | built_in_function code = (type == float_type_node) ? BUILT_IN_COPYSIGNF | 
|  | : (type == double_type_node) ? BUILT_IN_COPYSIGN | 
|  | : (type == long_double_type_node) ? BUILT_IN_COPYSIGNL | 
|  | : END_BUILTINS; | 
|  |  | 
|  | gcc_assert (code != END_BUILTINS); | 
|  |  | 
|  | return call_builtin_fn (callexp, code, 2, to, from); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end intrinsic call to pow().  This takes two arguments, the | 
|  | signature to which can be either: | 
|  |  | 
|  | float pow (float base, T exponent); | 
|  | double pow (double base, T exponent); | 
|  | real pow (real base, T exponent); | 
|  |  | 
|  | This computes the value of BASE raised to the power of EXPONENT. | 
|  | The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_pow (tree callexp) | 
|  | { | 
|  | tree base = CALL_EXPR_ARG (callexp, 0); | 
|  | tree exponent = CALL_EXPR_ARG (callexp, 1); | 
|  | tree exptype = TREE_TYPE (exponent); | 
|  |  | 
|  | /* Which variant of __builtin_pow* should we call?  */ | 
|  | built_in_function code = SCALAR_FLOAT_TYPE_P (exptype) ? BUILT_IN_POW | 
|  | : INTEGRAL_TYPE_P (exptype) ? BUILT_IN_POWI | 
|  | : END_BUILTINS; | 
|  | gcc_assert (code != END_BUILTINS); | 
|  |  | 
|  | tree builtin = mathfn_built_in (TREE_TYPE (base), code); | 
|  | gcc_assert (builtin != NULL_TREE); | 
|  |  | 
|  | return call_builtin_fn (callexp, DECL_FUNCTION_CODE (builtin), 2, | 
|  | base, exponent); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end intrinsic call to toPrec().  This takes one argument, the | 
|  | signature to which can be either: | 
|  |  | 
|  | T toPrec(T)(float f); | 
|  | T toPrec(T)(double f); | 
|  | T toPrec(T)(real f); | 
|  |  | 
|  | This rounds the argument F to the precision of the specified floating | 
|  | point type T.  The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_toprec (tree callexp) | 
|  | { | 
|  | tree f = CALL_EXPR_ARG (callexp, 0); | 
|  | tree type = TREE_TYPE (callexp); | 
|  |  | 
|  | return convert (type, f); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end intrinsic call to va_arg().  This takes either one or two | 
|  | arguments, the signature to which can be either: | 
|  |  | 
|  | T va_arg(T) (ref va_list ap); | 
|  | void va_arg(T) (va_list ap, ref T parmn); | 
|  |  | 
|  | This retrieves the next variadic parameter that is type T from the given | 
|  | va_list.  If also given, store the value into parmn, otherwise return it. | 
|  | The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_vaarg (tree callexp) | 
|  | { | 
|  | tree ap = CALL_EXPR_ARG (callexp, 0); | 
|  | tree parmn = NULL_TREE; | 
|  | tree type; | 
|  |  | 
|  | STRIP_NOPS (ap); | 
|  |  | 
|  | if (call_expr_nargs (callexp) == 1) | 
|  | type = TREE_TYPE (callexp); | 
|  | else | 
|  | { | 
|  | parmn = CALL_EXPR_ARG (callexp, 1); | 
|  | STRIP_NOPS (parmn); | 
|  |  | 
|  | /* The `ref' argument to va_arg is either an address or reference, | 
|  | get the value of it.  */ | 
|  | if (TREE_CODE (parmn) == PARM_DECL && POINTER_TYPE_P (TREE_TYPE (parmn))) | 
|  | parmn = build_deref (parmn); | 
|  | else | 
|  | { | 
|  | gcc_assert (TREE_CODE (parmn) == ADDR_EXPR); | 
|  | parmn = TREE_OPERAND (parmn, 0); | 
|  | } | 
|  |  | 
|  | type = TREE_TYPE (parmn); | 
|  | } | 
|  |  | 
|  | /* (T) VA_ARG_EXP<ap>;  */ | 
|  | tree exp = build1_loc (EXPR_LOCATION (callexp), VA_ARG_EXPR, type, ap); | 
|  |  | 
|  | /* parmn = (T) VA_ARG_EXP<ap>;  */ | 
|  | if (parmn != NULL_TREE) | 
|  | exp = modify_expr (parmn, exp); | 
|  |  | 
|  | return exp; | 
|  | } | 
|  |  | 
|  | /* Expand a front-end intrinsic call to va_start(), which takes two arguments, | 
|  | the signature to which is: | 
|  |  | 
|  | void va_start(T) (out va_list ap, ref T parmn); | 
|  |  | 
|  | This initializes the va_list type, where parmn should be the last named | 
|  | parameter.  The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_vastart (tree callexp) | 
|  | { | 
|  | tree ap = CALL_EXPR_ARG (callexp, 0); | 
|  | tree parmn = CALL_EXPR_ARG (callexp, 1); | 
|  |  | 
|  | STRIP_NOPS (ap); | 
|  | STRIP_NOPS (parmn); | 
|  |  | 
|  | /* The va_list argument should already have its address taken.  The second | 
|  | argument, however, is inout and that needs to be fixed to prevent a | 
|  | warning.  Could be casting, so need to check type too?  */ | 
|  | gcc_assert (TREE_CODE (ap) == ADDR_EXPR | 
|  | || (TREE_CODE (ap) == PARM_DECL | 
|  | && POINTER_TYPE_P (TREE_TYPE (ap)))); | 
|  |  | 
|  | /* Assuming nobody tries to change the return type.  */ | 
|  | if (TREE_CODE (parmn) != PARM_DECL) | 
|  | { | 
|  | gcc_assert (TREE_CODE (parmn) == ADDR_EXPR); | 
|  | parmn = TREE_OPERAND (parmn, 0); | 
|  | } | 
|  |  | 
|  | return call_builtin_fn (callexp, BUILT_IN_VA_START, 2, ap, parmn); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to INTRINSIC, which is either a call to | 
|  | adds(), addu(), subs(), subu(), negs(), muls(), or mulu().  These intrinsics | 
|  | expect to take two or three arguments, the signature to which can be either: | 
|  |  | 
|  | int adds (int x, int y, ref bool overflow); | 
|  | long adds (long x, long y, ref bool overflow); | 
|  | int negs (int x, ref bool overflow); | 
|  | long negs (long x, ref bool overflow); | 
|  |  | 
|  | This performs an operation on two signed or unsigned integers, checking for | 
|  | overflow.  The overflow is sticky, meaning that a sequence of operations | 
|  | can be done and overflow need only be checked at the end.  The original call | 
|  | expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_checkedint (intrinsic_code intrinsic, tree callexp) | 
|  | { | 
|  | tree type = TREE_TYPE (callexp); | 
|  | tree x; | 
|  | tree y; | 
|  | tree overflow; | 
|  | internal_fn icode; | 
|  |  | 
|  | /* Which variant of *_OVERFLOW should we generate?  */ | 
|  | switch (intrinsic) | 
|  | { | 
|  | case INTRINSIC_ADDS: | 
|  | case INTRINSIC_ADDSL: | 
|  | case INTRINSIC_ADDU: | 
|  | case INTRINSIC_ADDUL: | 
|  | x = CALL_EXPR_ARG (callexp, 0); | 
|  | y = CALL_EXPR_ARG (callexp, 1); | 
|  | overflow = CALL_EXPR_ARG (callexp, 2); | 
|  | icode = IFN_ADD_OVERFLOW; | 
|  | break; | 
|  |  | 
|  | case INTRINSIC_SUBS: | 
|  | case INTRINSIC_SUBSL: | 
|  | case INTRINSIC_SUBU: | 
|  | case INTRINSIC_SUBUL: | 
|  | x = CALL_EXPR_ARG (callexp, 0); | 
|  | y = CALL_EXPR_ARG (callexp, 1); | 
|  | overflow = CALL_EXPR_ARG (callexp, 2); | 
|  | icode = IFN_SUB_OVERFLOW; | 
|  | break; | 
|  |  | 
|  | case INTRINSIC_MULS: | 
|  | case INTRINSIC_MULSL: | 
|  | case INTRINSIC_MULU: | 
|  | case INTRINSIC_MULUI: | 
|  | case INTRINSIC_MULUL: | 
|  | x = CALL_EXPR_ARG (callexp, 0); | 
|  | y = CALL_EXPR_ARG (callexp, 1); | 
|  | overflow = CALL_EXPR_ARG (callexp, 2); | 
|  | icode = IFN_MUL_OVERFLOW; | 
|  | break; | 
|  |  | 
|  | case INTRINSIC_NEGS: | 
|  | case INTRINSIC_NEGSL: | 
|  | /* The negs() intrinsic gets turned into SUB_OVERFLOW (0, y).  */ | 
|  | x = fold_convert (type, integer_zero_node); | 
|  | y = CALL_EXPR_ARG (callexp, 0); | 
|  | overflow = CALL_EXPR_ARG (callexp, 1); | 
|  | icode = IFN_SUB_OVERFLOW; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | gcc_unreachable (); | 
|  | } | 
|  |  | 
|  | tree result | 
|  | = build_call_expr_internal_loc (EXPR_LOCATION (callexp), icode, | 
|  | build_complex_type (type), 2, x, y); | 
|  |  | 
|  | STRIP_NOPS (overflow); | 
|  | overflow = build_deref (overflow); | 
|  |  | 
|  | /* Assign returned result to overflow parameter, however if overflow is | 
|  | already true, maintain its value.  */ | 
|  | type = TREE_TYPE (overflow); | 
|  | result = save_expr (result); | 
|  |  | 
|  | tree exp = fold_build2 (BIT_IOR_EXPR, type, overflow, | 
|  | fold_convert (type, imaginary_part (result))); | 
|  | exp = modify_expr (overflow, exp); | 
|  |  | 
|  | /* Return the value of result.  */ | 
|  | return compound_expr (exp, real_part (result)); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to volatileLoad().  This takes one | 
|  | argument, the signature to which can be either: | 
|  |  | 
|  | ubyte volatileLoad (ubyte* ptr); | 
|  | ushort volatileLoad (ushort* ptr); | 
|  | uint volatileLoad (uint* ptr); | 
|  | ulong volatileLoad (ulong* ptr); | 
|  |  | 
|  | This reads a value from the memory location indicated by ptr.  Calls to | 
|  | them are be guaranteed to not be removed (such as during DCE) or reordered | 
|  | in the same thread.  The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_volatile_load (tree callexp) | 
|  | { | 
|  | tree ptr = CALL_EXPR_ARG (callexp, 0); | 
|  | tree ptrtype = TREE_TYPE (ptr); | 
|  | gcc_assert (POINTER_TYPE_P (ptrtype)); | 
|  |  | 
|  | /* (T) *(volatile T *) ptr;  */ | 
|  | tree type = build_qualified_type (TREE_TYPE (ptrtype), TYPE_QUAL_VOLATILE); | 
|  | tree result = indirect_ref (type, ptr); | 
|  | TREE_THIS_VOLATILE (result) = 1; | 
|  | TREE_SIDE_EFFECTS (result) = 1; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to volatileStore().  This takes two | 
|  | arguments, the signature to which can be either: | 
|  |  | 
|  | void volatileStore (ubyte* ptr, ubyte value); | 
|  | void volatileStore (ushort* ptr, ushort value); | 
|  | void volatileStore (uint* ptr, uint value); | 
|  | void volatileStore (ulong* ptr, ulong value); | 
|  |  | 
|  | This writes a value to the memory location indicated by ptr.  Calls to | 
|  | them are be guaranteed to not be removed (such as during DCE) or reordered | 
|  | in the same thread.  The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_volatile_store (tree callexp) | 
|  | { | 
|  | tree ptr = CALL_EXPR_ARG (callexp, 0); | 
|  | tree ptrtype = TREE_TYPE (ptr); | 
|  | gcc_assert (POINTER_TYPE_P (ptrtype)); | 
|  |  | 
|  | /* (T) *(volatile T *) ptr;  */ | 
|  | tree type = build_qualified_type (TREE_TYPE (ptrtype), TYPE_QUAL_VOLATILE); | 
|  | tree result = indirect_ref (type, ptr); | 
|  | TREE_THIS_VOLATILE (result) = 1; | 
|  | TREE_SIDE_EFFECTS (result) = 1; | 
|  |  | 
|  | /* (*(volatile T *) ptr) = value;  */ | 
|  | tree value = CALL_EXPR_ARG (callexp, 1); | 
|  | return modify_expr (result, value); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to convertvector().  This takes one | 
|  | argument, the signature to which is: | 
|  |  | 
|  | vector(T) convertvector (vector(F) vec); | 
|  |  | 
|  | This converts a vector VEC to TYPE by casting every element in VEC to the | 
|  | element type of TYPE.  The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_vec_convert (tree callexp) | 
|  | { | 
|  | tree vec = CALL_EXPR_ARG (callexp, 0); | 
|  | tree type = TREE_TYPE (callexp); | 
|  |  | 
|  | /* Use VIEW_CONVERT for simple vector conversions.  */ | 
|  | if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (vec))) | 
|  | == TYPE_MAIN_VARIANT (TREE_TYPE (type))) | 
|  | || (VECTOR_INTEGER_TYPE_P (TREE_TYPE (vec)) | 
|  | && VECTOR_INTEGER_TYPE_P (type) | 
|  | && (TYPE_PRECISION (TREE_TYPE (TREE_TYPE (vec))) | 
|  | == TYPE_PRECISION (TREE_TYPE (type))))) | 
|  | return build1_loc (EXPR_LOCATION (callexp), VIEW_CONVERT_EXPR, type, vec); | 
|  |  | 
|  | return build_call_expr_internal_loc (EXPR_LOCATION (callexp), IFN_VEC_CONVERT, | 
|  | type, 1, vec); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to blendvector().  This expects to take | 
|  | three arguments, the signature to which is: | 
|  |  | 
|  | vector(T) blendvector (vector(T) vec0, vector(U) vec1, vector(M) mask); | 
|  |  | 
|  | This builds a VEC_COND_EXPR if VEC0, VEC1, and MASK are vector types, VEC0 | 
|  | has the same type as VEC1, and the number of elements of VEC0, VEC1, and MASK | 
|  | are the same.  The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_vec_blend (tree callexp) | 
|  | { | 
|  | tree vec0 = CALL_EXPR_ARG (callexp, 0); | 
|  | tree vec1 = CALL_EXPR_ARG (callexp, 1); | 
|  | tree mask = CALL_EXPR_ARG (callexp, 2); | 
|  |  | 
|  | tree cmp = fold_build2_loc (EXPR_LOCATION (callexp), NE_EXPR, | 
|  | truth_type_for (TREE_TYPE (mask)), | 
|  | mask, build_zero_cst (TREE_TYPE (mask))); | 
|  |  | 
|  | tree ret = fold_build3_loc (EXPR_LOCATION (callexp), VEC_COND_EXPR, | 
|  | TREE_TYPE (callexp), cmp, vec0, vec1); | 
|  |  | 
|  | if (!CONSTANT_CLASS_P (vec0) || !CONSTANT_CLASS_P (vec1)) | 
|  | ret = force_target_expr (ret); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to shuffle().  This expects to take three | 
|  | arguments, the signature to which is: | 
|  |  | 
|  | vector(T) shuffle (vector(T) vec0, vector(T) vec1, vector(M) mask); | 
|  |  | 
|  | This builds a VEC_PERM_EXPR if VEC0, VEC1, and MASK are vector types, VEC0 | 
|  | has the same type as VEC1, and the number of elements of VEC0, VEC1, and MASK | 
|  | are the same.  The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_vec_shuffle (tree callexp) | 
|  | { | 
|  | tree vec0 = CALL_EXPR_ARG (callexp, 0); | 
|  | tree vec1 = CALL_EXPR_ARG (callexp, 1); | 
|  | tree mask = CALL_EXPR_ARG (callexp, 2); | 
|  |  | 
|  | return build3_loc (EXPR_LOCATION (callexp), VEC_PERM_EXPR, | 
|  | TREE_TYPE (callexp), vec0, vec1, mask); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to shufflevector().  This takes two | 
|  | positional arguments and a variadic list, the signature to which is: | 
|  |  | 
|  | vector(TM) shuffle (vector(T) vec1, vector(T) vec2, index...); | 
|  |  | 
|  | This builds a VEC_PERM_EXPR if VEC0 and VEC1 are vector types, VEC0 has the | 
|  | same element type as VEC1, and the number of elements in INDEX is a valid | 
|  | power of two.  The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_vec_shufflevector (tree callexp) | 
|  | { | 
|  | tree vec0 = CALL_EXPR_ARG (callexp, 0); | 
|  | tree vec1 = CALL_EXPR_ARG (callexp, 1); | 
|  |  | 
|  | unsigned HOST_WIDE_INT v0elems = | 
|  | TYPE_VECTOR_SUBPARTS (TREE_TYPE (vec0)).to_constant (); | 
|  | unsigned HOST_WIDE_INT v1elems = | 
|  | TYPE_VECTOR_SUBPARTS (TREE_TYPE (vec1)).to_constant (); | 
|  |  | 
|  | unsigned HOST_WIDE_INT num_indices = call_expr_nargs (callexp) - 2; | 
|  | unsigned HOST_WIDE_INT masklen = MAX (num_indices, MAX (v0elems, v1elems)); | 
|  | unsigned HOST_WIDE_INT pad_size = (v0elems < masklen ? masklen - v0elems : 0); | 
|  | vec_perm_builder sel (masklen, masklen, 1); | 
|  |  | 
|  | unsigned n = 0; | 
|  | for (; n < num_indices; ++n) | 
|  | { | 
|  | tree idx = CALL_EXPR_ARG (callexp, n + 2); | 
|  | HOST_WIDE_INT iidx = tree_to_shwi (idx); | 
|  | /* VEC_PERM_EXPR does not allow different sized inputs.  */ | 
|  | if ((unsigned HOST_WIDE_INT) iidx >= v0elems) | 
|  | iidx += pad_size; | 
|  |  | 
|  | sel.quick_push (iidx); | 
|  | } | 
|  |  | 
|  | /* VEC_PERM_EXPR does not support a result that is smaller than the inputs.  */ | 
|  | for (; n < masklen; ++n) | 
|  | sel.quick_push (n); | 
|  |  | 
|  | vec_perm_indices indices (sel, 2, masklen); | 
|  |  | 
|  | /* Pad out arguments to the common vector size.  */ | 
|  | tree ret_type = build_vector_type (TREE_TYPE (TREE_TYPE (vec0)), masklen); | 
|  | if (v0elems < masklen) | 
|  | { | 
|  | constructor_elt elt = { NULL_TREE, build_zero_cst (TREE_TYPE (vec0)) }; | 
|  | vec0 = build_constructor_single (ret_type, NULL_TREE, vec0); | 
|  | for (unsigned i = 1; i < masklen / v0elems; ++i) | 
|  | vec_safe_push (CONSTRUCTOR_ELTS (vec0), elt); | 
|  | } | 
|  |  | 
|  | if (v1elems < masklen) | 
|  | { | 
|  | constructor_elt elt = { NULL_TREE, build_zero_cst (TREE_TYPE (vec1)) }; | 
|  | vec1 = build_constructor_single (ret_type, NULL_TREE, vec1); | 
|  | for (unsigned i = 1; i < masklen / v1elems; ++i) | 
|  | vec_safe_push (CONSTRUCTOR_ELTS (vec1), elt); | 
|  | } | 
|  |  | 
|  | tree mask_type = build_vector_type (build_nonstandard_integer_type | 
|  | (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (ret_type))), 1), | 
|  | masklen); | 
|  | tree ret = build3_loc (EXPR_LOCATION (callexp), VEC_PERM_EXPR, ret_type, vec0, | 
|  | vec1, vec_perm_indices_to_tree (mask_type, indices)); | 
|  |  | 
|  | /* Get the low part we are interested in.  */ | 
|  | if (num_indices < masklen) | 
|  | { | 
|  | ret = build3_loc (EXPR_LOCATION (callexp), BIT_FIELD_REF, | 
|  | TREE_TYPE (callexp), ret, | 
|  | TYPE_SIZE (TREE_TYPE (callexp)), bitsize_zero_node); | 
|  | /* Wrap the low part operation in a TARGET_EXPR so it gets a separate | 
|  | temporary during gimplification.  */ | 
|  | ret = force_target_expr (ret); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to loadUnaligned().  This takes one | 
|  | argument, the signature to which is: | 
|  |  | 
|  | vector(T) loadUnaligned (vector(T)* ptr) | 
|  |  | 
|  | This generates a load of a vector from an unaligned address PTR. | 
|  | The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_vec_load_unaligned (tree callexp) | 
|  | { | 
|  | tree ptr = CALL_EXPR_ARG (callexp, 0); | 
|  |  | 
|  | tree unaligned_type = build_variant_type_copy (TREE_TYPE (TREE_TYPE (ptr))); | 
|  | SET_TYPE_ALIGN (unaligned_type, 1 * BITS_PER_UNIT); | 
|  | TYPE_USER_ALIGN (unaligned_type) = 1; | 
|  |  | 
|  | tree load = indirect_ref (unaligned_type, ptr); | 
|  | return convert (TREE_TYPE (callexp), load); | 
|  | } | 
|  |  | 
|  | /* Expand a front-end instrinsic call to storeUnaligned().  This takes two | 
|  | arguments, the signature to which is: | 
|  |  | 
|  | vector(T) storeUnaligned (vector(T)* ptr, vector(T) value) | 
|  |  | 
|  | This generates an assignment of a vector VALUE to an unaligned address PTR. | 
|  | The original call expression is held in CALLEXP.  */ | 
|  |  | 
|  | static tree | 
|  | expand_intrinsic_vec_store_unaligned (tree callexp) | 
|  | { | 
|  | tree ptr = CALL_EXPR_ARG (callexp, 0); | 
|  | tree vec = CALL_EXPR_ARG (callexp, 1); | 
|  |  | 
|  | tree unaligned_type = build_variant_type_copy (TREE_TYPE (TREE_TYPE (ptr))); | 
|  | SET_TYPE_ALIGN (unaligned_type, 1 * BITS_PER_UNIT); | 
|  | TYPE_USER_ALIGN (unaligned_type) = 1; | 
|  |  | 
|  | tree load = indirect_ref (unaligned_type, ptr); | 
|  | return build_assign (MODIFY_EXPR, load, vec); | 
|  | } | 
|  |  | 
|  | /* If CALLEXP is for an intrinsic , expand and return inlined compiler | 
|  | generated instructions.  Most map directly to GCC builtins, others | 
|  | require a little extra work around them.  */ | 
|  |  | 
|  | tree | 
|  | maybe_expand_intrinsic (tree callexp) | 
|  | { | 
|  | tree callee = get_callee_fndecl (callexp); | 
|  |  | 
|  | if (callee == NULL_TREE || TREE_CODE (callee) != FUNCTION_DECL) | 
|  | return callexp; | 
|  |  | 
|  | /* Don't expand CTFE-only intrinsics outside of semantic processing.  */ | 
|  | if (DECL_BUILT_IN_CTFE (callee) && !doing_semantic_analysis_p) | 
|  | return callexp; | 
|  |  | 
|  | /* Gate the expansion of the intrinsic with constraint checks, if any fail | 
|  | then bail out without any lowering.  */ | 
|  | if (maybe_warn_intrinsic_mismatch (callee, callexp)) | 
|  | { | 
|  | /* Reset the built-in flag so that we don't trip fold_builtin.  */ | 
|  | set_decl_built_in_function (callee, NOT_BUILT_IN, 0); | 
|  | return callexp; | 
|  | } | 
|  |  | 
|  | intrinsic_code intrinsic = DECL_INTRINSIC_CODE (callee); | 
|  | built_in_function code; | 
|  |  | 
|  | switch (intrinsic) | 
|  | { | 
|  | case INTRINSIC_NONE: | 
|  | return callexp; | 
|  |  | 
|  | case INTRINSIC_BSF: | 
|  | case INTRINSIC_BSF64: | 
|  | return expand_intrinsic_bsf (callexp); | 
|  |  | 
|  | case INTRINSIC_BSR: | 
|  | case INTRINSIC_BSR64: | 
|  | return expand_intrinsic_bsr (callexp); | 
|  |  | 
|  | case INTRINSIC_BT: | 
|  | case INTRINSIC_BT64: | 
|  | case INTRINSIC_BTC: | 
|  | case INTRINSIC_BTC64: | 
|  | case INTRINSIC_BTR: | 
|  | case INTRINSIC_BTR64: | 
|  | case INTRINSIC_BTS: | 
|  | case INTRINSIC_BTS64: | 
|  | return expand_intrinsic_bt (intrinsic, callexp); | 
|  |  | 
|  | case INTRINSIC_POPCNT32: | 
|  | case INTRINSIC_POPCNT64: | 
|  | return expand_intrinsic_popcnt (callexp); | 
|  |  | 
|  | case INTRINSIC_ROL: | 
|  | case INTRINSIC_ROL_TIARG: | 
|  | case INTRINSIC_ROR: | 
|  | case INTRINSIC_ROR_TIARG: | 
|  | return expand_intrinsic_rotate (intrinsic, callexp); | 
|  |  | 
|  | case INTRINSIC_BSWAP16: | 
|  | case INTRINSIC_BSWAP32: | 
|  | case INTRINSIC_BSWAP64: | 
|  | case INTRINSIC_CEIL: | 
|  | case INTRINSIC_CEILF: | 
|  | case INTRINSIC_CEILL: | 
|  | case INTRINSIC_COS: | 
|  | case INTRINSIC_COSF: | 
|  | case INTRINSIC_COSL: | 
|  | case INTRINSIC_EXP: | 
|  | case INTRINSIC_EXP2: | 
|  | case INTRINSIC_EXPM1: | 
|  | case INTRINSIC_FABS: | 
|  | case INTRINSIC_FABSF: | 
|  | case INTRINSIC_FABSL: | 
|  | case INTRINSIC_FLOOR: | 
|  | case INTRINSIC_FLOORF: | 
|  | case INTRINSIC_FLOORL: | 
|  | case INTRINSIC_ISFINITE: | 
|  | case INTRINSIC_ISINFINITY: | 
|  | case INTRINSIC_ISNAN: | 
|  | case INTRINSIC_LOG: | 
|  | case INTRINSIC_LOG10: | 
|  | case INTRINSIC_LOG2: | 
|  | case INTRINSIC_RINT: | 
|  | case INTRINSIC_RINTF: | 
|  | case INTRINSIC_RINTL: | 
|  | case INTRINSIC_RNDTOL: | 
|  | case INTRINSIC_RNDTOLF: | 
|  | case INTRINSIC_RNDTOLL: | 
|  | case INTRINSIC_ROUND: | 
|  | case INTRINSIC_SIN: | 
|  | case INTRINSIC_SINF: | 
|  | case INTRINSIC_SINL: | 
|  | case INTRINSIC_SQRT: | 
|  | case INTRINSIC_SQRTF: | 
|  | case INTRINSIC_SQRTL: | 
|  | case INTRINSIC_TAN: | 
|  | case INTRINSIC_TRUNC: | 
|  | code = intrinsic_decls[intrinsic].built_in; | 
|  | gcc_assert (code != BUILT_IN_NONE); | 
|  | return call_builtin_fn (callexp, code, 1, | 
|  | CALL_EXPR_ARG (callexp, 0)); | 
|  |  | 
|  | case INTRINSIC_FMAX: | 
|  | case INTRINSIC_FMIN: | 
|  | case INTRINSIC_LDEXP: | 
|  | case INTRINSIC_LDEXPF: | 
|  | case INTRINSIC_LDEXPL: | 
|  | code = intrinsic_decls[intrinsic].built_in; | 
|  | gcc_assert (code != BUILT_IN_NONE); | 
|  | return call_builtin_fn (callexp, code, 2, | 
|  | CALL_EXPR_ARG (callexp, 0), | 
|  | CALL_EXPR_ARG (callexp, 1)); | 
|  |  | 
|  | case INTRINSIC_FMA: | 
|  | code = intrinsic_decls[intrinsic].built_in; | 
|  | gcc_assert (code != BUILT_IN_NONE); | 
|  | return call_builtin_fn (callexp, code, 3, | 
|  | CALL_EXPR_ARG (callexp, 0), | 
|  | CALL_EXPR_ARG (callexp, 1), | 
|  | CALL_EXPR_ARG (callexp, 2)); | 
|  |  | 
|  | case INTRINSIC_COPYSIGN: | 
|  | case INTRINSIC_COPYSIGNI: | 
|  | return expand_intrinsic_copysign (callexp); | 
|  |  | 
|  | case INTRINSIC_POW: | 
|  | return expand_intrinsic_pow (callexp); | 
|  |  | 
|  | case INTRINSIC_TOPREC: | 
|  | case INTRINSIC_TOPRECF: | 
|  | case INTRINSIC_TOPRECL: | 
|  | return expand_intrinsic_toprec (callexp); | 
|  |  | 
|  | case INTRINSIC_VA_ARG: | 
|  | case INTRINSIC_C_VA_ARG: | 
|  | return expand_intrinsic_vaarg (callexp); | 
|  |  | 
|  | case INTRINSIC_VASTART: | 
|  | return expand_intrinsic_vastart (callexp); | 
|  |  | 
|  | case INTRINSIC_ADDS: | 
|  | case INTRINSIC_ADDSL: | 
|  | case INTRINSIC_ADDU: | 
|  | case INTRINSIC_ADDUL: | 
|  | case INTRINSIC_SUBS: | 
|  | case INTRINSIC_SUBSL: | 
|  | case INTRINSIC_SUBU: | 
|  | case INTRINSIC_SUBUL: | 
|  | case INTRINSIC_MULS: | 
|  | case INTRINSIC_MULSL: | 
|  | case INTRINSIC_MULU: | 
|  | case INTRINSIC_MULUI: | 
|  | case INTRINSIC_MULUL: | 
|  | case INTRINSIC_NEGS: | 
|  | case INTRINSIC_NEGSL: | 
|  | return expand_intrinsic_checkedint (intrinsic, callexp); | 
|  |  | 
|  | case INTRINSIC_VLOAD8: | 
|  | case INTRINSIC_VLOAD16: | 
|  | case INTRINSIC_VLOAD32: | 
|  | case INTRINSIC_VLOAD64: | 
|  | return expand_volatile_load (callexp); | 
|  |  | 
|  | case INTRINSIC_VSTORE8: | 
|  | case INTRINSIC_VSTORE16: | 
|  | case INTRINSIC_VSTORE32: | 
|  | case INTRINSIC_VSTORE64: | 
|  | return expand_volatile_store (callexp); | 
|  |  | 
|  | case INTRINSIC_LOADUNALIGNED: | 
|  | return expand_intrinsic_vec_load_unaligned (callexp); | 
|  |  | 
|  | case INTRINSIC_STOREUNALIGNED: | 
|  | return expand_intrinsic_vec_store_unaligned (callexp); | 
|  |  | 
|  | case INTRINSIC_SHUFFLE: | 
|  | return expand_intrinsic_vec_shuffle (callexp); | 
|  |  | 
|  | case INTRINSIC_SHUFFLEVECTOR: | 
|  | return expand_intrinsic_vec_shufflevector (callexp); | 
|  |  | 
|  | case INTRINSIC_CONVERTVECTOR: | 
|  | return expand_intrinsic_vec_convert (callexp); | 
|  |  | 
|  | case INTRINSIC_BLENDVECTOR: | 
|  | return expand_intrinsic_vec_blend (callexp); | 
|  |  | 
|  | default: | 
|  | gcc_unreachable (); | 
|  | } | 
|  | } |