190075Sobrien/* Expand builtin functions. 2132727Skan Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 3169699Skan 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. 490075Sobrien 590075SobrienThis file is part of GCC. 690075Sobrien 790075SobrienGCC is free software; you can redistribute it and/or modify it under 890075Sobrienthe terms of the GNU General Public License as published by the Free 990075SobrienSoftware Foundation; either version 2, or (at your option) any later 1090075Sobrienversion. 1190075Sobrien 1290075SobrienGCC is distributed in the hope that it will be useful, but WITHOUT ANY 1390075SobrienWARRANTY; without even the implied warranty of MERCHANTABILITY or 1490075SobrienFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1590075Sobrienfor more details. 1690075Sobrien 1790075SobrienYou should have received a copy of the GNU General Public License 1890075Sobrienalong with GCC; see the file COPYING. If not, write to the Free 19169699SkanSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 20169699Skan02110-1301, USA. */ 2190075Sobrien 2290075Sobrien#include "config.h" 2390075Sobrien#include "system.h" 24132727Skan#include "coretypes.h" 25132727Skan#include "tm.h" 2690075Sobrien#include "machmode.h" 27117404Skan#include "real.h" 2890075Sobrien#include "rtl.h" 2990075Sobrien#include "tree.h" 30169699Skan#include "tree-gimple.h" 3190075Sobrien#include "flags.h" 3290075Sobrien#include "regs.h" 3390075Sobrien#include "hard-reg-set.h" 3490075Sobrien#include "except.h" 3590075Sobrien#include "function.h" 3690075Sobrien#include "insn-config.h" 3790075Sobrien#include "expr.h" 3890075Sobrien#include "optabs.h" 3990075Sobrien#include "libfuncs.h" 4090075Sobrien#include "recog.h" 4190075Sobrien#include "output.h" 4290075Sobrien#include "typeclass.h" 4390075Sobrien#include "toplev.h" 4490075Sobrien#include "predict.h" 4590075Sobrien#include "tm_p.h" 4690075Sobrien#include "target.h" 47117404Skan#include "langhooks.h" 48169699Skan#include "basic-block.h" 49169699Skan#include "tree-mudflap.h" 5090075Sobrien 5190075Sobrien#ifndef PAD_VARARGS_DOWN 5290075Sobrien#define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN 5390075Sobrien#endif 5490075Sobrien 5590075Sobrien/* Define the names of the builtin function types and codes. */ 5690075Sobrienconst char *const built_in_class_names[4] 5790075Sobrien = {"NOT_BUILT_IN", "BUILT_IN_FRONTEND", "BUILT_IN_MD", "BUILT_IN_NORMAL"}; 5890075Sobrien 59169699Skan#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM, COND) #X, 60169699Skanconst char * built_in_names[(int) END_BUILTINS] = 6190075Sobrien{ 6290075Sobrien#include "builtins.def" 6390075Sobrien}; 6490075Sobrien#undef DEF_BUILTIN 6590075Sobrien 6690075Sobrien/* Setup an array of _DECL trees, make sure each element is 6790075Sobrien initialized to NULL_TREE. */ 6890075Sobrientree built_in_decls[(int) END_BUILTINS]; 69132727Skan/* Declarations used when constructing the builtin implicitly in the compiler. 70132727Skan It may be NULL_TREE when this is invalid (for instance runtime is not 71169699Skan required to implement the function call in all cases). */ 72132727Skantree implicit_built_in_decls[(int) END_BUILTINS]; 7390075Sobrien 74132727Skanstatic int get_pointer_alignment (tree, unsigned int); 75132727Skanstatic const char *c_getstr (tree); 76132727Skanstatic rtx c_readstr (const char *, enum machine_mode); 77132727Skanstatic int target_char_cast (tree, char *); 78169699Skanstatic rtx get_memory_rtx (tree, tree); 79132727Skanstatic int apply_args_size (void); 80132727Skanstatic int apply_result_size (void); 8190075Sobrien#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) 82132727Skanstatic rtx result_vector (int, rtx); 8390075Sobrien#endif 84169699Skanstatic void expand_builtin_update_setjmp_buf (rtx); 85132727Skanstatic void expand_builtin_prefetch (tree); 86132727Skanstatic rtx expand_builtin_apply_args (void); 87132727Skanstatic rtx expand_builtin_apply_args_1 (void); 88132727Skanstatic rtx expand_builtin_apply (rtx, rtx, rtx); 89132727Skanstatic void expand_builtin_return (rtx); 90132727Skanstatic enum type_class type_to_class (tree); 91132727Skanstatic rtx expand_builtin_classify_type (tree); 92132727Skanstatic void expand_errno_check (tree, rtx); 93132727Skanstatic rtx expand_builtin_mathfn (tree, rtx, rtx); 94132727Skanstatic rtx expand_builtin_mathfn_2 (tree, rtx, rtx); 95169699Skanstatic rtx expand_builtin_mathfn_3 (tree, rtx, rtx); 96169699Skanstatic rtx expand_builtin_sincos (tree); 97169699Skanstatic rtx expand_builtin_int_roundingfn (tree, rtx, rtx); 98132727Skanstatic rtx expand_builtin_args_info (tree); 99169699Skanstatic rtx expand_builtin_next_arg (void); 100132727Skanstatic rtx expand_builtin_va_start (tree); 101132727Skanstatic rtx expand_builtin_va_end (tree); 102132727Skanstatic rtx expand_builtin_va_copy (tree); 103132727Skanstatic rtx expand_builtin_memcmp (tree, tree, rtx, enum machine_mode); 104132727Skanstatic rtx expand_builtin_strcmp (tree, rtx, enum machine_mode); 105132727Skanstatic rtx expand_builtin_strncmp (tree, rtx, enum machine_mode); 106132727Skanstatic rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, enum machine_mode); 107169699Skanstatic rtx expand_builtin_strcat (tree, tree, rtx, enum machine_mode); 108132727Skanstatic rtx expand_builtin_strncat (tree, rtx, enum machine_mode); 109132727Skanstatic rtx expand_builtin_strspn (tree, rtx, enum machine_mode); 110132727Skanstatic rtx expand_builtin_strcspn (tree, rtx, enum machine_mode); 111132727Skanstatic rtx expand_builtin_memcpy (tree, rtx, enum machine_mode); 112169699Skanstatic rtx expand_builtin_mempcpy (tree, tree, rtx, enum machine_mode, int); 113169699Skanstatic rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode, tree); 114132727Skanstatic rtx expand_builtin_bcopy (tree); 115169699Skanstatic rtx expand_builtin_strcpy (tree, tree, rtx, enum machine_mode); 116132727Skanstatic rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode); 117132727Skanstatic rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode); 118132727Skanstatic rtx expand_builtin_strncpy (tree, rtx, enum machine_mode); 119132727Skanstatic rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode); 120132727Skanstatic rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode); 121169699Skanstatic rtx expand_builtin_memset (tree, rtx, enum machine_mode, tree); 122132727Skanstatic rtx expand_builtin_bzero (tree); 123132727Skanstatic rtx expand_builtin_strlen (tree, rtx, enum machine_mode); 124169699Skanstatic rtx expand_builtin_strstr (tree, tree, rtx, enum machine_mode); 125169699Skanstatic rtx expand_builtin_strpbrk (tree, tree, rtx, enum machine_mode); 126169699Skanstatic rtx expand_builtin_strchr (tree, tree, rtx, enum machine_mode); 127169699Skanstatic rtx expand_builtin_strrchr (tree, tree, rtx, enum machine_mode); 128132727Skanstatic rtx expand_builtin_alloca (tree, rtx); 129132727Skanstatic rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab); 130132727Skanstatic rtx expand_builtin_frame_address (tree, tree); 131132727Skanstatic rtx expand_builtin_fputs (tree, rtx, bool); 132132727Skanstatic rtx expand_builtin_printf (tree, rtx, enum machine_mode, bool); 133132727Skanstatic rtx expand_builtin_fprintf (tree, rtx, enum machine_mode, bool); 134132727Skanstatic rtx expand_builtin_sprintf (tree, rtx, enum machine_mode); 135132727Skanstatic tree stabilize_va_list (tree, int); 136132727Skanstatic rtx expand_builtin_expect (tree, rtx); 137132727Skanstatic tree fold_builtin_constant_p (tree); 138132727Skanstatic tree fold_builtin_classify_type (tree); 139169699Skanstatic tree fold_builtin_strlen (tree); 140132727Skanstatic tree fold_builtin_inf (tree, int); 141132727Skanstatic tree fold_builtin_nan (tree, tree, int); 142132727Skanstatic int validate_arglist (tree, ...); 143132727Skanstatic bool integer_valued_real_p (tree); 144169699Skanstatic tree fold_trunc_transparent_mathfn (tree, tree); 145132727Skanstatic bool readonly_data_expr (tree); 146132727Skanstatic rtx expand_builtin_fabs (tree, rtx, rtx); 147169699Skanstatic rtx expand_builtin_signbit (tree, rtx); 148169699Skanstatic tree fold_builtin_sqrt (tree, tree); 149169699Skanstatic tree fold_builtin_cbrt (tree, tree); 150169699Skanstatic tree fold_builtin_pow (tree, tree, tree); 151169699Skanstatic tree fold_builtin_powi (tree, tree, tree); 152169699Skanstatic tree fold_builtin_sin (tree); 153169699Skanstatic tree fold_builtin_cos (tree, tree, tree); 154169699Skanstatic tree fold_builtin_tan (tree); 155169699Skanstatic tree fold_builtin_atan (tree, tree); 156169699Skanstatic tree fold_builtin_trunc (tree, tree); 157169699Skanstatic tree fold_builtin_floor (tree, tree); 158169699Skanstatic tree fold_builtin_ceil (tree, tree); 159169699Skanstatic tree fold_builtin_round (tree, tree); 160169699Skanstatic tree fold_builtin_int_roundingfn (tree, tree); 161169699Skanstatic tree fold_builtin_bitop (tree, tree); 162169699Skanstatic tree fold_builtin_memory_op (tree, tree, bool, int); 163169699Skanstatic tree fold_builtin_strchr (tree, tree); 164132727Skanstatic tree fold_builtin_memcmp (tree); 165132727Skanstatic tree fold_builtin_strcmp (tree); 166132727Skanstatic tree fold_builtin_strncmp (tree); 167169699Skanstatic tree fold_builtin_signbit (tree, tree); 168169699Skanstatic tree fold_builtin_copysign (tree, tree, tree); 169169699Skanstatic tree fold_builtin_isascii (tree); 170169699Skanstatic tree fold_builtin_toascii (tree); 171169699Skanstatic tree fold_builtin_isdigit (tree); 172169699Skanstatic tree fold_builtin_fabs (tree, tree); 173169699Skanstatic tree fold_builtin_abs (tree, tree); 174169699Skanstatic tree fold_builtin_unordered_cmp (tree, tree, enum tree_code, 175169699Skan enum tree_code); 176169699Skanstatic tree fold_builtin_1 (tree, tree, bool); 17790075Sobrien 178169699Skanstatic tree fold_builtin_strpbrk (tree, tree); 179169699Skanstatic tree fold_builtin_strstr (tree, tree); 180169699Skanstatic tree fold_builtin_strrchr (tree, tree); 181169699Skanstatic tree fold_builtin_strcat (tree); 182169699Skanstatic tree fold_builtin_strncat (tree); 183169699Skanstatic tree fold_builtin_strspn (tree); 184169699Skanstatic tree fold_builtin_strcspn (tree); 185169699Skanstatic tree fold_builtin_sprintf (tree, int); 186169699Skan 187169699Skanstatic rtx expand_builtin_object_size (tree); 188169699Skanstatic rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode, 189169699Skan enum built_in_function); 190169699Skanstatic void maybe_emit_chk_warning (tree, enum built_in_function); 191169699Skanstatic void maybe_emit_sprintf_chk_warning (tree, enum built_in_function); 192169699Skanstatic tree fold_builtin_object_size (tree); 193169699Skanstatic tree fold_builtin_strcat_chk (tree, tree); 194169699Skanstatic tree fold_builtin_strncat_chk (tree, tree); 195169699Skanstatic tree fold_builtin_sprintf_chk (tree, enum built_in_function); 196169699Skanstatic tree fold_builtin_printf (tree, tree, bool, enum built_in_function); 197169699Skanstatic tree fold_builtin_fprintf (tree, tree, bool, enum built_in_function); 198169699Skanstatic bool init_target_chars (void); 199169699Skan 200169699Skanstatic unsigned HOST_WIDE_INT target_newline; 201169699Skanstatic unsigned HOST_WIDE_INT target_percent; 202169699Skanstatic unsigned HOST_WIDE_INT target_c; 203169699Skanstatic unsigned HOST_WIDE_INT target_s; 204169699Skanstatic char target_percent_c[3]; 205169699Skanstatic char target_percent_s[3]; 206169699Skanstatic char target_percent_s_newline[4]; 207169699Skan 208169699Skan/* Return true if NODE should be considered for inline expansion regardless 209169699Skan of the optimization level. This means whenever a function is invoked with 210169699Skan its "internal" name, which normally contains the prefix "__builtin". */ 211169699Skan 212169699Skanstatic bool called_as_built_in (tree node) 213169699Skan{ 214169699Skan const char *name = IDENTIFIER_POINTER (DECL_NAME (node)); 215169699Skan if (strncmp (name, "__builtin_", 10) == 0) 216169699Skan return true; 217169699Skan if (strncmp (name, "__sync_", 7) == 0) 218169699Skan return true; 219169699Skan return false; 220169699Skan} 221169699Skan 22290075Sobrien/* Return the alignment in bits of EXP, a pointer valued expression. 22390075Sobrien But don't return more than MAX_ALIGN no matter what. 22490075Sobrien The alignment returned is, by default, the alignment of the thing that 22590075Sobrien EXP points to. If it is not a POINTER_TYPE, 0 is returned. 22690075Sobrien 22790075Sobrien Otherwise, look at the expression to see if we can do better, i.e., if the 22890075Sobrien expression is actually pointing at an object whose alignment is tighter. */ 22990075Sobrien 23090075Sobrienstatic int 231132727Skanget_pointer_alignment (tree exp, unsigned int max_align) 23290075Sobrien{ 23390075Sobrien unsigned int align, inner; 23490075Sobrien 235169699Skan /* We rely on TER to compute accurate alignment information. */ 236169699Skan if (!(optimize && flag_tree_ter)) 23790075Sobrien return 0; 23890075Sobrien 239169699Skan if (!POINTER_TYPE_P (TREE_TYPE (exp))) 240169699Skan return 0; 241169699Skan 24290075Sobrien align = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); 24390075Sobrien align = MIN (align, max_align); 24490075Sobrien 24590075Sobrien while (1) 24690075Sobrien { 24790075Sobrien switch (TREE_CODE (exp)) 24890075Sobrien { 24990075Sobrien case NOP_EXPR: 25090075Sobrien case CONVERT_EXPR: 25190075Sobrien case NON_LVALUE_EXPR: 25290075Sobrien exp = TREE_OPERAND (exp, 0); 253169699Skan if (! POINTER_TYPE_P (TREE_TYPE (exp))) 25490075Sobrien return align; 25590075Sobrien 25690075Sobrien inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); 25790075Sobrien align = MIN (inner, max_align); 25890075Sobrien break; 25990075Sobrien 26090075Sobrien case PLUS_EXPR: 26190075Sobrien /* If sum of pointer + int, restrict our maximum alignment to that 26290075Sobrien imposed by the integer. If not, we can't do any better than 26390075Sobrien ALIGN. */ 26490075Sobrien if (! host_integerp (TREE_OPERAND (exp, 1), 1)) 26590075Sobrien return align; 26690075Sobrien 26790075Sobrien while (((tree_low_cst (TREE_OPERAND (exp, 1), 1)) 26890075Sobrien & (max_align / BITS_PER_UNIT - 1)) 26990075Sobrien != 0) 27090075Sobrien max_align >>= 1; 27190075Sobrien 27290075Sobrien exp = TREE_OPERAND (exp, 0); 27390075Sobrien break; 27490075Sobrien 27590075Sobrien case ADDR_EXPR: 27690075Sobrien /* See what we are pointing at and look at its alignment. */ 27790075Sobrien exp = TREE_OPERAND (exp, 0); 278169699Skan inner = max_align; 279169699Skan if (handled_component_p (exp)) 280169699Skan { 281169699Skan HOST_WIDE_INT bitsize, bitpos; 282169699Skan tree offset; 283169699Skan enum machine_mode mode; 284169699Skan int unsignedp, volatilep; 285169699Skan 286169699Skan exp = get_inner_reference (exp, &bitsize, &bitpos, &offset, 287169699Skan &mode, &unsignedp, &volatilep, true); 288169699Skan if (bitpos) 289169699Skan inner = MIN (inner, (unsigned) (bitpos & -bitpos)); 290169699Skan if (offset && TREE_CODE (offset) == PLUS_EXPR 291169699Skan && host_integerp (TREE_OPERAND (offset, 1), 1)) 292169699Skan { 293169699Skan /* Any overflow in calculating offset_bits won't change 294169699Skan the alignment. */ 295169699Skan unsigned offset_bits 296169699Skan = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1) 297169699Skan * BITS_PER_UNIT); 298169699Skan 299169699Skan if (offset_bits) 300169699Skan inner = MIN (inner, (offset_bits & -offset_bits)); 301169699Skan offset = TREE_OPERAND (offset, 0); 302169699Skan } 303169699Skan if (offset && TREE_CODE (offset) == MULT_EXPR 304169699Skan && host_integerp (TREE_OPERAND (offset, 1), 1)) 305169699Skan { 306169699Skan /* Any overflow in calculating offset_factor won't change 307169699Skan the alignment. */ 308169699Skan unsigned offset_factor 309169699Skan = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1) 310169699Skan * BITS_PER_UNIT); 311169699Skan 312169699Skan if (offset_factor) 313169699Skan inner = MIN (inner, (offset_factor & -offset_factor)); 314169699Skan } 315169699Skan else if (offset) 316169699Skan inner = MIN (inner, BITS_PER_UNIT); 317169699Skan } 318259694Spfg if (DECL_P (exp)) 319169699Skan align = MIN (inner, DECL_ALIGN (exp)); 32090075Sobrien#ifdef CONSTANT_ALIGNMENT 321169699Skan else if (CONSTANT_CLASS_P (exp)) 322169699Skan align = MIN (inner, (unsigned)CONSTANT_ALIGNMENT (exp, align)); 32390075Sobrien#endif 324169699Skan else if (TREE_CODE (exp) == VIEW_CONVERT_EXPR 325169699Skan || TREE_CODE (exp) == INDIRECT_REF) 326169699Skan align = MIN (TYPE_ALIGN (TREE_TYPE (exp)), inner); 327169699Skan else 328169699Skan align = MIN (align, inner); 32990075Sobrien return MIN (align, max_align); 33090075Sobrien 33190075Sobrien default: 33290075Sobrien return align; 33390075Sobrien } 33490075Sobrien } 33590075Sobrien} 33690075Sobrien 33790075Sobrien/* Compute the length of a C string. TREE_STRING_LENGTH is not the right 33890075Sobrien way, because it could contain a zero byte in the middle. 33990075Sobrien TREE_STRING_LENGTH is the size of the character array, not the string. 34090075Sobrien 341132727Skan ONLY_VALUE should be nonzero if the result is not going to be emitted 342132727Skan into the instruction stream and zero if it is going to be expanded. 343132727Skan E.g. with i++ ? "foo" : "bar", if ONLY_VALUE is nonzero, constant 3 344132727Skan is returned, otherwise NULL, since 345132727Skan len = c_strlen (src, 1); if (len) expand_expr (len, ...); would not 346132727Skan evaluate the side-effects. 347132727Skan 34890075Sobrien The value returned is of type `ssizetype'. 34990075Sobrien 35090075Sobrien Unfortunately, string_constant can't access the values of const char 35190075Sobrien arrays with initializers, so neither can we do so here. */ 35290075Sobrien 353169699Skantree 354132727Skanc_strlen (tree src, int only_value) 35590075Sobrien{ 35690075Sobrien tree offset_node; 35790075Sobrien HOST_WIDE_INT offset; 35890075Sobrien int max; 35990075Sobrien const char *ptr; 36090075Sobrien 361132727Skan STRIP_NOPS (src); 362132727Skan if (TREE_CODE (src) == COND_EXPR 363132727Skan && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) 364132727Skan { 365132727Skan tree len1, len2; 366132727Skan 367132727Skan len1 = c_strlen (TREE_OPERAND (src, 1), only_value); 368132727Skan len2 = c_strlen (TREE_OPERAND (src, 2), only_value); 369169699Skan if (tree_int_cst_equal (len1, len2)) 370132727Skan return len1; 371132727Skan } 372132727Skan 373132727Skan if (TREE_CODE (src) == COMPOUND_EXPR 374132727Skan && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) 375132727Skan return c_strlen (TREE_OPERAND (src, 1), only_value); 376132727Skan 37790075Sobrien src = string_constant (src, &offset_node); 37890075Sobrien if (src == 0) 37990075Sobrien return 0; 38090075Sobrien 38190075Sobrien max = TREE_STRING_LENGTH (src) - 1; 38290075Sobrien ptr = TREE_STRING_POINTER (src); 38390075Sobrien 38490075Sobrien if (offset_node && TREE_CODE (offset_node) != INTEGER_CST) 38590075Sobrien { 38690075Sobrien /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't 38790075Sobrien compute the offset to the following null if we don't know where to 38890075Sobrien start searching for it. */ 38990075Sobrien int i; 39090075Sobrien 39190075Sobrien for (i = 0; i < max; i++) 39290075Sobrien if (ptr[i] == 0) 39390075Sobrien return 0; 39490075Sobrien 39590075Sobrien /* We don't know the starting offset, but we do know that the string 39690075Sobrien has no internal zero bytes. We can assume that the offset falls 39790075Sobrien within the bounds of the string; otherwise, the programmer deserves 39890075Sobrien what he gets. Subtract the offset from the length of the string, 39990075Sobrien and return that. This would perhaps not be valid if we were dealing 40090075Sobrien with named arrays in addition to literal string constants. */ 40190075Sobrien 40290075Sobrien return size_diffop (size_int (max), offset_node); 40390075Sobrien } 40490075Sobrien 40590075Sobrien /* We have a known offset into the string. Start searching there for 40690075Sobrien a null character if we can represent it as a single HOST_WIDE_INT. */ 40790075Sobrien if (offset_node == 0) 40890075Sobrien offset = 0; 40990075Sobrien else if (! host_integerp (offset_node, 0)) 41090075Sobrien offset = -1; 41190075Sobrien else 41290075Sobrien offset = tree_low_cst (offset_node, 0); 41390075Sobrien 41490075Sobrien /* If the offset is known to be out of bounds, warn, and call strlen at 41590075Sobrien runtime. */ 41690075Sobrien if (offset < 0 || offset > max) 41790075Sobrien { 418169699Skan warning (0, "offset outside bounds of constant string"); 41990075Sobrien return 0; 42090075Sobrien } 42190075Sobrien 42290075Sobrien /* Use strlen to search for the first zero byte. Since any strings 42390075Sobrien constructed with build_string will have nulls appended, we win even 42490075Sobrien if we get handed something like (char[4])"abcd". 42590075Sobrien 42690075Sobrien Since OFFSET is our starting index into the string, no further 42790075Sobrien calculation is needed. */ 42890075Sobrien return ssize_int (strlen (ptr + offset)); 42990075Sobrien} 43090075Sobrien 43190075Sobrien/* Return a char pointer for a C string if it is a string constant 43290075Sobrien or sum of string constant and integer constant. */ 43390075Sobrien 43490075Sobrienstatic const char * 435132727Skanc_getstr (tree src) 43690075Sobrien{ 43790075Sobrien tree offset_node; 43890075Sobrien 43990075Sobrien src = string_constant (src, &offset_node); 44090075Sobrien if (src == 0) 44190075Sobrien return 0; 44290075Sobrien 44390075Sobrien if (offset_node == 0) 44490075Sobrien return TREE_STRING_POINTER (src); 44590075Sobrien else if (!host_integerp (offset_node, 1) 44690075Sobrien || compare_tree_int (offset_node, TREE_STRING_LENGTH (src) - 1) > 0) 44790075Sobrien return 0; 44890075Sobrien 44990075Sobrien return TREE_STRING_POINTER (src) + tree_low_cst (offset_node, 1); 45090075Sobrien} 45190075Sobrien 45290075Sobrien/* Return a CONST_INT or CONST_DOUBLE corresponding to target reading 45390075Sobrien GET_MODE_BITSIZE (MODE) bits from string constant STR. */ 45490075Sobrien 45590075Sobrienstatic rtx 456132727Skanc_readstr (const char *str, enum machine_mode mode) 45790075Sobrien{ 45890075Sobrien HOST_WIDE_INT c[2]; 45990075Sobrien HOST_WIDE_INT ch; 46090075Sobrien unsigned int i, j; 46190075Sobrien 462169699Skan gcc_assert (GET_MODE_CLASS (mode) == MODE_INT); 463169699Skan 46490075Sobrien c[0] = 0; 46590075Sobrien c[1] = 0; 46690075Sobrien ch = 1; 46790075Sobrien for (i = 0; i < GET_MODE_SIZE (mode); i++) 46890075Sobrien { 46990075Sobrien j = i; 47090075Sobrien if (WORDS_BIG_ENDIAN) 47190075Sobrien j = GET_MODE_SIZE (mode) - i - 1; 47290075Sobrien if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN 47390075Sobrien && GET_MODE_SIZE (mode) > UNITS_PER_WORD) 47490075Sobrien j = j + UNITS_PER_WORD - 2 * (j % UNITS_PER_WORD) - 1; 47590075Sobrien j *= BITS_PER_UNIT; 476169699Skan gcc_assert (j <= 2 * HOST_BITS_PER_WIDE_INT); 477169699Skan 47890075Sobrien if (ch) 47990075Sobrien ch = (unsigned char) str[i]; 48090075Sobrien c[j / HOST_BITS_PER_WIDE_INT] |= ch << (j % HOST_BITS_PER_WIDE_INT); 48190075Sobrien } 48290075Sobrien return immed_double_const (c[0], c[1], mode); 48390075Sobrien} 48490075Sobrien 48590075Sobrien/* Cast a target constant CST to target CHAR and if that value fits into 486169699Skan host char type, return zero and put that value into variable pointed to by 48790075Sobrien P. */ 48890075Sobrien 48990075Sobrienstatic int 490132727Skantarget_char_cast (tree cst, char *p) 49190075Sobrien{ 49290075Sobrien unsigned HOST_WIDE_INT val, hostval; 49390075Sobrien 49490075Sobrien if (!host_integerp (cst, 1) 49590075Sobrien || CHAR_TYPE_SIZE > HOST_BITS_PER_WIDE_INT) 49690075Sobrien return 1; 49790075Sobrien 49890075Sobrien val = tree_low_cst (cst, 1); 49990075Sobrien if (CHAR_TYPE_SIZE < HOST_BITS_PER_WIDE_INT) 50090075Sobrien val &= (((unsigned HOST_WIDE_INT) 1) << CHAR_TYPE_SIZE) - 1; 50190075Sobrien 50290075Sobrien hostval = val; 50390075Sobrien if (HOST_BITS_PER_CHAR < HOST_BITS_PER_WIDE_INT) 50490075Sobrien hostval &= (((unsigned HOST_WIDE_INT) 1) << HOST_BITS_PER_CHAR) - 1; 50590075Sobrien 50690075Sobrien if (val != hostval) 50790075Sobrien return 1; 50890075Sobrien 50990075Sobrien *p = hostval; 51090075Sobrien return 0; 51190075Sobrien} 51290075Sobrien 513169699Skan/* Similar to save_expr, but assumes that arbitrary code is not executed 514169699Skan in between the multiple evaluations. In particular, we assume that a 515169699Skan non-addressable local variable will not be modified. */ 516169699Skan 517169699Skanstatic tree 518169699Skanbuiltin_save_expr (tree exp) 519169699Skan{ 520169699Skan if (TREE_ADDRESSABLE (exp) == 0 521169699Skan && (TREE_CODE (exp) == PARM_DECL 522169699Skan || (TREE_CODE (exp) == VAR_DECL && !TREE_STATIC (exp)))) 523169699Skan return exp; 524169699Skan 525169699Skan return save_expr (exp); 526169699Skan} 527169699Skan 52890075Sobrien/* Given TEM, a pointer to a stack frame, follow the dynamic chain COUNT 52990075Sobrien times to get the address of either a higher stack frame, or a return 53090075Sobrien address located within it (depending on FNDECL_CODE). */ 53190075Sobrien 532169699Skanstatic rtx 533169699Skanexpand_builtin_return_addr (enum built_in_function fndecl_code, int count) 53490075Sobrien{ 53590075Sobrien int i; 53690075Sobrien 537169699Skan#ifdef INITIAL_FRAME_ADDRESS_RTX 538169699Skan rtx tem = INITIAL_FRAME_ADDRESS_RTX; 539169699Skan#else 540169699Skan rtx tem; 541169699Skan 542169699Skan /* For a zero count with __builtin_return_address, we don't care what 543169699Skan frame address we return, because target-specific definitions will 544169699Skan override us. Therefore frame pointer elimination is OK, and using 545169699Skan the soft frame pointer is OK. 546169699Skan 547169699Skan For a non-zero count, or a zero count with __builtin_frame_address, 548169699Skan we require a stable offset from the current frame pointer to the 549169699Skan previous one, so we must use the hard frame pointer, and 550169699Skan we must disable frame pointer elimination. */ 551169699Skan if (count == 0 && fndecl_code == BUILT_IN_RETURN_ADDRESS) 552169699Skan tem = frame_pointer_rtx; 553169699Skan else 554169699Skan { 555169699Skan tem = hard_frame_pointer_rtx; 556169699Skan 557169699Skan /* Tell reload not to eliminate the frame pointer. */ 558169699Skan current_function_accesses_prior_frames = 1; 559169699Skan } 560169699Skan#endif 561169699Skan 56290075Sobrien /* Some machines need special handling before we can access 563169699Skan arbitrary frames. For example, on the SPARC, we must first flush 56490075Sobrien all register windows to the stack. */ 56590075Sobrien#ifdef SETUP_FRAME_ADDRESSES 56690075Sobrien if (count > 0) 56790075Sobrien SETUP_FRAME_ADDRESSES (); 56890075Sobrien#endif 56990075Sobrien 570169699Skan /* On the SPARC, the return address is not in the frame, it is in a 57190075Sobrien register. There is no way to access it off of the current frame 57290075Sobrien pointer, but it can be accessed off the previous frame pointer by 57390075Sobrien reading the value from the register window save area. */ 57490075Sobrien#ifdef RETURN_ADDR_IN_PREVIOUS_FRAME 57590075Sobrien if (fndecl_code == BUILT_IN_RETURN_ADDRESS) 57690075Sobrien count--; 57790075Sobrien#endif 57890075Sobrien 57990075Sobrien /* Scan back COUNT frames to the specified frame. */ 58090075Sobrien for (i = 0; i < count; i++) 58190075Sobrien { 58290075Sobrien /* Assume the dynamic chain pointer is in the word that the 58390075Sobrien frame address points to, unless otherwise specified. */ 58490075Sobrien#ifdef DYNAMIC_CHAIN_ADDRESS 58590075Sobrien tem = DYNAMIC_CHAIN_ADDRESS (tem); 58690075Sobrien#endif 58790075Sobrien tem = memory_address (Pmode, tem); 588169699Skan tem = gen_frame_mem (Pmode, tem); 58990075Sobrien tem = copy_to_reg (tem); 59090075Sobrien } 59190075Sobrien 592169699Skan /* For __builtin_frame_address, return what we've got. But, on 593169699Skan the SPARC for example, we may have to add a bias. */ 59490075Sobrien if (fndecl_code == BUILT_IN_FRAME_ADDRESS) 595169699Skan#ifdef FRAME_ADDR_RTX 596169699Skan return FRAME_ADDR_RTX (tem); 597169699Skan#else 59890075Sobrien return tem; 599169699Skan#endif 60090075Sobrien 601169699Skan /* For __builtin_return_address, get the return address from that frame. */ 60290075Sobrien#ifdef RETURN_ADDR_RTX 60390075Sobrien tem = RETURN_ADDR_RTX (count, tem); 60490075Sobrien#else 60590075Sobrien tem = memory_address (Pmode, 60690075Sobrien plus_constant (tem, GET_MODE_SIZE (Pmode))); 607169699Skan tem = gen_frame_mem (Pmode, tem); 60890075Sobrien#endif 60990075Sobrien return tem; 61090075Sobrien} 61190075Sobrien 61290075Sobrien/* Alias set used for setjmp buffer. */ 61390075Sobrienstatic HOST_WIDE_INT setjmp_alias_set = -1; 61490075Sobrien 61590075Sobrien/* Construct the leading half of a __builtin_setjmp call. Control will 616169699Skan return to RECEIVER_LABEL. This is also called directly by the SJLJ 617169699Skan exception handling code. */ 61890075Sobrien 61990075Sobrienvoid 620132727Skanexpand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) 62190075Sobrien{ 62290075Sobrien enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); 62390075Sobrien rtx stack_save; 62490075Sobrien rtx mem; 62590075Sobrien 62690075Sobrien if (setjmp_alias_set == -1) 62790075Sobrien setjmp_alias_set = new_alias_set (); 62890075Sobrien 629132727Skan buf_addr = convert_memory_address (Pmode, buf_addr); 63090075Sobrien 63190075Sobrien buf_addr = force_reg (Pmode, force_operand (buf_addr, NULL_RTX)); 63290075Sobrien 63390075Sobrien /* We store the frame pointer and the address of receiver_label in 63490075Sobrien the buffer and use the rest of it for the stack save area, which 63590075Sobrien is machine-dependent. */ 63690075Sobrien 63790075Sobrien mem = gen_rtx_MEM (Pmode, buf_addr); 63890075Sobrien set_mem_alias_set (mem, setjmp_alias_set); 639169699Skan emit_move_insn (mem, targetm.builtin_setjmp_frame_value ()); 64090075Sobrien 64190075Sobrien mem = gen_rtx_MEM (Pmode, plus_constant (buf_addr, GET_MODE_SIZE (Pmode))), 64290075Sobrien set_mem_alias_set (mem, setjmp_alias_set); 64390075Sobrien 64490075Sobrien emit_move_insn (validize_mem (mem), 64590075Sobrien force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label))); 64690075Sobrien 64790075Sobrien stack_save = gen_rtx_MEM (sa_mode, 64890075Sobrien plus_constant (buf_addr, 64990075Sobrien 2 * GET_MODE_SIZE (Pmode))); 65090075Sobrien set_mem_alias_set (stack_save, setjmp_alias_set); 65190075Sobrien emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX); 65290075Sobrien 65390075Sobrien /* If there is further processing to do, do it. */ 65490075Sobrien#ifdef HAVE_builtin_setjmp_setup 65590075Sobrien if (HAVE_builtin_setjmp_setup) 65690075Sobrien emit_insn (gen_builtin_setjmp_setup (buf_addr)); 65790075Sobrien#endif 65890075Sobrien 65990075Sobrien /* Tell optimize_save_area_alloca that extra work is going to 66090075Sobrien need to go on during alloca. */ 66190075Sobrien current_function_calls_setjmp = 1; 66290075Sobrien 66390075Sobrien /* Set this so all the registers get saved in our frame; we need to be 66490075Sobrien able to copy the saved values for any registers from frames we unwind. */ 66590075Sobrien current_function_has_nonlocal_label = 1; 66690075Sobrien} 66790075Sobrien 668169699Skan/* Construct the trailing part of a __builtin_setjmp call. This is 669169699Skan also called directly by the SJLJ exception handling code. */ 67090075Sobrien 67190075Sobrienvoid 672132727Skanexpand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED) 67390075Sobrien{ 67490075Sobrien /* Clobber the FP when we get here, so we have to make sure it's 67590075Sobrien marked as used by this function. */ 67690075Sobrien emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); 67790075Sobrien 67890075Sobrien /* Mark the static chain as clobbered here so life information 67990075Sobrien doesn't get messed up for it. */ 68090075Sobrien emit_insn (gen_rtx_CLOBBER (VOIDmode, static_chain_rtx)); 68190075Sobrien 68290075Sobrien /* Now put in the code to restore the frame pointer, and argument 683169699Skan pointer, if needed. */ 68490075Sobrien#ifdef HAVE_nonlocal_goto 68590075Sobrien if (! HAVE_nonlocal_goto) 68690075Sobrien#endif 687169699Skan { 688169699Skan emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx); 689169699Skan /* This might change the hard frame pointer in ways that aren't 690169699Skan apparent to early optimization passes, so force a clobber. */ 691169699Skan emit_insn (gen_rtx_CLOBBER (VOIDmode, hard_frame_pointer_rtx)); 692169699Skan } 69390075Sobrien 69490075Sobrien#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM 69590075Sobrien if (fixed_regs[ARG_POINTER_REGNUM]) 69690075Sobrien { 69790075Sobrien#ifdef ELIMINABLE_REGS 69890075Sobrien size_t i; 69990075Sobrien static const struct elims {const int from, to;} elim_regs[] = ELIMINABLE_REGS; 70090075Sobrien 70190075Sobrien for (i = 0; i < ARRAY_SIZE (elim_regs); i++) 70290075Sobrien if (elim_regs[i].from == ARG_POINTER_REGNUM 70390075Sobrien && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM) 70490075Sobrien break; 70590075Sobrien 70690075Sobrien if (i == ARRAY_SIZE (elim_regs)) 70790075Sobrien#endif 70890075Sobrien { 70990075Sobrien /* Now restore our arg pointer from the address at which it 71090075Sobrien was saved in our stack frame. */ 71190075Sobrien emit_move_insn (virtual_incoming_args_rtx, 71290075Sobrien copy_to_reg (get_arg_pointer_save_area (cfun))); 71390075Sobrien } 71490075Sobrien } 71590075Sobrien#endif 71690075Sobrien 71790075Sobrien#ifdef HAVE_builtin_setjmp_receiver 71890075Sobrien if (HAVE_builtin_setjmp_receiver) 71990075Sobrien emit_insn (gen_builtin_setjmp_receiver (receiver_label)); 72090075Sobrien else 72190075Sobrien#endif 72290075Sobrien#ifdef HAVE_nonlocal_goto_receiver 72390075Sobrien if (HAVE_nonlocal_goto_receiver) 72490075Sobrien emit_insn (gen_nonlocal_goto_receiver ()); 72590075Sobrien else 72690075Sobrien#endif 72790075Sobrien { /* Nothing */ } 72890075Sobrien 72990075Sobrien /* @@@ This is a kludge. Not all machine descriptions define a blockage 73090075Sobrien insn, but we must not allow the code we just generated to be reordered 73190075Sobrien by scheduling. Specifically, the update of the frame pointer must 73290075Sobrien happen immediately, not later. So emit an ASM_INPUT to act as blockage 73390075Sobrien insn. */ 73490075Sobrien emit_insn (gen_rtx_ASM_INPUT (VOIDmode, "")); 73590075Sobrien} 73690075Sobrien 73790075Sobrien/* __builtin_longjmp is passed a pointer to an array of five words (not 73890075Sobrien all will be used on all machines). It operates similarly to the C 73990075Sobrien library function of the same name, but is more efficient. Much of 740169699Skan the code below is copied from the handling of non-local gotos. */ 74190075Sobrien 742169699Skanstatic void 743132727Skanexpand_builtin_longjmp (rtx buf_addr, rtx value) 74490075Sobrien{ 745117404Skan rtx fp, lab, stack, insn, last; 74690075Sobrien enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); 74790075Sobrien 74890075Sobrien if (setjmp_alias_set == -1) 74990075Sobrien setjmp_alias_set = new_alias_set (); 75090075Sobrien 751132727Skan buf_addr = convert_memory_address (Pmode, buf_addr); 75290075Sobrien 75390075Sobrien buf_addr = force_reg (Pmode, buf_addr); 75490075Sobrien 75590075Sobrien /* We used to store value in static_chain_rtx, but that fails if pointers 75690075Sobrien are smaller than integers. We instead require that the user must pass 75790075Sobrien a second argument of 1, because that is what builtin_setjmp will 75890075Sobrien return. This also makes EH slightly more efficient, since we are no 75990075Sobrien longer copying around a value that we don't care about. */ 760169699Skan gcc_assert (value == const1_rtx); 76190075Sobrien 762117404Skan last = get_last_insn (); 76390075Sobrien#ifdef HAVE_builtin_longjmp 76490075Sobrien if (HAVE_builtin_longjmp) 76590075Sobrien emit_insn (gen_builtin_longjmp (buf_addr)); 76690075Sobrien else 76790075Sobrien#endif 76890075Sobrien { 76990075Sobrien fp = gen_rtx_MEM (Pmode, buf_addr); 77090075Sobrien lab = gen_rtx_MEM (Pmode, plus_constant (buf_addr, 77190075Sobrien GET_MODE_SIZE (Pmode))); 77290075Sobrien 77390075Sobrien stack = gen_rtx_MEM (sa_mode, plus_constant (buf_addr, 77490075Sobrien 2 * GET_MODE_SIZE (Pmode))); 77590075Sobrien set_mem_alias_set (fp, setjmp_alias_set); 77690075Sobrien set_mem_alias_set (lab, setjmp_alias_set); 77790075Sobrien set_mem_alias_set (stack, setjmp_alias_set); 77890075Sobrien 77990075Sobrien /* Pick up FP, label, and SP from the block and jump. This code is 78090075Sobrien from expand_goto in stmt.c; see there for detailed comments. */ 781169699Skan#ifdef HAVE_nonlocal_goto 78290075Sobrien if (HAVE_nonlocal_goto) 78390075Sobrien /* We have to pass a value to the nonlocal_goto pattern that will 78490075Sobrien get copied into the static_chain pointer, but it does not matter 78590075Sobrien what that value is, because builtin_setjmp does not use it. */ 78690075Sobrien emit_insn (gen_nonlocal_goto (value, lab, stack, fp)); 78790075Sobrien else 78890075Sobrien#endif 78990075Sobrien { 79090075Sobrien lab = copy_to_reg (lab); 79190075Sobrien 792132727Skan emit_insn (gen_rtx_CLOBBER (VOIDmode, 793132727Skan gen_rtx_MEM (BLKmode, 794132727Skan gen_rtx_SCRATCH (VOIDmode)))); 795132727Skan emit_insn (gen_rtx_CLOBBER (VOIDmode, 796132727Skan gen_rtx_MEM (BLKmode, 797132727Skan hard_frame_pointer_rtx))); 798132727Skan 79990075Sobrien emit_move_insn (hard_frame_pointer_rtx, fp); 80090075Sobrien emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX); 80190075Sobrien 80290075Sobrien emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); 80390075Sobrien emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx)); 80490075Sobrien emit_indirect_jump (lab); 80590075Sobrien } 80690075Sobrien } 80790075Sobrien 80890075Sobrien /* Search backwards and mark the jump insn as a non-local goto. 80990075Sobrien Note that this precludes the use of __builtin_longjmp to a 81090075Sobrien __builtin_setjmp target in the same function. However, we've 81190075Sobrien already cautioned the user that these functions are for 81290075Sobrien internal exception handling use only. */ 81390075Sobrien for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) 81490075Sobrien { 815169699Skan gcc_assert (insn != last); 816169699Skan 817169699Skan if (JUMP_P (insn)) 81890075Sobrien { 81990075Sobrien REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO, const0_rtx, 82090075Sobrien REG_NOTES (insn)); 82190075Sobrien break; 82290075Sobrien } 823169699Skan else if (CALL_P (insn)) 824117404Skan break; 82590075Sobrien } 82690075Sobrien} 82790075Sobrien 828169699Skan/* Expand a call to __builtin_nonlocal_goto. We're passed the target label 829169699Skan and the address of the save area. */ 830169699Skan 831169699Skanstatic rtx 832169699Skanexpand_builtin_nonlocal_goto (tree arglist) 833169699Skan{ 834169699Skan tree t_label, t_save_area; 835169699Skan rtx r_label, r_save_area, r_fp, r_sp, insn; 836169699Skan 837169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 838169699Skan return NULL_RTX; 839169699Skan 840169699Skan t_label = TREE_VALUE (arglist); 841169699Skan arglist = TREE_CHAIN (arglist); 842169699Skan t_save_area = TREE_VALUE (arglist); 843169699Skan 844169699Skan r_label = expand_normal (t_label); 845169699Skan r_label = convert_memory_address (Pmode, r_label); 846169699Skan r_save_area = expand_normal (t_save_area); 847169699Skan r_save_area = convert_memory_address (Pmode, r_save_area); 848169699Skan r_fp = gen_rtx_MEM (Pmode, r_save_area); 849169699Skan r_sp = gen_rtx_MEM (STACK_SAVEAREA_MODE (SAVE_NONLOCAL), 850169699Skan plus_constant (r_save_area, GET_MODE_SIZE (Pmode))); 851169699Skan 852169699Skan current_function_has_nonlocal_goto = 1; 853169699Skan 854169699Skan#ifdef HAVE_nonlocal_goto 855169699Skan /* ??? We no longer need to pass the static chain value, afaik. */ 856169699Skan if (HAVE_nonlocal_goto) 857169699Skan emit_insn (gen_nonlocal_goto (const0_rtx, r_label, r_sp, r_fp)); 858169699Skan else 859169699Skan#endif 860169699Skan { 861169699Skan r_label = copy_to_reg (r_label); 862169699Skan 863169699Skan emit_insn (gen_rtx_CLOBBER (VOIDmode, 864169699Skan gen_rtx_MEM (BLKmode, 865169699Skan gen_rtx_SCRATCH (VOIDmode)))); 866169699Skan 867169699Skan emit_insn (gen_rtx_CLOBBER (VOIDmode, 868169699Skan gen_rtx_MEM (BLKmode, 869169699Skan hard_frame_pointer_rtx))); 870169699Skan 871169699Skan /* Restore frame pointer for containing function. 872169699Skan This sets the actual hard register used for the frame pointer 873169699Skan to the location of the function's incoming static chain info. 874169699Skan The non-local goto handler will then adjust it to contain the 875169699Skan proper value and reload the argument pointer, if needed. */ 876169699Skan emit_move_insn (hard_frame_pointer_rtx, r_fp); 877169699Skan emit_stack_restore (SAVE_NONLOCAL, r_sp, NULL_RTX); 878169699Skan 879169699Skan /* USE of hard_frame_pointer_rtx added for consistency; 880169699Skan not clear if really needed. */ 881169699Skan emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); 882169699Skan emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx)); 883169699Skan emit_indirect_jump (r_label); 884169699Skan } 885169699Skan 886169699Skan /* Search backwards to the jump insn and mark it as a 887169699Skan non-local goto. */ 888169699Skan for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) 889169699Skan { 890169699Skan if (JUMP_P (insn)) 891169699Skan { 892169699Skan REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO, 893169699Skan const0_rtx, REG_NOTES (insn)); 894169699Skan break; 895169699Skan } 896169699Skan else if (CALL_P (insn)) 897169699Skan break; 898169699Skan } 899169699Skan 900169699Skan return const0_rtx; 901169699Skan} 902169699Skan 903169699Skan/* __builtin_update_setjmp_buf is passed a pointer to an array of five words 904169699Skan (not all will be used on all machines) that was passed to __builtin_setjmp. 905169699Skan It updates the stack pointer in that block to correspond to the current 906169699Skan stack pointer. */ 907169699Skan 908169699Skanstatic void 909169699Skanexpand_builtin_update_setjmp_buf (rtx buf_addr) 910169699Skan{ 911169699Skan enum machine_mode sa_mode = Pmode; 912169699Skan rtx stack_save; 913169699Skan 914169699Skan 915169699Skan#ifdef HAVE_save_stack_nonlocal 916169699Skan if (HAVE_save_stack_nonlocal) 917169699Skan sa_mode = insn_data[(int) CODE_FOR_save_stack_nonlocal].operand[0].mode; 918169699Skan#endif 919169699Skan#ifdef STACK_SAVEAREA_MODE 920169699Skan sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); 921169699Skan#endif 922169699Skan 923169699Skan stack_save 924169699Skan = gen_rtx_MEM (sa_mode, 925169699Skan memory_address 926169699Skan (sa_mode, 927169699Skan plus_constant (buf_addr, 2 * GET_MODE_SIZE (Pmode)))); 928169699Skan 929169699Skan#ifdef HAVE_setjmp 930169699Skan if (HAVE_setjmp) 931169699Skan emit_insn (gen_setjmp ()); 932169699Skan#endif 933169699Skan 934169699Skan emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX); 935169699Skan} 936169699Skan 93790075Sobrien/* Expand a call to __builtin_prefetch. For a target that does not support 93890075Sobrien data prefetch, evaluate the memory address argument in case it has side 93990075Sobrien effects. */ 94090075Sobrien 94190075Sobrienstatic void 942132727Skanexpand_builtin_prefetch (tree arglist) 94390075Sobrien{ 94490075Sobrien tree arg0, arg1, arg2; 94590075Sobrien rtx op0, op1, op2; 94690075Sobrien 94790075Sobrien if (!validate_arglist (arglist, POINTER_TYPE, 0)) 94890075Sobrien return; 94990075Sobrien 95090075Sobrien arg0 = TREE_VALUE (arglist); 95190075Sobrien /* Arguments 1 and 2 are optional; argument 1 (read/write) defaults to 95290075Sobrien zero (read) and argument 2 (locality) defaults to 3 (high degree of 95390075Sobrien locality). */ 95490075Sobrien if (TREE_CHAIN (arglist)) 95590075Sobrien { 95690075Sobrien arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 95790075Sobrien if (TREE_CHAIN (TREE_CHAIN (arglist))) 958117404Skan arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 95990075Sobrien else 960169699Skan arg2 = build_int_cst (NULL_TREE, 3); 96190075Sobrien } 96290075Sobrien else 96390075Sobrien { 96490075Sobrien arg1 = integer_zero_node; 965169699Skan arg2 = build_int_cst (NULL_TREE, 3); 96690075Sobrien } 96790075Sobrien 96890075Sobrien /* Argument 0 is an address. */ 96990075Sobrien op0 = expand_expr (arg0, NULL_RTX, Pmode, EXPAND_NORMAL); 97090075Sobrien 97190075Sobrien /* Argument 1 (read/write flag) must be a compile-time constant int. */ 97290075Sobrien if (TREE_CODE (arg1) != INTEGER_CST) 97390075Sobrien { 974169699Skan error ("second argument to %<__builtin_prefetch%> must be a constant"); 975117404Skan arg1 = integer_zero_node; 97690075Sobrien } 977169699Skan op1 = expand_normal (arg1); 97890075Sobrien /* Argument 1 must be either zero or one. */ 97990075Sobrien if (INTVAL (op1) != 0 && INTVAL (op1) != 1) 98090075Sobrien { 981169699Skan warning (0, "invalid second argument to %<__builtin_prefetch%>;" 982169699Skan " using zero"); 98390075Sobrien op1 = const0_rtx; 98490075Sobrien } 98590075Sobrien 98690075Sobrien /* Argument 2 (locality) must be a compile-time constant int. */ 98790075Sobrien if (TREE_CODE (arg2) != INTEGER_CST) 98890075Sobrien { 989169699Skan error ("third argument to %<__builtin_prefetch%> must be a constant"); 99090075Sobrien arg2 = integer_zero_node; 99190075Sobrien } 992169699Skan op2 = expand_normal (arg2); 99390075Sobrien /* Argument 2 must be 0, 1, 2, or 3. */ 99490075Sobrien if (INTVAL (op2) < 0 || INTVAL (op2) > 3) 99590075Sobrien { 996169699Skan warning (0, "invalid third argument to %<__builtin_prefetch%>; using zero"); 99790075Sobrien op2 = const0_rtx; 99890075Sobrien } 99990075Sobrien 100090075Sobrien#ifdef HAVE_prefetch 100190075Sobrien if (HAVE_prefetch) 100290075Sobrien { 1003117404Skan if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate) 1004117404Skan (op0, 1005117404Skan insn_data[(int) CODE_FOR_prefetch].operand[0].mode)) 1006132727Skan || (GET_MODE (op0) != Pmode)) 1007117404Skan { 1008132727Skan op0 = convert_memory_address (Pmode, op0); 1009117404Skan op0 = force_reg (Pmode, op0); 1010117404Skan } 101190075Sobrien emit_insn (gen_prefetch (op0, op1, op2)); 101290075Sobrien } 101390075Sobrien#endif 1014169699Skan 1015117404Skan /* Don't do anything with direct references to volatile memory, but 1016117404Skan generate code to handle other side effects. */ 1017169699Skan if (!MEM_P (op0) && side_effects_p (op0)) 1018117404Skan emit_insn (op0); 101990075Sobrien} 102090075Sobrien 102190075Sobrien/* Get a MEM rtx for expression EXP which is the address of an operand 1022169699Skan to be used in a string instruction (cmpstrsi, movmemsi, ..). LEN is 1023169699Skan the maximum length of the block of memory that might be accessed or 1024169699Skan NULL if unknown. */ 102590075Sobrien 102690075Sobrienstatic rtx 1027169699Skanget_memory_rtx (tree exp, tree len) 102890075Sobrien{ 1029169699Skan rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL); 1030169699Skan rtx mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr)); 103190075Sobrien 103290075Sobrien /* Get an expression we can use to find the attributes to assign to MEM. 103390075Sobrien If it is an ADDR_EXPR, use the operand. Otherwise, dereference it if 103490075Sobrien we can. First remove any nops. */ 103590075Sobrien while ((TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR 1036117404Skan || TREE_CODE (exp) == NON_LVALUE_EXPR) 103790075Sobrien && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0)))) 103890075Sobrien exp = TREE_OPERAND (exp, 0); 103990075Sobrien 104090075Sobrien if (TREE_CODE (exp) == ADDR_EXPR) 1041169699Skan exp = TREE_OPERAND (exp, 0); 1042169699Skan else if (POINTER_TYPE_P (TREE_TYPE (exp))) 1043169699Skan exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp); 1044169699Skan else 1045169699Skan exp = NULL; 1046169699Skan 1047169699Skan /* Honor attributes derived from exp, except for the alias set 1048169699Skan (as builtin stringops may alias with anything) and the size 1049169699Skan (as stringops may access multiple array elements). */ 1050169699Skan if (exp) 105190075Sobrien { 105290075Sobrien set_mem_attributes (mem, exp, 0); 1053169699Skan 1054169699Skan /* Allow the string and memory builtins to overflow from one 1055169699Skan field into another, see http://gcc.gnu.org/PR23561. 1056169699Skan Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole 1057169699Skan memory accessed by the string or memory builtin will fit 1058169699Skan within the field. */ 1059169699Skan if (MEM_EXPR (mem) && TREE_CODE (MEM_EXPR (mem)) == COMPONENT_REF) 1060169699Skan { 1061169699Skan tree mem_expr = MEM_EXPR (mem); 1062169699Skan HOST_WIDE_INT offset = -1, length = -1; 1063169699Skan tree inner = exp; 1064169699Skan 1065169699Skan while (TREE_CODE (inner) == ARRAY_REF 1066169699Skan || TREE_CODE (inner) == NOP_EXPR 1067169699Skan || TREE_CODE (inner) == CONVERT_EXPR 1068169699Skan || TREE_CODE (inner) == NON_LVALUE_EXPR 1069169699Skan || TREE_CODE (inner) == VIEW_CONVERT_EXPR 1070169699Skan || TREE_CODE (inner) == SAVE_EXPR) 1071169699Skan inner = TREE_OPERAND (inner, 0); 1072169699Skan 1073169699Skan gcc_assert (TREE_CODE (inner) == COMPONENT_REF); 1074169699Skan 1075169699Skan if (MEM_OFFSET (mem) 1076169699Skan && GET_CODE (MEM_OFFSET (mem)) == CONST_INT) 1077169699Skan offset = INTVAL (MEM_OFFSET (mem)); 1078169699Skan 1079169699Skan if (offset >= 0 && len && host_integerp (len, 0)) 1080169699Skan length = tree_low_cst (len, 0); 1081169699Skan 1082169699Skan while (TREE_CODE (inner) == COMPONENT_REF) 1083169699Skan { 1084169699Skan tree field = TREE_OPERAND (inner, 1); 1085169699Skan gcc_assert (! DECL_BIT_FIELD (field)); 1086169699Skan gcc_assert (TREE_CODE (mem_expr) == COMPONENT_REF); 1087169699Skan gcc_assert (field == TREE_OPERAND (mem_expr, 1)); 1088169699Skan 1089169699Skan if (length >= 0 1090169699Skan && TYPE_SIZE_UNIT (TREE_TYPE (inner)) 1091169699Skan && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0)) 1092169699Skan { 1093169699Skan HOST_WIDE_INT size 1094169699Skan = tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0); 1095169699Skan /* If we can prove the memory starting at XEXP (mem, 0) 1096169699Skan and ending at XEXP (mem, 0) + LENGTH will fit into 1097169699Skan this field, we can keep that COMPONENT_REF in MEM_EXPR. */ 1098169699Skan if (offset <= size 1099169699Skan && length <= size 1100169699Skan && offset + length <= size) 1101169699Skan break; 1102169699Skan } 1103169699Skan 1104169699Skan if (offset >= 0 1105169699Skan && host_integerp (DECL_FIELD_OFFSET (field), 0)) 1106169699Skan offset += tree_low_cst (DECL_FIELD_OFFSET (field), 0) 1107169699Skan + tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1) 1108169699Skan / BITS_PER_UNIT; 1109169699Skan else 1110169699Skan { 1111169699Skan offset = -1; 1112169699Skan length = -1; 1113169699Skan } 1114169699Skan 1115169699Skan mem_expr = TREE_OPERAND (mem_expr, 0); 1116169699Skan inner = TREE_OPERAND (inner, 0); 1117169699Skan } 1118169699Skan 1119169699Skan if (mem_expr == NULL) 1120169699Skan offset = -1; 1121169699Skan if (mem_expr != MEM_EXPR (mem)) 1122169699Skan { 1123169699Skan set_mem_expr (mem, mem_expr); 1124169699Skan set_mem_offset (mem, offset >= 0 ? GEN_INT (offset) : NULL_RTX); 1125169699Skan } 1126169699Skan } 112790075Sobrien set_mem_alias_set (mem, 0); 1128169699Skan set_mem_size (mem, NULL_RTX); 112990075Sobrien } 113090075Sobrien 113190075Sobrien return mem; 113290075Sobrien} 113390075Sobrien 113490075Sobrien/* Built-in functions to perform an untyped call and return. */ 113590075Sobrien 113690075Sobrien/* For each register that may be used for calling a function, this 113790075Sobrien gives a mode used to copy the register's value. VOIDmode indicates 113890075Sobrien the register is not used for calling a function. If the machine 113990075Sobrien has register windows, this gives only the outbound registers. 114090075Sobrien INCOMING_REGNO gives the corresponding inbound register. */ 114190075Sobrienstatic enum machine_mode apply_args_mode[FIRST_PSEUDO_REGISTER]; 114290075Sobrien 114390075Sobrien/* For each register that may be used for returning values, this gives 114490075Sobrien a mode used to copy the register's value. VOIDmode indicates the 114590075Sobrien register is not used for returning values. If the machine has 114690075Sobrien register windows, this gives only the outbound registers. 114790075Sobrien INCOMING_REGNO gives the corresponding inbound register. */ 114890075Sobrienstatic enum machine_mode apply_result_mode[FIRST_PSEUDO_REGISTER]; 114990075Sobrien 115090075Sobrien/* For each register that may be used for calling a function, this 115190075Sobrien gives the offset of that register into the block returned by 115290075Sobrien __builtin_apply_args. 0 indicates that the register is not 115390075Sobrien used for calling a function. */ 115490075Sobrienstatic int apply_args_reg_offset[FIRST_PSEUDO_REGISTER]; 115590075Sobrien 115690075Sobrien/* Return the size required for the block returned by __builtin_apply_args, 115790075Sobrien and initialize apply_args_mode. */ 115890075Sobrien 115990075Sobrienstatic int 1160132727Skanapply_args_size (void) 116190075Sobrien{ 116290075Sobrien static int size = -1; 116390075Sobrien int align; 116490075Sobrien unsigned int regno; 116590075Sobrien enum machine_mode mode; 116690075Sobrien 116790075Sobrien /* The values computed by this function never change. */ 116890075Sobrien if (size < 0) 116990075Sobrien { 117090075Sobrien /* The first value is the incoming arg-pointer. */ 117190075Sobrien size = GET_MODE_SIZE (Pmode); 117290075Sobrien 117390075Sobrien /* The second value is the structure value address unless this is 117490075Sobrien passed as an "invisible" first argument. */ 1175132727Skan if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0)) 117690075Sobrien size += GET_MODE_SIZE (Pmode); 117790075Sobrien 117890075Sobrien for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) 117990075Sobrien if (FUNCTION_ARG_REGNO_P (regno)) 118090075Sobrien { 1181169699Skan mode = reg_raw_mode[regno]; 118290075Sobrien 1183169699Skan gcc_assert (mode != VOIDmode); 118490075Sobrien 118590075Sobrien align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; 118690075Sobrien if (size % align != 0) 118790075Sobrien size = CEIL (size, align) * align; 118890075Sobrien apply_args_reg_offset[regno] = size; 118990075Sobrien size += GET_MODE_SIZE (mode); 119090075Sobrien apply_args_mode[regno] = mode; 119190075Sobrien } 119290075Sobrien else 119390075Sobrien { 119490075Sobrien apply_args_mode[regno] = VOIDmode; 119590075Sobrien apply_args_reg_offset[regno] = 0; 119690075Sobrien } 119790075Sobrien } 119890075Sobrien return size; 119990075Sobrien} 120090075Sobrien 120190075Sobrien/* Return the size required for the block returned by __builtin_apply, 120290075Sobrien and initialize apply_result_mode. */ 120390075Sobrien 120490075Sobrienstatic int 1205132727Skanapply_result_size (void) 120690075Sobrien{ 120790075Sobrien static int size = -1; 120890075Sobrien int align, regno; 120990075Sobrien enum machine_mode mode; 121090075Sobrien 121190075Sobrien /* The values computed by this function never change. */ 121290075Sobrien if (size < 0) 121390075Sobrien { 121490075Sobrien size = 0; 121590075Sobrien 121690075Sobrien for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) 121790075Sobrien if (FUNCTION_VALUE_REGNO_P (regno)) 121890075Sobrien { 1219169699Skan mode = reg_raw_mode[regno]; 122090075Sobrien 1221169699Skan gcc_assert (mode != VOIDmode); 122290075Sobrien 122390075Sobrien align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; 122490075Sobrien if (size % align != 0) 122590075Sobrien size = CEIL (size, align) * align; 122690075Sobrien size += GET_MODE_SIZE (mode); 122790075Sobrien apply_result_mode[regno] = mode; 122890075Sobrien } 122990075Sobrien else 123090075Sobrien apply_result_mode[regno] = VOIDmode; 123190075Sobrien 123290075Sobrien /* Allow targets that use untyped_call and untyped_return to override 123390075Sobrien the size so that machine-specific information can be stored here. */ 123490075Sobrien#ifdef APPLY_RESULT_SIZE 123590075Sobrien size = APPLY_RESULT_SIZE; 123690075Sobrien#endif 123790075Sobrien } 123890075Sobrien return size; 123990075Sobrien} 124090075Sobrien 124190075Sobrien#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) 124290075Sobrien/* Create a vector describing the result block RESULT. If SAVEP is true, 124390075Sobrien the result block is used to save the values; otherwise it is used to 124490075Sobrien restore the values. */ 124590075Sobrien 124690075Sobrienstatic rtx 1247132727Skanresult_vector (int savep, rtx result) 124890075Sobrien{ 124990075Sobrien int regno, size, align, nelts; 125090075Sobrien enum machine_mode mode; 125190075Sobrien rtx reg, mem; 1252132727Skan rtx *savevec = alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx)); 125390075Sobrien 125490075Sobrien size = nelts = 0; 125590075Sobrien for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) 125690075Sobrien if ((mode = apply_result_mode[regno]) != VOIDmode) 125790075Sobrien { 125890075Sobrien align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; 125990075Sobrien if (size % align != 0) 126090075Sobrien size = CEIL (size, align) * align; 126190075Sobrien reg = gen_rtx_REG (mode, savep ? regno : INCOMING_REGNO (regno)); 126290075Sobrien mem = adjust_address (result, mode, size); 126390075Sobrien savevec[nelts++] = (savep 126490075Sobrien ? gen_rtx_SET (VOIDmode, mem, reg) 126590075Sobrien : gen_rtx_SET (VOIDmode, reg, mem)); 126690075Sobrien size += GET_MODE_SIZE (mode); 126790075Sobrien } 126890075Sobrien return gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (nelts, savevec)); 126990075Sobrien} 127090075Sobrien#endif /* HAVE_untyped_call or HAVE_untyped_return */ 127190075Sobrien 127290075Sobrien/* Save the state required to perform an untyped call with the same 127390075Sobrien arguments as were passed to the current function. */ 127490075Sobrien 127590075Sobrienstatic rtx 1276132727Skanexpand_builtin_apply_args_1 (void) 127790075Sobrien{ 1278132727Skan rtx registers, tem; 127990075Sobrien int size, align, regno; 128090075Sobrien enum machine_mode mode; 1281132727Skan rtx struct_incoming_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 1); 128290075Sobrien 128390075Sobrien /* Create a block where the arg-pointer, structure value address, 128490075Sobrien and argument registers can be saved. */ 128590075Sobrien registers = assign_stack_local (BLKmode, apply_args_size (), -1); 128690075Sobrien 128790075Sobrien /* Walk past the arg-pointer and structure value address. */ 128890075Sobrien size = GET_MODE_SIZE (Pmode); 1289132727Skan if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0)) 129090075Sobrien size += GET_MODE_SIZE (Pmode); 129190075Sobrien 129290075Sobrien /* Save each register used in calling a function to the block. */ 129390075Sobrien for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) 129490075Sobrien if ((mode = apply_args_mode[regno]) != VOIDmode) 129590075Sobrien { 129690075Sobrien align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; 129790075Sobrien if (size % align != 0) 129890075Sobrien size = CEIL (size, align) * align; 129990075Sobrien 130090075Sobrien tem = gen_rtx_REG (mode, INCOMING_REGNO (regno)); 130190075Sobrien 130290075Sobrien emit_move_insn (adjust_address (registers, mode, size), tem); 130390075Sobrien size += GET_MODE_SIZE (mode); 130490075Sobrien } 130590075Sobrien 130690075Sobrien /* Save the arg pointer to the block. */ 1307132727Skan tem = copy_to_reg (virtual_incoming_args_rtx); 1308132727Skan#ifdef STACK_GROWS_DOWNWARD 1309132727Skan /* We need the pointer as the caller actually passed them to us, not 1310132727Skan as we might have pretended they were passed. Make sure it's a valid 1311132727Skan operand, as emit_move_insn isn't expected to handle a PLUS. */ 1312132727Skan tem 1313132727Skan = force_operand (plus_constant (tem, current_function_pretend_args_size), 1314132727Skan NULL_RTX); 1315132727Skan#endif 1316132727Skan emit_move_insn (adjust_address (registers, Pmode, 0), tem); 1317169699Skan 131890075Sobrien size = GET_MODE_SIZE (Pmode); 131990075Sobrien 132090075Sobrien /* Save the structure value address unless this is passed as an 132190075Sobrien "invisible" first argument. */ 1322132727Skan if (struct_incoming_value) 132390075Sobrien { 132490075Sobrien emit_move_insn (adjust_address (registers, Pmode, size), 1325132727Skan copy_to_reg (struct_incoming_value)); 132690075Sobrien size += GET_MODE_SIZE (Pmode); 132790075Sobrien } 132890075Sobrien 132990075Sobrien /* Return the address of the block. */ 133090075Sobrien return copy_addr_to_reg (XEXP (registers, 0)); 133190075Sobrien} 133290075Sobrien 133390075Sobrien/* __builtin_apply_args returns block of memory allocated on 133490075Sobrien the stack into which is stored the arg pointer, structure 133590075Sobrien value address, static chain, and all the registers that might 133690075Sobrien possibly be used in performing a function call. The code is 133790075Sobrien moved to the start of the function so the incoming values are 133890075Sobrien saved. */ 133990075Sobrien 134090075Sobrienstatic rtx 1341132727Skanexpand_builtin_apply_args (void) 134290075Sobrien{ 134390075Sobrien /* Don't do __builtin_apply_args more than once in a function. 134490075Sobrien Save the result of the first call and reuse it. */ 134590075Sobrien if (apply_args_value != 0) 134690075Sobrien return apply_args_value; 134790075Sobrien { 134890075Sobrien /* When this function is called, it means that registers must be 134990075Sobrien saved on entry to this function. So we migrate the 135090075Sobrien call to the first insn of this function. */ 135190075Sobrien rtx temp; 135290075Sobrien rtx seq; 135390075Sobrien 135490075Sobrien start_sequence (); 135590075Sobrien temp = expand_builtin_apply_args_1 (); 135690075Sobrien seq = get_insns (); 135790075Sobrien end_sequence (); 135890075Sobrien 135990075Sobrien apply_args_value = temp; 136090075Sobrien 1361117404Skan /* Put the insns after the NOTE that starts the function. 1362117404Skan If this is inside a start_sequence, make the outer-level insn 136390075Sobrien chain current, so the code is placed at the start of the 136490075Sobrien function. */ 136590075Sobrien push_topmost_sequence (); 1366169699Skan emit_insn_before (seq, NEXT_INSN (entry_of_function ())); 136790075Sobrien pop_topmost_sequence (); 136890075Sobrien return temp; 136990075Sobrien } 137090075Sobrien} 137190075Sobrien 137290075Sobrien/* Perform an untyped call and save the state required to perform an 137390075Sobrien untyped return of whatever value was returned by the given function. */ 137490075Sobrien 137590075Sobrienstatic rtx 1376132727Skanexpand_builtin_apply (rtx function, rtx arguments, rtx argsize) 137790075Sobrien{ 137890075Sobrien int size, align, regno; 137990075Sobrien enum machine_mode mode; 138090075Sobrien rtx incoming_args, result, reg, dest, src, call_insn; 138190075Sobrien rtx old_stack_level = 0; 138290075Sobrien rtx call_fusage = 0; 1383132727Skan rtx struct_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0); 138490075Sobrien 1385132727Skan arguments = convert_memory_address (Pmode, arguments); 138690075Sobrien 138790075Sobrien /* Create a block where the return registers can be saved. */ 138890075Sobrien result = assign_stack_local (BLKmode, apply_result_size (), -1); 138990075Sobrien 139090075Sobrien /* Fetch the arg pointer from the ARGUMENTS block. */ 139190075Sobrien incoming_args = gen_reg_rtx (Pmode); 139290075Sobrien emit_move_insn (incoming_args, gen_rtx_MEM (Pmode, arguments)); 139390075Sobrien#ifndef STACK_GROWS_DOWNWARD 139490075Sobrien incoming_args = expand_simple_binop (Pmode, MINUS, incoming_args, argsize, 139590075Sobrien incoming_args, 0, OPTAB_LIB_WIDEN); 139690075Sobrien#endif 139790075Sobrien 139890075Sobrien /* Push a new argument block and copy the arguments. Do not allow 139990075Sobrien the (potential) memcpy call below to interfere with our stack 140090075Sobrien manipulations. */ 140190075Sobrien do_pending_stack_adjust (); 140290075Sobrien NO_DEFER_POP; 140390075Sobrien 1404132727Skan /* Save the stack with nonlocal if available. */ 140590075Sobrien#ifdef HAVE_save_stack_nonlocal 140690075Sobrien if (HAVE_save_stack_nonlocal) 140790075Sobrien emit_stack_save (SAVE_NONLOCAL, &old_stack_level, NULL_RTX); 140890075Sobrien else 140990075Sobrien#endif 141090075Sobrien emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); 141190075Sobrien 1412132727Skan /* Allocate a block of memory onto the stack and copy the memory 1413132727Skan arguments to the outgoing arguments address. */ 1414132727Skan allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT); 1415132727Skan dest = virtual_outgoing_args_rtx; 1416132727Skan#ifndef STACK_GROWS_DOWNWARD 1417132727Skan if (GET_CODE (argsize) == CONST_INT) 1418132727Skan dest = plus_constant (dest, -INTVAL (argsize)); 1419132727Skan else 1420132727Skan dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize)); 1421132727Skan#endif 142290075Sobrien dest = gen_rtx_MEM (BLKmode, dest); 142390075Sobrien set_mem_align (dest, PARM_BOUNDARY); 142490075Sobrien src = gen_rtx_MEM (BLKmode, incoming_args); 142590075Sobrien set_mem_align (src, PARM_BOUNDARY); 1426117404Skan emit_block_move (dest, src, argsize, BLOCK_OP_NORMAL); 142790075Sobrien 142890075Sobrien /* Refer to the argument block. */ 142990075Sobrien apply_args_size (); 143090075Sobrien arguments = gen_rtx_MEM (BLKmode, arguments); 143190075Sobrien set_mem_align (arguments, PARM_BOUNDARY); 143290075Sobrien 143390075Sobrien /* Walk past the arg-pointer and structure value address. */ 143490075Sobrien size = GET_MODE_SIZE (Pmode); 1435132727Skan if (struct_value) 143690075Sobrien size += GET_MODE_SIZE (Pmode); 143790075Sobrien 143890075Sobrien /* Restore each of the registers previously saved. Make USE insns 143990075Sobrien for each of these registers for use in making the call. */ 144090075Sobrien for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) 144190075Sobrien if ((mode = apply_args_mode[regno]) != VOIDmode) 144290075Sobrien { 144390075Sobrien align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; 144490075Sobrien if (size % align != 0) 144590075Sobrien size = CEIL (size, align) * align; 144690075Sobrien reg = gen_rtx_REG (mode, regno); 144790075Sobrien emit_move_insn (reg, adjust_address (arguments, mode, size)); 144890075Sobrien use_reg (&call_fusage, reg); 144990075Sobrien size += GET_MODE_SIZE (mode); 145090075Sobrien } 145190075Sobrien 145290075Sobrien /* Restore the structure value address unless this is passed as an 145390075Sobrien "invisible" first argument. */ 145490075Sobrien size = GET_MODE_SIZE (Pmode); 1455132727Skan if (struct_value) 145690075Sobrien { 145790075Sobrien rtx value = gen_reg_rtx (Pmode); 145890075Sobrien emit_move_insn (value, adjust_address (arguments, Pmode, size)); 1459132727Skan emit_move_insn (struct_value, value); 1460169699Skan if (REG_P (struct_value)) 1461132727Skan use_reg (&call_fusage, struct_value); 146290075Sobrien size += GET_MODE_SIZE (Pmode); 146390075Sobrien } 146490075Sobrien 146590075Sobrien /* All arguments and registers used for the call are set up by now! */ 1466169699Skan function = prepare_call_address (function, NULL, &call_fusage, 0, 0); 146790075Sobrien 146890075Sobrien /* Ensure address is valid. SYMBOL_REF is already valid, so no need, 146990075Sobrien and we don't want to load it into a register as an optimization, 147090075Sobrien because prepare_call_address already did it if it should be done. */ 147190075Sobrien if (GET_CODE (function) != SYMBOL_REF) 147290075Sobrien function = memory_address (FUNCTION_MODE, function); 147390075Sobrien 147490075Sobrien /* Generate the actual call instruction and save the return value. */ 147590075Sobrien#ifdef HAVE_untyped_call 147690075Sobrien if (HAVE_untyped_call) 147790075Sobrien emit_call_insn (gen_untyped_call (gen_rtx_MEM (FUNCTION_MODE, function), 147890075Sobrien result, result_vector (1, result))); 147990075Sobrien else 148090075Sobrien#endif 148190075Sobrien#ifdef HAVE_call_value 148290075Sobrien if (HAVE_call_value) 148390075Sobrien { 148490075Sobrien rtx valreg = 0; 148590075Sobrien 148690075Sobrien /* Locate the unique return register. It is not possible to 148790075Sobrien express a call that sets more than one return register using 148890075Sobrien call_value; use untyped_call for that. In fact, untyped_call 148990075Sobrien only needs to save the return registers in the given block. */ 149090075Sobrien for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) 149190075Sobrien if ((mode = apply_result_mode[regno]) != VOIDmode) 149290075Sobrien { 1493169699Skan gcc_assert (!valreg); /* HAVE_untyped_call required. */ 1494169699Skan 149590075Sobrien valreg = gen_rtx_REG (mode, regno); 149690075Sobrien } 149790075Sobrien 149890075Sobrien emit_call_insn (GEN_CALL_VALUE (valreg, 149990075Sobrien gen_rtx_MEM (FUNCTION_MODE, function), 150090075Sobrien const0_rtx, NULL_RTX, const0_rtx)); 150190075Sobrien 150290075Sobrien emit_move_insn (adjust_address (result, GET_MODE (valreg), 0), valreg); 150390075Sobrien } 150490075Sobrien else 150590075Sobrien#endif 1506169699Skan gcc_unreachable (); 150790075Sobrien 1508132727Skan /* Find the CALL insn we just emitted, and attach the register usage 1509132727Skan information. */ 1510132727Skan call_insn = last_call_insn (); 1511132727Skan add_function_usage_to (call_insn, call_fusage); 151290075Sobrien 151390075Sobrien /* Restore the stack. */ 151490075Sobrien#ifdef HAVE_save_stack_nonlocal 151590075Sobrien if (HAVE_save_stack_nonlocal) 151690075Sobrien emit_stack_restore (SAVE_NONLOCAL, old_stack_level, NULL_RTX); 151790075Sobrien else 151890075Sobrien#endif 151990075Sobrien emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); 152090075Sobrien 152190075Sobrien OK_DEFER_POP; 152290075Sobrien 152390075Sobrien /* Return the address of the result block. */ 1524122190Skan result = copy_addr_to_reg (XEXP (result, 0)); 1525132727Skan return convert_memory_address (ptr_mode, result); 152690075Sobrien} 152790075Sobrien 152890075Sobrien/* Perform an untyped return. */ 152990075Sobrien 153090075Sobrienstatic void 1531132727Skanexpand_builtin_return (rtx result) 153290075Sobrien{ 153390075Sobrien int size, align, regno; 153490075Sobrien enum machine_mode mode; 153590075Sobrien rtx reg; 153690075Sobrien rtx call_fusage = 0; 153790075Sobrien 1538132727Skan result = convert_memory_address (Pmode, result); 153990075Sobrien 154090075Sobrien apply_result_size (); 154190075Sobrien result = gen_rtx_MEM (BLKmode, result); 154290075Sobrien 154390075Sobrien#ifdef HAVE_untyped_return 154490075Sobrien if (HAVE_untyped_return) 154590075Sobrien { 154690075Sobrien emit_jump_insn (gen_untyped_return (result, result_vector (0, result))); 154790075Sobrien emit_barrier (); 154890075Sobrien return; 154990075Sobrien } 155090075Sobrien#endif 155190075Sobrien 155290075Sobrien /* Restore the return value and note that each value is used. */ 155390075Sobrien size = 0; 155490075Sobrien for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) 155590075Sobrien if ((mode = apply_result_mode[regno]) != VOIDmode) 155690075Sobrien { 155790075Sobrien align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; 155890075Sobrien if (size % align != 0) 155990075Sobrien size = CEIL (size, align) * align; 156090075Sobrien reg = gen_rtx_REG (mode, INCOMING_REGNO (regno)); 156190075Sobrien emit_move_insn (reg, adjust_address (result, mode, size)); 156290075Sobrien 156390075Sobrien push_to_sequence (call_fusage); 156490075Sobrien emit_insn (gen_rtx_USE (VOIDmode, reg)); 156590075Sobrien call_fusage = get_insns (); 156690075Sobrien end_sequence (); 156790075Sobrien size += GET_MODE_SIZE (mode); 156890075Sobrien } 156990075Sobrien 157090075Sobrien /* Put the USE insns before the return. */ 1571117404Skan emit_insn (call_fusage); 157290075Sobrien 157390075Sobrien /* Return whatever values was restored by jumping directly to the end 157490075Sobrien of the function. */ 1575132727Skan expand_naked_return (); 157690075Sobrien} 157790075Sobrien 157890075Sobrien/* Used by expand_builtin_classify_type and fold_builtin_classify_type. */ 157990075Sobrien 158090075Sobrienstatic enum type_class 1581132727Skantype_to_class (tree type) 158290075Sobrien{ 158390075Sobrien switch (TREE_CODE (type)) 158490075Sobrien { 158590075Sobrien case VOID_TYPE: return void_type_class; 158690075Sobrien case INTEGER_TYPE: return integer_type_class; 158790075Sobrien case ENUMERAL_TYPE: return enumeral_type_class; 158890075Sobrien case BOOLEAN_TYPE: return boolean_type_class; 158990075Sobrien case POINTER_TYPE: return pointer_type_class; 159090075Sobrien case REFERENCE_TYPE: return reference_type_class; 159190075Sobrien case OFFSET_TYPE: return offset_type_class; 159290075Sobrien case REAL_TYPE: return real_type_class; 159390075Sobrien case COMPLEX_TYPE: return complex_type_class; 159490075Sobrien case FUNCTION_TYPE: return function_type_class; 159590075Sobrien case METHOD_TYPE: return method_type_class; 159690075Sobrien case RECORD_TYPE: return record_type_class; 159790075Sobrien case UNION_TYPE: 159890075Sobrien case QUAL_UNION_TYPE: return union_type_class; 159990075Sobrien case ARRAY_TYPE: return (TYPE_STRING_FLAG (type) 160090075Sobrien ? string_type_class : array_type_class); 160190075Sobrien case LANG_TYPE: return lang_type_class; 160290075Sobrien default: return no_type_class; 160390075Sobrien } 160490075Sobrien} 160590075Sobrien 160690075Sobrien/* Expand a call to __builtin_classify_type with arguments found in 160790075Sobrien ARGLIST. */ 160890075Sobrien 160990075Sobrienstatic rtx 1610132727Skanexpand_builtin_classify_type (tree arglist) 161190075Sobrien{ 161290075Sobrien if (arglist != 0) 161390075Sobrien return GEN_INT (type_to_class (TREE_TYPE (TREE_VALUE (arglist)))); 161490075Sobrien return GEN_INT (no_type_class); 161590075Sobrien} 161690075Sobrien 1617132727Skan/* This helper macro, meant to be used in mathfn_built_in below, 1618132727Skan determines which among a set of three builtin math functions is 1619132727Skan appropriate for a given type mode. The `F' and `L' cases are 1620132727Skan automatically generated from the `double' case. */ 1621132727Skan#define CASE_MATHFN(BUILT_IN_MATHFN) \ 1622132727Skan case BUILT_IN_MATHFN: case BUILT_IN_MATHFN##F: case BUILT_IN_MATHFN##L: \ 1623132727Skan fcode = BUILT_IN_MATHFN; fcodef = BUILT_IN_MATHFN##F ; \ 1624132727Skan fcodel = BUILT_IN_MATHFN##L ; break; 1625132727Skan 1626132727Skan/* Return mathematic function equivalent to FN but operating directly 1627132727Skan on TYPE, if available. If we can't do the conversion, return zero. */ 1628132727Skantree 1629132727Skanmathfn_built_in (tree type, enum built_in_function fn) 1630132727Skan{ 1631132727Skan enum built_in_function fcode, fcodef, fcodel; 1632132727Skan 1633132727Skan switch (fn) 1634132727Skan { 1635132727Skan CASE_MATHFN (BUILT_IN_ACOS) 1636132727Skan CASE_MATHFN (BUILT_IN_ACOSH) 1637132727Skan CASE_MATHFN (BUILT_IN_ASIN) 1638132727Skan CASE_MATHFN (BUILT_IN_ASINH) 1639132727Skan CASE_MATHFN (BUILT_IN_ATAN) 1640132727Skan CASE_MATHFN (BUILT_IN_ATAN2) 1641132727Skan CASE_MATHFN (BUILT_IN_ATANH) 1642132727Skan CASE_MATHFN (BUILT_IN_CBRT) 1643132727Skan CASE_MATHFN (BUILT_IN_CEIL) 1644132727Skan CASE_MATHFN (BUILT_IN_COPYSIGN) 1645132727Skan CASE_MATHFN (BUILT_IN_COS) 1646132727Skan CASE_MATHFN (BUILT_IN_COSH) 1647132727Skan CASE_MATHFN (BUILT_IN_DREM) 1648132727Skan CASE_MATHFN (BUILT_IN_ERF) 1649132727Skan CASE_MATHFN (BUILT_IN_ERFC) 1650132727Skan CASE_MATHFN (BUILT_IN_EXP) 1651132727Skan CASE_MATHFN (BUILT_IN_EXP10) 1652132727Skan CASE_MATHFN (BUILT_IN_EXP2) 1653132727Skan CASE_MATHFN (BUILT_IN_EXPM1) 1654132727Skan CASE_MATHFN (BUILT_IN_FABS) 1655132727Skan CASE_MATHFN (BUILT_IN_FDIM) 1656132727Skan CASE_MATHFN (BUILT_IN_FLOOR) 1657132727Skan CASE_MATHFN (BUILT_IN_FMA) 1658132727Skan CASE_MATHFN (BUILT_IN_FMAX) 1659132727Skan CASE_MATHFN (BUILT_IN_FMIN) 1660132727Skan CASE_MATHFN (BUILT_IN_FMOD) 1661132727Skan CASE_MATHFN (BUILT_IN_FREXP) 1662132727Skan CASE_MATHFN (BUILT_IN_GAMMA) 1663132727Skan CASE_MATHFN (BUILT_IN_HUGE_VAL) 1664132727Skan CASE_MATHFN (BUILT_IN_HYPOT) 1665132727Skan CASE_MATHFN (BUILT_IN_ILOGB) 1666132727Skan CASE_MATHFN (BUILT_IN_INF) 1667132727Skan CASE_MATHFN (BUILT_IN_J0) 1668132727Skan CASE_MATHFN (BUILT_IN_J1) 1669132727Skan CASE_MATHFN (BUILT_IN_JN) 1670169699Skan CASE_MATHFN (BUILT_IN_LCEIL) 1671132727Skan CASE_MATHFN (BUILT_IN_LDEXP) 1672169699Skan CASE_MATHFN (BUILT_IN_LFLOOR) 1673132727Skan CASE_MATHFN (BUILT_IN_LGAMMA) 1674169699Skan CASE_MATHFN (BUILT_IN_LLCEIL) 1675169699Skan CASE_MATHFN (BUILT_IN_LLFLOOR) 1676132727Skan CASE_MATHFN (BUILT_IN_LLRINT) 1677132727Skan CASE_MATHFN (BUILT_IN_LLROUND) 1678132727Skan CASE_MATHFN (BUILT_IN_LOG) 1679132727Skan CASE_MATHFN (BUILT_IN_LOG10) 1680132727Skan CASE_MATHFN (BUILT_IN_LOG1P) 1681132727Skan CASE_MATHFN (BUILT_IN_LOG2) 1682132727Skan CASE_MATHFN (BUILT_IN_LOGB) 1683132727Skan CASE_MATHFN (BUILT_IN_LRINT) 1684132727Skan CASE_MATHFN (BUILT_IN_LROUND) 1685132727Skan CASE_MATHFN (BUILT_IN_MODF) 1686132727Skan CASE_MATHFN (BUILT_IN_NAN) 1687132727Skan CASE_MATHFN (BUILT_IN_NANS) 1688132727Skan CASE_MATHFN (BUILT_IN_NEARBYINT) 1689132727Skan CASE_MATHFN (BUILT_IN_NEXTAFTER) 1690132727Skan CASE_MATHFN (BUILT_IN_NEXTTOWARD) 1691132727Skan CASE_MATHFN (BUILT_IN_POW) 1692169699Skan CASE_MATHFN (BUILT_IN_POWI) 1693132727Skan CASE_MATHFN (BUILT_IN_POW10) 1694132727Skan CASE_MATHFN (BUILT_IN_REMAINDER) 1695132727Skan CASE_MATHFN (BUILT_IN_REMQUO) 1696132727Skan CASE_MATHFN (BUILT_IN_RINT) 1697132727Skan CASE_MATHFN (BUILT_IN_ROUND) 1698132727Skan CASE_MATHFN (BUILT_IN_SCALB) 1699132727Skan CASE_MATHFN (BUILT_IN_SCALBLN) 1700132727Skan CASE_MATHFN (BUILT_IN_SCALBN) 1701132727Skan CASE_MATHFN (BUILT_IN_SIGNIFICAND) 1702132727Skan CASE_MATHFN (BUILT_IN_SIN) 1703132727Skan CASE_MATHFN (BUILT_IN_SINCOS) 1704132727Skan CASE_MATHFN (BUILT_IN_SINH) 1705132727Skan CASE_MATHFN (BUILT_IN_SQRT) 1706132727Skan CASE_MATHFN (BUILT_IN_TAN) 1707132727Skan CASE_MATHFN (BUILT_IN_TANH) 1708132727Skan CASE_MATHFN (BUILT_IN_TGAMMA) 1709132727Skan CASE_MATHFN (BUILT_IN_TRUNC) 1710132727Skan CASE_MATHFN (BUILT_IN_Y0) 1711132727Skan CASE_MATHFN (BUILT_IN_Y1) 1712132727Skan CASE_MATHFN (BUILT_IN_YN) 1713132727Skan 1714132727Skan default: 1715132727Skan return 0; 1716132727Skan } 1717132727Skan 1718169699Skan if (TYPE_MAIN_VARIANT (type) == double_type_node) 1719132727Skan return implicit_built_in_decls[fcode]; 1720169699Skan else if (TYPE_MAIN_VARIANT (type) == float_type_node) 1721132727Skan return implicit_built_in_decls[fcodef]; 1722169699Skan else if (TYPE_MAIN_VARIANT (type) == long_double_type_node) 1723132727Skan return implicit_built_in_decls[fcodel]; 1724132727Skan else 1725132727Skan return 0; 1726132727Skan} 1727132727Skan 1728132727Skan/* If errno must be maintained, expand the RTL to check if the result, 1729132727Skan TARGET, of a built-in function call, EXP, is NaN, and if so set 1730132727Skan errno to EDOM. */ 1731132727Skan 1732132727Skanstatic void 1733132727Skanexpand_errno_check (tree exp, rtx target) 1734132727Skan{ 1735132727Skan rtx lab = gen_label_rtx (); 1736132727Skan 1737132727Skan /* Test the result; if it is NaN, set errno=EDOM because 1738132727Skan the argument was not in the domain. */ 1739132727Skan emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target), 1740132727Skan 0, lab); 1741132727Skan 1742132727Skan#ifdef TARGET_EDOM 1743132727Skan /* If this built-in doesn't throw an exception, set errno directly. */ 1744132727Skan if (TREE_NOTHROW (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) 1745132727Skan { 1746132727Skan#ifdef GEN_ERRNO_RTX 1747132727Skan rtx errno_rtx = GEN_ERRNO_RTX; 1748132727Skan#else 1749132727Skan rtx errno_rtx 1750132727Skan = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno")); 1751132727Skan#endif 1752132727Skan emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM)); 1753132727Skan emit_label (lab); 1754132727Skan return; 1755132727Skan } 1756132727Skan#endif 1757132727Skan 1758132727Skan /* We can't set errno=EDOM directly; let the library call do it. 1759132727Skan Pop the arguments right away in case the call gets deleted. */ 1760132727Skan NO_DEFER_POP; 1761132727Skan expand_call (exp, target, 0); 1762132727Skan OK_DEFER_POP; 1763132727Skan emit_label (lab); 1764132727Skan} 1765132727Skan 1766132727Skan 1767169699Skan/* Expand a call to one of the builtin math functions (sqrt, exp, or log). 176890075Sobrien Return 0 if a normal call should be emitted rather than expanding the 176990075Sobrien function in-line. EXP is the expression that is a call to the builtin 177090075Sobrien function; if convenient, the result should be placed in TARGET. 177190075Sobrien SUBTARGET may be used as the target for computing one of EXP's operands. */ 177290075Sobrien 177390075Sobrienstatic rtx 1774132727Skanexpand_builtin_mathfn (tree exp, rtx target, rtx subtarget) 177590075Sobrien{ 177690075Sobrien optab builtin_optab; 1777132727Skan rtx op0, insns, before_call; 1778132727Skan tree fndecl = get_callee_fndecl (exp); 177990075Sobrien tree arglist = TREE_OPERAND (exp, 1); 1780132727Skan enum machine_mode mode; 1781132727Skan bool errno_set = false; 1782132727Skan tree arg, narg; 178390075Sobrien 178490075Sobrien if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 178590075Sobrien return 0; 178690075Sobrien 1787132727Skan arg = TREE_VALUE (arglist); 178890075Sobrien 178990075Sobrien switch (DECL_FUNCTION_CODE (fndecl)) 179090075Sobrien { 1791169699Skan CASE_FLT_FN (BUILT_IN_SQRT): 1792132727Skan errno_set = ! tree_expr_nonnegative_p (arg); 1793132727Skan builtin_optab = sqrt_optab; 1794132727Skan break; 1795169699Skan CASE_FLT_FN (BUILT_IN_EXP): 1796132727Skan errno_set = true; builtin_optab = exp_optab; break; 1797169699Skan CASE_FLT_FN (BUILT_IN_EXP10): 1798169699Skan CASE_FLT_FN (BUILT_IN_POW10): 1799169699Skan errno_set = true; builtin_optab = exp10_optab; break; 1800169699Skan CASE_FLT_FN (BUILT_IN_EXP2): 1801169699Skan errno_set = true; builtin_optab = exp2_optab; break; 1802169699Skan CASE_FLT_FN (BUILT_IN_EXPM1): 1803169699Skan errno_set = true; builtin_optab = expm1_optab; break; 1804169699Skan CASE_FLT_FN (BUILT_IN_LOGB): 1805169699Skan errno_set = true; builtin_optab = logb_optab; break; 1806169699Skan CASE_FLT_FN (BUILT_IN_ILOGB): 1807169699Skan errno_set = true; builtin_optab = ilogb_optab; break; 1808169699Skan CASE_FLT_FN (BUILT_IN_LOG): 1809132727Skan errno_set = true; builtin_optab = log_optab; break; 1810169699Skan CASE_FLT_FN (BUILT_IN_LOG10): 1811169699Skan errno_set = true; builtin_optab = log10_optab; break; 1812169699Skan CASE_FLT_FN (BUILT_IN_LOG2): 1813169699Skan errno_set = true; builtin_optab = log2_optab; break; 1814169699Skan CASE_FLT_FN (BUILT_IN_LOG1P): 1815169699Skan errno_set = true; builtin_optab = log1p_optab; break; 1816169699Skan CASE_FLT_FN (BUILT_IN_ASIN): 1817169699Skan builtin_optab = asin_optab; break; 1818169699Skan CASE_FLT_FN (BUILT_IN_ACOS): 1819169699Skan builtin_optab = acos_optab; break; 1820169699Skan CASE_FLT_FN (BUILT_IN_TAN): 1821132727Skan builtin_optab = tan_optab; break; 1822169699Skan CASE_FLT_FN (BUILT_IN_ATAN): 1823132727Skan builtin_optab = atan_optab; break; 1824169699Skan CASE_FLT_FN (BUILT_IN_FLOOR): 1825132727Skan builtin_optab = floor_optab; break; 1826169699Skan CASE_FLT_FN (BUILT_IN_CEIL): 1827132727Skan builtin_optab = ceil_optab; break; 1828169699Skan CASE_FLT_FN (BUILT_IN_TRUNC): 1829132727Skan builtin_optab = btrunc_optab; break; 1830169699Skan CASE_FLT_FN (BUILT_IN_ROUND): 1831132727Skan builtin_optab = round_optab; break; 1832169699Skan CASE_FLT_FN (BUILT_IN_NEARBYINT): 1833132727Skan builtin_optab = nearbyint_optab; break; 1834169699Skan CASE_FLT_FN (BUILT_IN_RINT): 1835169699Skan builtin_optab = rint_optab; break; 1836169699Skan CASE_FLT_FN (BUILT_IN_LRINT): 1837169699Skan CASE_FLT_FN (BUILT_IN_LLRINT): 1838169699Skan builtin_optab = lrint_optab; break; 1839117404Skan default: 1840169699Skan gcc_unreachable (); 184190075Sobrien } 184290075Sobrien 1843132727Skan /* Make a suitable register to place result in. */ 1844132727Skan mode = TYPE_MODE (TREE_TYPE (exp)); 184590075Sobrien 1846132727Skan if (! flag_errno_math || ! HONOR_NANS (mode)) 1847132727Skan errno_set = false; 1848132727Skan 1849132727Skan /* Before working hard, check whether the instruction is available. */ 1850132727Skan if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 185190075Sobrien { 1852132727Skan target = gen_reg_rtx (mode); 1853132727Skan 1854132727Skan /* Wrap the computation of the argument in a SAVE_EXPR, as we may 1855132727Skan need to expand the argument again. This way, we will not perform 1856132727Skan side-effects more the once. */ 1857169699Skan narg = builtin_save_expr (arg); 1858132727Skan if (narg != arg) 1859132727Skan { 1860146906Skan arg = narg; 1861132727Skan arglist = build_tree_list (NULL_TREE, arg); 1862132727Skan exp = build_function_call_expr (fndecl, arglist); 1863132727Skan } 1864132727Skan 1865132727Skan op0 = expand_expr (arg, subtarget, VOIDmode, 0); 1866132727Skan 1867132727Skan start_sequence (); 1868132727Skan 1869132727Skan /* Compute into TARGET. 1870132727Skan Set TARGET to wherever the result comes back. */ 1871132727Skan target = expand_unop (mode, builtin_optab, op0, target, 0); 1872132727Skan 1873132727Skan if (target != 0) 1874132727Skan { 1875132727Skan if (errno_set) 1876132727Skan expand_errno_check (exp, target); 1877132727Skan 1878132727Skan /* Output the entire sequence. */ 1879132727Skan insns = get_insns (); 1880132727Skan end_sequence (); 1881132727Skan emit_insn (insns); 1882132727Skan return target; 1883132727Skan } 1884132727Skan 1885132727Skan /* If we were unable to expand via the builtin, stop the sequence 1886132727Skan (without outputting the insns) and call to the library function 1887132727Skan with the stabilized argument list. */ 188890075Sobrien end_sequence (); 188990075Sobrien } 189090075Sobrien 1891132727Skan before_call = get_last_insn (); 189290075Sobrien 1893132727Skan target = expand_call (exp, target, target == const0_rtx); 1894132727Skan 1895132727Skan /* If this is a sqrt operation and we don't care about errno, try to 1896132727Skan attach a REG_EQUAL note with a SQRT rtx to the emitted libcall. 1897132727Skan This allows the semantics of the libcall to be visible to the RTL 1898132727Skan optimizers. */ 1899132727Skan if (builtin_optab == sqrt_optab && !errno_set) 190090075Sobrien { 1901132727Skan /* Search backwards through the insns emitted by expand_call looking 1902132727Skan for the instruction with the REG_RETVAL note. */ 1903132727Skan rtx last = get_last_insn (); 1904132727Skan while (last != before_call) 1905132727Skan { 1906132727Skan if (find_reg_note (last, REG_RETVAL, NULL)) 1907132727Skan { 1908132727Skan rtx note = find_reg_note (last, REG_EQUAL, NULL); 1909132727Skan /* Check that the REQ_EQUAL note is an EXPR_LIST with 1910132727Skan two elements, i.e. symbol_ref(sqrt) and the operand. */ 1911132727Skan if (note 1912132727Skan && GET_CODE (note) == EXPR_LIST 1913132727Skan && GET_CODE (XEXP (note, 0)) == EXPR_LIST 1914132727Skan && XEXP (XEXP (note, 0), 1) != NULL_RTX 1915132727Skan && XEXP (XEXP (XEXP (note, 0), 1), 1) == NULL_RTX) 1916132727Skan { 1917132727Skan rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0); 1918132727Skan /* Check operand is a register with expected mode. */ 1919132727Skan if (operand 1920169699Skan && REG_P (operand) 1921132727Skan && GET_MODE (operand) == mode) 1922132727Skan { 1923132727Skan /* Replace the REG_EQUAL note with a SQRT rtx. */ 1924132727Skan rtx equiv = gen_rtx_SQRT (mode, operand); 1925132727Skan set_unique_reg_note (last, REG_EQUAL, equiv); 1926132727Skan } 1927132727Skan } 1928132727Skan break; 1929132727Skan } 1930132727Skan last = PREV_INSN (last); 1931132727Skan } 1932132727Skan } 193390075Sobrien 1934132727Skan return target; 1935132727Skan} 193690075Sobrien 1937132727Skan/* Expand a call to the builtin binary math functions (pow and atan2). 1938132727Skan Return 0 if a normal call should be emitted rather than expanding the 1939132727Skan function in-line. EXP is the expression that is a call to the builtin 1940132727Skan function; if convenient, the result should be placed in TARGET. 1941132727Skan SUBTARGET may be used as the target for computing one of EXP's 1942132727Skan operands. */ 194390075Sobrien 1944132727Skanstatic rtx 1945132727Skanexpand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) 1946132727Skan{ 1947132727Skan optab builtin_optab; 1948132727Skan rtx op0, op1, insns; 1949169699Skan int op1_type = REAL_TYPE; 1950132727Skan tree fndecl = get_callee_fndecl (exp); 1951132727Skan tree arglist = TREE_OPERAND (exp, 1); 1952132727Skan tree arg0, arg1, temp, narg; 1953132727Skan enum machine_mode mode; 1954132727Skan bool errno_set = true; 1955132727Skan bool stable = true; 195690075Sobrien 1957169699Skan if ((DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXP) 1958169699Skan || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPF) 1959169699Skan || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPL)) 1960169699Skan op1_type = INTEGER_TYPE; 1961169699Skan 1962169699Skan if (!validate_arglist (arglist, REAL_TYPE, op1_type, VOID_TYPE)) 1963132727Skan return 0; 196490075Sobrien 1965132727Skan arg0 = TREE_VALUE (arglist); 1966132727Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 1967132727Skan 1968132727Skan switch (DECL_FUNCTION_CODE (fndecl)) 1969132727Skan { 1970169699Skan CASE_FLT_FN (BUILT_IN_POW): 1971132727Skan builtin_optab = pow_optab; break; 1972169699Skan CASE_FLT_FN (BUILT_IN_ATAN2): 1973132727Skan builtin_optab = atan2_optab; break; 1974169699Skan CASE_FLT_FN (BUILT_IN_LDEXP): 1975169699Skan builtin_optab = ldexp_optab; break; 1976169699Skan CASE_FLT_FN (BUILT_IN_FMOD): 1977169699Skan builtin_optab = fmod_optab; break; 1978169699Skan CASE_FLT_FN (BUILT_IN_DREM): 1979169699Skan builtin_optab = drem_optab; break; 1980132727Skan default: 1981169699Skan gcc_unreachable (); 198290075Sobrien } 198390075Sobrien 1984132727Skan /* Make a suitable register to place result in. */ 1985132727Skan mode = TYPE_MODE (TREE_TYPE (exp)); 1986132727Skan 1987132727Skan /* Before working hard, check whether the instruction is available. */ 1988132727Skan if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) 1989132727Skan return 0; 1990132727Skan 1991132727Skan target = gen_reg_rtx (mode); 1992132727Skan 1993132727Skan if (! flag_errno_math || ! HONOR_NANS (mode)) 1994132727Skan errno_set = false; 1995132727Skan 1996169699Skan /* Always stabilize the argument list. */ 1997169699Skan narg = builtin_save_expr (arg1); 1998132727Skan if (narg != arg1) 1999132727Skan { 2000146906Skan arg1 = narg; 2001132727Skan temp = build_tree_list (NULL_TREE, narg); 2002132727Skan stable = false; 2003132727Skan } 2004132727Skan else 2005132727Skan temp = TREE_CHAIN (arglist); 2006132727Skan 2007169699Skan narg = builtin_save_expr (arg0); 2008132727Skan if (narg != arg0) 2009132727Skan { 2010146906Skan arg0 = narg; 2011132727Skan arglist = tree_cons (NULL_TREE, narg, temp); 2012132727Skan stable = false; 2013132727Skan } 2014132727Skan else if (! stable) 2015132727Skan arglist = tree_cons (NULL_TREE, arg0, temp); 2016132727Skan 2017132727Skan if (! stable) 2018132727Skan exp = build_function_call_expr (fndecl, arglist); 2019132727Skan 2020169699Skan op0 = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL); 2021169699Skan op1 = expand_normal (arg1); 2022132727Skan 2023132727Skan start_sequence (); 2024132727Skan 2025132727Skan /* Compute into TARGET. 2026132727Skan Set TARGET to wherever the result comes back. */ 2027132727Skan target = expand_binop (mode, builtin_optab, op0, op1, 2028132727Skan target, 0, OPTAB_DIRECT); 2029132727Skan 2030132727Skan /* If we were unable to expand via the builtin, stop the sequence 2031132727Skan (without outputting the insns) and call to the library function 2032132727Skan with the stabilized argument list. */ 2033132727Skan if (target == 0) 2034132727Skan { 2035132727Skan end_sequence (); 2036132727Skan return expand_call (exp, target, target == const0_rtx); 2037132727Skan } 2038132727Skan 2039132727Skan if (errno_set) 2040132727Skan expand_errno_check (exp, target); 2041132727Skan 204290075Sobrien /* Output the entire sequence. */ 204390075Sobrien insns = get_insns (); 204490075Sobrien end_sequence (); 2045117404Skan emit_insn (insns); 204690075Sobrien 204790075Sobrien return target; 204890075Sobrien} 204990075Sobrien 2050169699Skan/* Expand a call to the builtin sin and cos math functions. 2051169699Skan Return 0 if a normal call should be emitted rather than expanding the 2052169699Skan function in-line. EXP is the expression that is a call to the builtin 2053169699Skan function; if convenient, the result should be placed in TARGET. 2054169699Skan SUBTARGET may be used as the target for computing one of EXP's 2055169699Skan operands. */ 2056169699Skan 2057169699Skanstatic rtx 2058169699Skanexpand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) 2059169699Skan{ 2060169699Skan optab builtin_optab; 2061169699Skan rtx op0, insns; 2062169699Skan tree fndecl = get_callee_fndecl (exp); 2063169699Skan tree arglist = TREE_OPERAND (exp, 1); 2064169699Skan enum machine_mode mode; 2065169699Skan tree arg, narg; 2066169699Skan 2067169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 2068169699Skan return 0; 2069169699Skan 2070169699Skan arg = TREE_VALUE (arglist); 2071169699Skan 2072169699Skan switch (DECL_FUNCTION_CODE (fndecl)) 2073169699Skan { 2074169699Skan CASE_FLT_FN (BUILT_IN_SIN): 2075169699Skan CASE_FLT_FN (BUILT_IN_COS): 2076169699Skan builtin_optab = sincos_optab; break; 2077169699Skan default: 2078169699Skan gcc_unreachable (); 2079169699Skan } 2080169699Skan 2081169699Skan /* Make a suitable register to place result in. */ 2082169699Skan mode = TYPE_MODE (TREE_TYPE (exp)); 2083169699Skan 2084169699Skan /* Check if sincos insn is available, otherwise fallback 2085169699Skan to sin or cos insn. */ 2086169699Skan if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) { 2087169699Skan switch (DECL_FUNCTION_CODE (fndecl)) 2088169699Skan { 2089169699Skan CASE_FLT_FN (BUILT_IN_SIN): 2090169699Skan builtin_optab = sin_optab; break; 2091169699Skan CASE_FLT_FN (BUILT_IN_COS): 2092169699Skan builtin_optab = cos_optab; break; 2093169699Skan default: 2094169699Skan gcc_unreachable (); 2095169699Skan } 2096169699Skan } 2097169699Skan 2098169699Skan /* Before working hard, check whether the instruction is available. */ 2099169699Skan if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 2100169699Skan { 2101169699Skan target = gen_reg_rtx (mode); 2102169699Skan 2103169699Skan /* Wrap the computation of the argument in a SAVE_EXPR, as we may 2104169699Skan need to expand the argument again. This way, we will not perform 2105169699Skan side-effects more the once. */ 2106169699Skan narg = save_expr (arg); 2107169699Skan if (narg != arg) 2108169699Skan { 2109169699Skan arg = narg; 2110169699Skan arglist = build_tree_list (NULL_TREE, arg); 2111169699Skan exp = build_function_call_expr (fndecl, arglist); 2112169699Skan } 2113169699Skan 2114169699Skan op0 = expand_expr (arg, subtarget, VOIDmode, 0); 2115169699Skan 2116169699Skan start_sequence (); 2117169699Skan 2118169699Skan /* Compute into TARGET. 2119169699Skan Set TARGET to wherever the result comes back. */ 2120169699Skan if (builtin_optab == sincos_optab) 2121169699Skan { 2122169699Skan int result; 2123169699Skan 2124169699Skan switch (DECL_FUNCTION_CODE (fndecl)) 2125169699Skan { 2126169699Skan CASE_FLT_FN (BUILT_IN_SIN): 2127169699Skan result = expand_twoval_unop (builtin_optab, op0, 0, target, 0); 2128169699Skan break; 2129169699Skan CASE_FLT_FN (BUILT_IN_COS): 2130169699Skan result = expand_twoval_unop (builtin_optab, op0, target, 0, 0); 2131169699Skan break; 2132169699Skan default: 2133169699Skan gcc_unreachable (); 2134169699Skan } 2135169699Skan gcc_assert (result); 2136169699Skan } 2137169699Skan else 2138169699Skan { 2139169699Skan target = expand_unop (mode, builtin_optab, op0, target, 0); 2140169699Skan } 2141169699Skan 2142169699Skan if (target != 0) 2143169699Skan { 2144169699Skan /* Output the entire sequence. */ 2145169699Skan insns = get_insns (); 2146169699Skan end_sequence (); 2147169699Skan emit_insn (insns); 2148169699Skan return target; 2149169699Skan } 2150169699Skan 2151169699Skan /* If we were unable to expand via the builtin, stop the sequence 2152169699Skan (without outputting the insns) and call to the library function 2153169699Skan with the stabilized argument list. */ 2154169699Skan end_sequence (); 2155169699Skan } 2156169699Skan 2157169699Skan target = expand_call (exp, target, target == const0_rtx); 2158169699Skan 2159169699Skan return target; 2160169699Skan} 2161169699Skan 2162169699Skan/* Expand a call to the builtin sincos math function. 2163169699Skan Return 0 if a normal call should be emitted rather than expanding the 2164169699Skan function in-line. EXP is the expression that is a call to the builtin 2165169699Skan function. */ 2166169699Skan 2167169699Skanstatic rtx 2168169699Skanexpand_builtin_sincos (tree exp) 2169169699Skan{ 2170169699Skan rtx op0, op1, op2, target1, target2; 2171169699Skan tree arglist = TREE_OPERAND (exp, 1); 2172169699Skan enum machine_mode mode; 2173169699Skan tree arg, sinp, cosp; 2174169699Skan int result; 2175169699Skan 2176169699Skan if (!validate_arglist (arglist, REAL_TYPE, 2177169699Skan POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 2178169699Skan return 0; 2179169699Skan 2180169699Skan arg = TREE_VALUE (arglist); 2181169699Skan sinp = TREE_VALUE (TREE_CHAIN (arglist)); 2182169699Skan cosp = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 2183169699Skan 2184169699Skan /* Make a suitable register to place result in. */ 2185169699Skan mode = TYPE_MODE (TREE_TYPE (arg)); 2186169699Skan 2187169699Skan /* Check if sincos insn is available, otherwise emit the call. */ 2188169699Skan if (sincos_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) 2189169699Skan return NULL_RTX; 2190169699Skan 2191169699Skan target1 = gen_reg_rtx (mode); 2192169699Skan target2 = gen_reg_rtx (mode); 2193169699Skan 2194169699Skan op0 = expand_normal (arg); 2195169699Skan op1 = expand_normal (build_fold_indirect_ref (sinp)); 2196169699Skan op2 = expand_normal (build_fold_indirect_ref (cosp)); 2197169699Skan 2198169699Skan /* Compute into target1 and target2. 2199169699Skan Set TARGET to wherever the result comes back. */ 2200169699Skan result = expand_twoval_unop (sincos_optab, op0, target2, target1, 0); 2201169699Skan gcc_assert (result); 2202169699Skan 2203169699Skan /* Move target1 and target2 to the memory locations indicated 2204169699Skan by op1 and op2. */ 2205169699Skan emit_move_insn (op1, target1); 2206169699Skan emit_move_insn (op2, target2); 2207169699Skan 2208169699Skan return const0_rtx; 2209169699Skan} 2210169699Skan 2211169699Skan/* Expand a call to one of the builtin rounding functions (lfloor). 2212169699Skan If expanding via optab fails, lower expression to (int)(floor(x)). 2213169699Skan EXP is the expression that is a call to the builtin function; 2214169699Skan if convenient, the result should be placed in TARGET. SUBTARGET may 2215169699Skan be used as the target for computing one of EXP's operands. */ 2216169699Skan 2217169699Skanstatic rtx 2218169699Skanexpand_builtin_int_roundingfn (tree exp, rtx target, rtx subtarget) 2219169699Skan{ 2220169699Skan optab builtin_optab; 2221169699Skan rtx op0, insns, tmp; 2222169699Skan tree fndecl = get_callee_fndecl (exp); 2223169699Skan tree arglist = TREE_OPERAND (exp, 1); 2224169699Skan enum built_in_function fallback_fn; 2225169699Skan tree fallback_fndecl; 2226169699Skan enum machine_mode mode; 2227169699Skan tree arg, narg; 2228169699Skan 2229169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 2230169699Skan gcc_unreachable (); 2231169699Skan 2232169699Skan arg = TREE_VALUE (arglist); 2233169699Skan 2234169699Skan switch (DECL_FUNCTION_CODE (fndecl)) 2235169699Skan { 2236169699Skan CASE_FLT_FN (BUILT_IN_LCEIL): 2237169699Skan CASE_FLT_FN (BUILT_IN_LLCEIL): 2238169699Skan builtin_optab = lceil_optab; 2239169699Skan fallback_fn = BUILT_IN_CEIL; 2240169699Skan break; 2241169699Skan 2242169699Skan CASE_FLT_FN (BUILT_IN_LFLOOR): 2243169699Skan CASE_FLT_FN (BUILT_IN_LLFLOOR): 2244169699Skan builtin_optab = lfloor_optab; 2245169699Skan fallback_fn = BUILT_IN_FLOOR; 2246169699Skan break; 2247169699Skan 2248169699Skan default: 2249169699Skan gcc_unreachable (); 2250169699Skan } 2251169699Skan 2252169699Skan /* Make a suitable register to place result in. */ 2253169699Skan mode = TYPE_MODE (TREE_TYPE (exp)); 2254169699Skan 2255169699Skan /* Before working hard, check whether the instruction is available. */ 2256169699Skan if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 2257169699Skan { 2258169699Skan target = gen_reg_rtx (mode); 2259169699Skan 2260169699Skan /* Wrap the computation of the argument in a SAVE_EXPR, as we may 2261169699Skan need to expand the argument again. This way, we will not perform 2262169699Skan side-effects more the once. */ 2263169699Skan narg = builtin_save_expr (arg); 2264169699Skan if (narg != arg) 2265169699Skan { 2266169699Skan arg = narg; 2267169699Skan arglist = build_tree_list (NULL_TREE, arg); 2268169699Skan exp = build_function_call_expr (fndecl, arglist); 2269169699Skan } 2270169699Skan 2271169699Skan op0 = expand_expr (arg, subtarget, VOIDmode, 0); 2272169699Skan 2273169699Skan start_sequence (); 2274169699Skan 2275169699Skan /* Compute into TARGET. 2276169699Skan Set TARGET to wherever the result comes back. */ 2277169699Skan target = expand_unop (mode, builtin_optab, op0, target, 0); 2278169699Skan 2279169699Skan if (target != 0) 2280169699Skan { 2281169699Skan /* Output the entire sequence. */ 2282169699Skan insns = get_insns (); 2283169699Skan end_sequence (); 2284169699Skan emit_insn (insns); 2285169699Skan return target; 2286169699Skan } 2287169699Skan 2288169699Skan /* If we were unable to expand via the builtin, stop the sequence 2289169699Skan (without outputting the insns). */ 2290169699Skan end_sequence (); 2291169699Skan } 2292169699Skan 2293169699Skan /* Fall back to floating point rounding optab. */ 2294169699Skan fallback_fndecl = mathfn_built_in (TREE_TYPE (arg), fallback_fn); 2295169699Skan /* We shouldn't get here on targets without TARGET_C99_FUNCTIONS. 2296169699Skan ??? Perhaps convert (int)floorf(x) into (int)floor((double)x). */ 2297169699Skan gcc_assert (fallback_fndecl != NULL_TREE); 2298169699Skan exp = build_function_call_expr (fallback_fndecl, arglist); 2299169699Skan 2300169699Skan tmp = expand_normal (exp); 2301169699Skan 2302169699Skan /* Truncate the result of floating point optab to integer 2303169699Skan via expand_fix (). */ 2304169699Skan target = gen_reg_rtx (mode); 2305169699Skan expand_fix (target, tmp, 0); 2306169699Skan 2307169699Skan return target; 2308169699Skan} 2309169699Skan 2310132727Skan/* To evaluate powi(x,n), the floating point value x raised to the 2311132727Skan constant integer exponent n, we use a hybrid algorithm that 2312132727Skan combines the "window method" with look-up tables. For an 2313132727Skan introduction to exponentiation algorithms and "addition chains", 2314132727Skan see section 4.6.3, "Evaluation of Powers" of Donald E. Knuth, 2315132727Skan "Seminumerical Algorithms", Vol. 2, "The Art of Computer Programming", 2316132727Skan 3rd Edition, 1998, and Daniel M. Gordon, "A Survey of Fast Exponentiation 2317132727Skan Methods", Journal of Algorithms, Vol. 27, pp. 129-146, 1998. */ 2318132727Skan 2319132727Skan/* Provide a default value for POWI_MAX_MULTS, the maximum number of 2320132727Skan multiplications to inline before calling the system library's pow 2321132727Skan function. powi(x,n) requires at worst 2*bits(n)-2 multiplications, 2322132727Skan so this default never requires calling pow, powf or powl. */ 2323169699Skan 2324132727Skan#ifndef POWI_MAX_MULTS 2325132727Skan#define POWI_MAX_MULTS (2*HOST_BITS_PER_WIDE_INT-2) 2326132727Skan#endif 2327132727Skan 2328132727Skan/* The size of the "optimal power tree" lookup table. All 2329132727Skan exponents less than this value are simply looked up in the 2330132727Skan powi_table below. This threshold is also used to size the 2331132727Skan cache of pseudo registers that hold intermediate results. */ 2332132727Skan#define POWI_TABLE_SIZE 256 2333132727Skan 2334132727Skan/* The size, in bits of the window, used in the "window method" 2335132727Skan exponentiation algorithm. This is equivalent to a radix of 2336132727Skan (1<<POWI_WINDOW_SIZE) in the corresponding "m-ary method". */ 2337132727Skan#define POWI_WINDOW_SIZE 3 2338132727Skan 2339132727Skan/* The following table is an efficient representation of an 2340132727Skan "optimal power tree". For each value, i, the corresponding 2341132727Skan value, j, in the table states than an optimal evaluation 2342132727Skan sequence for calculating pow(x,i) can be found by evaluating 2343132727Skan pow(x,j)*pow(x,i-j). An optimal power tree for the first 2344132727Skan 100 integers is given in Knuth's "Seminumerical algorithms". */ 2345132727Skan 2346132727Skanstatic const unsigned char powi_table[POWI_TABLE_SIZE] = 2347132727Skan { 2348132727Skan 0, 1, 1, 2, 2, 3, 3, 4, /* 0 - 7 */ 2349132727Skan 4, 6, 5, 6, 6, 10, 7, 9, /* 8 - 15 */ 2350132727Skan 8, 16, 9, 16, 10, 12, 11, 13, /* 16 - 23 */ 2351132727Skan 12, 17, 13, 18, 14, 24, 15, 26, /* 24 - 31 */ 2352132727Skan 16, 17, 17, 19, 18, 33, 19, 26, /* 32 - 39 */ 2353132727Skan 20, 25, 21, 40, 22, 27, 23, 44, /* 40 - 47 */ 2354132727Skan 24, 32, 25, 34, 26, 29, 27, 44, /* 48 - 55 */ 2355132727Skan 28, 31, 29, 34, 30, 60, 31, 36, /* 56 - 63 */ 2356132727Skan 32, 64, 33, 34, 34, 46, 35, 37, /* 64 - 71 */ 2357132727Skan 36, 65, 37, 50, 38, 48, 39, 69, /* 72 - 79 */ 2358132727Skan 40, 49, 41, 43, 42, 51, 43, 58, /* 80 - 87 */ 2359132727Skan 44, 64, 45, 47, 46, 59, 47, 76, /* 88 - 95 */ 2360132727Skan 48, 65, 49, 66, 50, 67, 51, 66, /* 96 - 103 */ 2361132727Skan 52, 70, 53, 74, 54, 104, 55, 74, /* 104 - 111 */ 2362132727Skan 56, 64, 57, 69, 58, 78, 59, 68, /* 112 - 119 */ 2363132727Skan 60, 61, 61, 80, 62, 75, 63, 68, /* 120 - 127 */ 2364132727Skan 64, 65, 65, 128, 66, 129, 67, 90, /* 128 - 135 */ 2365132727Skan 68, 73, 69, 131, 70, 94, 71, 88, /* 136 - 143 */ 2366132727Skan 72, 128, 73, 98, 74, 132, 75, 121, /* 144 - 151 */ 2367132727Skan 76, 102, 77, 124, 78, 132, 79, 106, /* 152 - 159 */ 2368132727Skan 80, 97, 81, 160, 82, 99, 83, 134, /* 160 - 167 */ 2369132727Skan 84, 86, 85, 95, 86, 160, 87, 100, /* 168 - 175 */ 2370132727Skan 88, 113, 89, 98, 90, 107, 91, 122, /* 176 - 183 */ 2371132727Skan 92, 111, 93, 102, 94, 126, 95, 150, /* 184 - 191 */ 2372132727Skan 96, 128, 97, 130, 98, 133, 99, 195, /* 192 - 199 */ 2373132727Skan 100, 128, 101, 123, 102, 164, 103, 138, /* 200 - 207 */ 2374132727Skan 104, 145, 105, 146, 106, 109, 107, 149, /* 208 - 215 */ 2375132727Skan 108, 200, 109, 146, 110, 170, 111, 157, /* 216 - 223 */ 2376132727Skan 112, 128, 113, 130, 114, 182, 115, 132, /* 224 - 231 */ 2377132727Skan 116, 200, 117, 132, 118, 158, 119, 206, /* 232 - 239 */ 2378132727Skan 120, 240, 121, 162, 122, 147, 123, 152, /* 240 - 247 */ 2379132727Skan 124, 166, 125, 214, 126, 138, 127, 153, /* 248 - 255 */ 2380132727Skan }; 2381132727Skan 2382132727Skan 2383132727Skan/* Return the number of multiplications required to calculate 2384132727Skan powi(x,n) where n is less than POWI_TABLE_SIZE. This is a 2385132727Skan subroutine of powi_cost. CACHE is an array indicating 2386132727Skan which exponents have already been calculated. */ 2387132727Skan 2388132727Skanstatic int 2389132727Skanpowi_lookup_cost (unsigned HOST_WIDE_INT n, bool *cache) 2390132727Skan{ 2391132727Skan /* If we've already calculated this exponent, then this evaluation 2392132727Skan doesn't require any additional multiplications. */ 2393132727Skan if (cache[n]) 2394132727Skan return 0; 2395132727Skan 2396132727Skan cache[n] = true; 2397132727Skan return powi_lookup_cost (n - powi_table[n], cache) 2398132727Skan + powi_lookup_cost (powi_table[n], cache) + 1; 2399132727Skan} 2400132727Skan 2401132727Skan/* Return the number of multiplications required to calculate 2402132727Skan powi(x,n) for an arbitrary x, given the exponent N. This 2403132727Skan function needs to be kept in sync with expand_powi below. */ 2404132727Skan 2405132727Skanstatic int 2406132727Skanpowi_cost (HOST_WIDE_INT n) 2407132727Skan{ 2408132727Skan bool cache[POWI_TABLE_SIZE]; 2409132727Skan unsigned HOST_WIDE_INT digit; 2410132727Skan unsigned HOST_WIDE_INT val; 2411132727Skan int result; 2412132727Skan 2413132727Skan if (n == 0) 2414132727Skan return 0; 2415132727Skan 2416132727Skan /* Ignore the reciprocal when calculating the cost. */ 2417132727Skan val = (n < 0) ? -n : n; 2418132727Skan 2419132727Skan /* Initialize the exponent cache. */ 2420132727Skan memset (cache, 0, POWI_TABLE_SIZE * sizeof (bool)); 2421132727Skan cache[1] = true; 2422132727Skan 2423132727Skan result = 0; 2424132727Skan 2425132727Skan while (val >= POWI_TABLE_SIZE) 2426132727Skan { 2427132727Skan if (val & 1) 2428132727Skan { 2429132727Skan digit = val & ((1 << POWI_WINDOW_SIZE) - 1); 2430132727Skan result += powi_lookup_cost (digit, cache) 2431132727Skan + POWI_WINDOW_SIZE + 1; 2432132727Skan val >>= POWI_WINDOW_SIZE; 2433132727Skan } 2434132727Skan else 2435132727Skan { 2436132727Skan val >>= 1; 2437132727Skan result++; 2438132727Skan } 2439132727Skan } 2440169699Skan 2441132727Skan return result + powi_lookup_cost (val, cache); 2442132727Skan} 2443132727Skan 2444132727Skan/* Recursive subroutine of expand_powi. This function takes the array, 2445132727Skan CACHE, of already calculated exponents and an exponent N and returns 2446132727Skan an RTX that corresponds to CACHE[1]**N, as calculated in mode MODE. */ 2447132727Skan 2448132727Skanstatic rtx 2449132727Skanexpand_powi_1 (enum machine_mode mode, unsigned HOST_WIDE_INT n, rtx *cache) 2450132727Skan{ 2451132727Skan unsigned HOST_WIDE_INT digit; 2452132727Skan rtx target, result; 2453132727Skan rtx op0, op1; 2454132727Skan 2455132727Skan if (n < POWI_TABLE_SIZE) 2456132727Skan { 2457132727Skan if (cache[n]) 2458169699Skan return cache[n]; 2459132727Skan 2460132727Skan target = gen_reg_rtx (mode); 2461132727Skan cache[n] = target; 2462132727Skan 2463132727Skan op0 = expand_powi_1 (mode, n - powi_table[n], cache); 2464132727Skan op1 = expand_powi_1 (mode, powi_table[n], cache); 2465132727Skan } 2466132727Skan else if (n & 1) 2467132727Skan { 2468132727Skan target = gen_reg_rtx (mode); 2469132727Skan digit = n & ((1 << POWI_WINDOW_SIZE) - 1); 2470132727Skan op0 = expand_powi_1 (mode, n - digit, cache); 2471132727Skan op1 = expand_powi_1 (mode, digit, cache); 2472132727Skan } 2473132727Skan else 2474132727Skan { 2475132727Skan target = gen_reg_rtx (mode); 2476132727Skan op0 = expand_powi_1 (mode, n >> 1, cache); 2477132727Skan op1 = op0; 2478132727Skan } 2479132727Skan 2480132727Skan result = expand_mult (mode, op0, op1, target, 0); 2481132727Skan if (result != target) 2482132727Skan emit_move_insn (target, result); 2483132727Skan return target; 2484132727Skan} 2485132727Skan 2486132727Skan/* Expand the RTL to evaluate powi(x,n) in mode MODE. X is the 2487132727Skan floating point operand in mode MODE, and N is the exponent. This 2488132727Skan function needs to be kept in sync with powi_cost above. */ 2489169699Skan 2490132727Skanstatic rtx 2491132727Skanexpand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n) 2492132727Skan{ 2493132727Skan unsigned HOST_WIDE_INT val; 2494132727Skan rtx cache[POWI_TABLE_SIZE]; 2495132727Skan rtx result; 2496132727Skan 2497132727Skan if (n == 0) 2498132727Skan return CONST1_RTX (mode); 2499132727Skan 2500132727Skan val = (n < 0) ? -n : n; 2501132727Skan 2502132727Skan memset (cache, 0, sizeof (cache)); 2503132727Skan cache[1] = x; 2504132727Skan 2505132727Skan result = expand_powi_1 (mode, (n < 0) ? -n : n, cache); 2506132727Skan 2507132727Skan /* If the original exponent was negative, reciprocate the result. */ 2508132727Skan if (n < 0) 2509132727Skan result = expand_binop (mode, sdiv_optab, CONST1_RTX (mode), 2510132727Skan result, NULL_RTX, 0, OPTAB_LIB_WIDEN); 2511132727Skan 2512132727Skan return result; 2513132727Skan} 2514132727Skan 2515132727Skan/* Expand a call to the pow built-in mathematical function. Return 0 if 2516132727Skan a normal call should be emitted rather than expanding the function 2517132727Skan in-line. EXP is the expression that is a call to the builtin 2518132727Skan function; if convenient, the result should be placed in TARGET. */ 2519132727Skan 2520132727Skanstatic rtx 2521132727Skanexpand_builtin_pow (tree exp, rtx target, rtx subtarget) 2522132727Skan{ 2523132727Skan tree arglist = TREE_OPERAND (exp, 1); 2524132727Skan tree arg0, arg1; 2525132727Skan 2526132727Skan if (! validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) 2527132727Skan return 0; 2528132727Skan 2529132727Skan arg0 = TREE_VALUE (arglist); 2530132727Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 2531132727Skan 2532132727Skan if (TREE_CODE (arg1) == REAL_CST 2533132727Skan && ! TREE_CONSTANT_OVERFLOW (arg1)) 2534132727Skan { 2535132727Skan REAL_VALUE_TYPE cint; 2536132727Skan REAL_VALUE_TYPE c; 2537132727Skan HOST_WIDE_INT n; 2538132727Skan 2539132727Skan c = TREE_REAL_CST (arg1); 2540132727Skan n = real_to_integer (&c); 2541132727Skan real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); 2542132727Skan if (real_identical (&c, &cint)) 2543132727Skan { 2544132727Skan /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact. 2545132727Skan Otherwise, check the number of multiplications required. 2546132727Skan Note that pow never sets errno for an integer exponent. */ 2547132727Skan if ((n >= -1 && n <= 2) 2548132727Skan || (flag_unsafe_math_optimizations 2549132727Skan && ! optimize_size 2550132727Skan && powi_cost (n) <= POWI_MAX_MULTS)) 2551132727Skan { 2552132727Skan enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); 2553132727Skan rtx op = expand_expr (arg0, subtarget, VOIDmode, 0); 2554132727Skan op = force_reg (mode, op); 2555132727Skan return expand_powi (op, mode, n); 2556132727Skan } 2557132727Skan } 2558132727Skan } 2559169699Skan 2560169699Skan if (! flag_unsafe_math_optimizations) 2561169699Skan return NULL_RTX; 2562169699Skan return expand_builtin_mathfn_2 (exp, target, subtarget); 2563132727Skan} 2564132727Skan 2565169699Skan/* Expand a call to the powi built-in mathematical function. Return 0 if 2566169699Skan a normal call should be emitted rather than expanding the function 2567169699Skan in-line. EXP is the expression that is a call to the builtin 2568169699Skan function; if convenient, the result should be placed in TARGET. */ 2569169699Skan 2570169699Skanstatic rtx 2571169699Skanexpand_builtin_powi (tree exp, rtx target, rtx subtarget) 2572169699Skan{ 2573169699Skan tree arglist = TREE_OPERAND (exp, 1); 2574169699Skan tree arg0, arg1; 2575169699Skan rtx op0, op1; 2576169699Skan enum machine_mode mode; 2577169699Skan enum machine_mode mode2; 2578169699Skan 2579169699Skan if (! validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE)) 2580169699Skan return 0; 2581169699Skan 2582169699Skan arg0 = TREE_VALUE (arglist); 2583169699Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 2584169699Skan mode = TYPE_MODE (TREE_TYPE (exp)); 2585169699Skan 2586169699Skan /* Handle constant power. */ 2587169699Skan 2588169699Skan if (TREE_CODE (arg1) == INTEGER_CST 2589169699Skan && ! TREE_CONSTANT_OVERFLOW (arg1)) 2590169699Skan { 2591169699Skan HOST_WIDE_INT n = TREE_INT_CST_LOW (arg1); 2592169699Skan 2593169699Skan /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact. 2594169699Skan Otherwise, check the number of multiplications required. */ 2595169699Skan if ((TREE_INT_CST_HIGH (arg1) == 0 2596169699Skan || TREE_INT_CST_HIGH (arg1) == -1) 2597169699Skan && ((n >= -1 && n <= 2) 2598169699Skan || (! optimize_size 2599169699Skan && powi_cost (n) <= POWI_MAX_MULTS))) 2600169699Skan { 2601169699Skan op0 = expand_expr (arg0, subtarget, VOIDmode, 0); 2602169699Skan op0 = force_reg (mode, op0); 2603169699Skan return expand_powi (op0, mode, n); 2604169699Skan } 2605169699Skan } 2606169699Skan 2607169699Skan /* Emit a libcall to libgcc. */ 2608169699Skan 2609169699Skan /* Mode of the 2nd argument must match that of an int. */ 2610169699Skan mode2 = mode_for_size (INT_TYPE_SIZE, MODE_INT, 0); 2611169699Skan 2612169699Skan if (target == NULL_RTX) 2613169699Skan target = gen_reg_rtx (mode); 2614169699Skan 2615169699Skan op0 = expand_expr (arg0, subtarget, mode, 0); 2616169699Skan if (GET_MODE (op0) != mode) 2617169699Skan op0 = convert_to_mode (mode, op0, 0); 2618169699Skan op1 = expand_expr (arg1, 0, mode2, 0); 2619169699Skan if (GET_MODE (op1) != mode2) 2620169699Skan op1 = convert_to_mode (mode2, op1, 0); 2621169699Skan 2622169699Skan target = emit_library_call_value (powi_optab->handlers[(int) mode].libfunc, 2623169699Skan target, LCT_CONST_MAKE_BLOCK, mode, 2, 2624169699Skan op0, mode, op1, mode2); 2625169699Skan 2626169699Skan return target; 2627169699Skan} 2628169699Skan 262990075Sobrien/* Expand expression EXP which is a call to the strlen builtin. Return 0 263090075Sobrien if we failed the caller should emit a normal call, otherwise 263190075Sobrien try to get the result in TARGET, if convenient. */ 263290075Sobrien 263390075Sobrienstatic rtx 2634132727Skanexpand_builtin_strlen (tree arglist, rtx target, 2635132727Skan enum machine_mode target_mode) 263690075Sobrien{ 263790075Sobrien if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) 263890075Sobrien return 0; 263990075Sobrien else 264090075Sobrien { 264190075Sobrien rtx pat; 2642132727Skan tree len, src = TREE_VALUE (arglist); 264390075Sobrien rtx result, src_reg, char_rtx, before_strlen; 2644132727Skan enum machine_mode insn_mode = target_mode, char_mode; 264590075Sobrien enum insn_code icode = CODE_FOR_nothing; 2646132727Skan int align; 264790075Sobrien 2648132727Skan /* If the length can be computed at compile-time, return it. */ 2649132727Skan len = c_strlen (src, 0); 2650132727Skan if (len) 2651132727Skan return expand_expr (len, target, target_mode, EXPAND_NORMAL); 2652132727Skan 2653132727Skan /* If the length can be computed at compile-time and is constant 2654132727Skan integer, but there are side-effects in src, evaluate 2655132727Skan src for side-effects, then return len. 2656132727Skan E.g. x = strlen (i++ ? "xfoo" + 1 : "bar"); 2657132727Skan can be optimized into: i++; x = 3; */ 2658132727Skan len = c_strlen (src, 1); 2659132727Skan if (len && TREE_CODE (len) == INTEGER_CST) 2660132727Skan { 2661132727Skan expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); 2662132727Skan return expand_expr (len, target, target_mode, EXPAND_NORMAL); 2663132727Skan } 2664132727Skan 2665132727Skan align = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; 2666132727Skan 266790075Sobrien /* If SRC is not a pointer type, don't do this operation inline. */ 266890075Sobrien if (align == 0) 266990075Sobrien return 0; 267090075Sobrien 267190075Sobrien /* Bail out if we can't compute strlen in the right mode. */ 267290075Sobrien while (insn_mode != VOIDmode) 267390075Sobrien { 267490075Sobrien icode = strlen_optab->handlers[(int) insn_mode].insn_code; 267590075Sobrien if (icode != CODE_FOR_nothing) 267690075Sobrien break; 267790075Sobrien 267890075Sobrien insn_mode = GET_MODE_WIDER_MODE (insn_mode); 267990075Sobrien } 268090075Sobrien if (insn_mode == VOIDmode) 268190075Sobrien return 0; 268290075Sobrien 268390075Sobrien /* Make a place to write the result of the instruction. */ 268490075Sobrien result = target; 268590075Sobrien if (! (result != 0 2686169699Skan && REG_P (result) 268790075Sobrien && GET_MODE (result) == insn_mode 268890075Sobrien && REGNO (result) >= FIRST_PSEUDO_REGISTER)) 268990075Sobrien result = gen_reg_rtx (insn_mode); 269090075Sobrien 269190075Sobrien /* Make a place to hold the source address. We will not expand 269290075Sobrien the actual source until we are sure that the expansion will 269390075Sobrien not fail -- there are trees that cannot be expanded twice. */ 269490075Sobrien src_reg = gen_reg_rtx (Pmode); 269590075Sobrien 269690075Sobrien /* Mark the beginning of the strlen sequence so we can emit the 269790075Sobrien source operand later. */ 2698117404Skan before_strlen = get_last_insn (); 269990075Sobrien 270090075Sobrien char_rtx = const0_rtx; 270190075Sobrien char_mode = insn_data[(int) icode].operand[2].mode; 270290075Sobrien if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx, 270390075Sobrien char_mode)) 270490075Sobrien char_rtx = copy_to_mode_reg (char_mode, char_rtx); 270590075Sobrien 270690075Sobrien pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg), 270790075Sobrien char_rtx, GEN_INT (align)); 270890075Sobrien if (! pat) 270990075Sobrien return 0; 271090075Sobrien emit_insn (pat); 271190075Sobrien 271290075Sobrien /* Now that we are assured of success, expand the source. */ 271390075Sobrien start_sequence (); 2714169699Skan pat = expand_expr (src, src_reg, ptr_mode, EXPAND_NORMAL); 271590075Sobrien if (pat != src_reg) 271690075Sobrien emit_move_insn (src_reg, pat); 2717117404Skan pat = get_insns (); 271890075Sobrien end_sequence (); 271990075Sobrien 272090075Sobrien if (before_strlen) 272190075Sobrien emit_insn_after (pat, before_strlen); 272290075Sobrien else 272390075Sobrien emit_insn_before (pat, get_insns ()); 272490075Sobrien 272590075Sobrien /* Return the value in the proper mode for this function. */ 2726132727Skan if (GET_MODE (result) == target_mode) 272790075Sobrien target = result; 272890075Sobrien else if (target != 0) 272990075Sobrien convert_move (target, result, 0); 273090075Sobrien else 2731132727Skan target = convert_to_mode (target_mode, result, 0); 273290075Sobrien 273390075Sobrien return target; 273490075Sobrien } 273590075Sobrien} 273690075Sobrien 273790075Sobrien/* Expand a call to the strstr builtin. Return 0 if we failed the 273890075Sobrien caller should emit a normal call, otherwise try to get the result 273990075Sobrien in TARGET, if convenient (and in mode MODE if that's convenient). */ 274090075Sobrien 274190075Sobrienstatic rtx 2742169699Skanexpand_builtin_strstr (tree arglist, tree type, rtx target, enum machine_mode mode) 274390075Sobrien{ 2744169699Skan if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 274590075Sobrien { 2746169699Skan tree result = fold_builtin_strstr (arglist, type); 2747169699Skan if (result) 2748169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 274990075Sobrien } 2750169699Skan return 0; 275190075Sobrien} 275290075Sobrien 275390075Sobrien/* Expand a call to the strchr builtin. Return 0 if we failed the 275490075Sobrien caller should emit a normal call, otherwise try to get the result 275590075Sobrien in TARGET, if convenient (and in mode MODE if that's convenient). */ 275690075Sobrien 275790075Sobrienstatic rtx 2758169699Skanexpand_builtin_strchr (tree arglist, tree type, rtx target, enum machine_mode mode) 275990075Sobrien{ 2760169699Skan if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 276190075Sobrien { 2762169699Skan tree result = fold_builtin_strchr (arglist, type); 2763169699Skan if (result) 2764169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 276590075Sobrien 2766169699Skan /* FIXME: Should use strchrM optab so that ports can optimize this. */ 276790075Sobrien } 2768169699Skan return 0; 276990075Sobrien} 277090075Sobrien 277190075Sobrien/* Expand a call to the strrchr builtin. Return 0 if we failed the 277290075Sobrien caller should emit a normal call, otherwise try to get the result 277390075Sobrien in TARGET, if convenient (and in mode MODE if that's convenient). */ 277490075Sobrien 277590075Sobrienstatic rtx 2776169699Skanexpand_builtin_strrchr (tree arglist, tree type, rtx target, enum machine_mode mode) 277790075Sobrien{ 2778169699Skan if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 277990075Sobrien { 2780169699Skan tree result = fold_builtin_strrchr (arglist, type); 2781169699Skan if (result) 2782169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 278390075Sobrien } 2784169699Skan return 0; 278590075Sobrien} 278690075Sobrien 278790075Sobrien/* Expand a call to the strpbrk builtin. Return 0 if we failed the 278890075Sobrien caller should emit a normal call, otherwise try to get the result 278990075Sobrien in TARGET, if convenient (and in mode MODE if that's convenient). */ 279090075Sobrien 279190075Sobrienstatic rtx 2792169699Skanexpand_builtin_strpbrk (tree arglist, tree type, rtx target, enum machine_mode mode) 279390075Sobrien{ 2794169699Skan if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 279590075Sobrien { 2796169699Skan tree result = fold_builtin_strpbrk (arglist, type); 2797169699Skan if (result) 2798169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 279990075Sobrien } 2800169699Skan return 0; 280190075Sobrien} 280290075Sobrien 280390075Sobrien/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) 280490075Sobrien bytes from constant string DATA + OFFSET and return it as target 280590075Sobrien constant. */ 280690075Sobrien 280790075Sobrienstatic rtx 2808132727Skanbuiltin_memcpy_read_str (void *data, HOST_WIDE_INT offset, 2809132727Skan enum machine_mode mode) 281090075Sobrien{ 281190075Sobrien const char *str = (const char *) data; 281290075Sobrien 2813169699Skan gcc_assert (offset >= 0 2814169699Skan && ((unsigned HOST_WIDE_INT) offset + GET_MODE_SIZE (mode) 2815169699Skan <= strlen (str) + 1)); 281690075Sobrien 281790075Sobrien return c_readstr (str + offset, mode); 281890075Sobrien} 281990075Sobrien 282090075Sobrien/* Expand a call to the memcpy builtin, with arguments in ARGLIST. 2821132727Skan Return 0 if we failed, the caller should emit a normal call, 2822132727Skan otherwise try to get the result in TARGET, if convenient (and in 2823132727Skan mode MODE if that's convenient). */ 282490075Sobrienstatic rtx 2825169699Skanexpand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode) 282690075Sobrien{ 2827169699Skan tree fndecl = get_callee_fndecl (exp); 2828169699Skan tree arglist = TREE_OPERAND (exp, 1); 282990075Sobrien if (!validate_arglist (arglist, 283090075Sobrien POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 283190075Sobrien return 0; 283290075Sobrien else 283390075Sobrien { 283490075Sobrien tree dest = TREE_VALUE (arglist); 283590075Sobrien tree src = TREE_VALUE (TREE_CHAIN (arglist)); 283690075Sobrien tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 283790075Sobrien const char *src_str; 283890075Sobrien unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); 283990075Sobrien unsigned int dest_align 284090075Sobrien = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); 284190075Sobrien rtx dest_mem, src_mem, dest_addr, len_rtx; 2842169699Skan tree result = fold_builtin_memory_op (arglist, TREE_TYPE (TREE_TYPE (fndecl)), 2843169699Skan false, /*endp=*/0); 284490075Sobrien 2845169699Skan if (result) 2846169699Skan { 2847169699Skan while (TREE_CODE (result) == COMPOUND_EXPR) 2848169699Skan { 2849169699Skan expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode, 2850169699Skan EXPAND_NORMAL); 2851169699Skan result = TREE_OPERAND (result, 1); 2852169699Skan } 2853169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 2854169699Skan } 2855169699Skan 285690075Sobrien /* If DEST is not a pointer type, call the normal function. */ 285790075Sobrien if (dest_align == 0) 2858117404Skan return 0; 285990075Sobrien 286090075Sobrien /* If either SRC is not a pointer type, don't do this 2861169699Skan operation in-line. */ 286290075Sobrien if (src_align == 0) 2863117404Skan return 0; 286490075Sobrien 2865169699Skan dest_mem = get_memory_rtx (dest, len); 286690075Sobrien set_mem_align (dest_mem, dest_align); 2867169699Skan len_rtx = expand_normal (len); 286890075Sobrien src_str = c_getstr (src); 286990075Sobrien 287090075Sobrien /* If SRC is a string constant and block move would be done 287190075Sobrien by pieces, we can avoid loading the string from memory 287290075Sobrien and only stored the computed constants. */ 287390075Sobrien if (src_str 287490075Sobrien && GET_CODE (len_rtx) == CONST_INT 287590075Sobrien && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 287690075Sobrien && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, 2877132727Skan (void *) src_str, dest_align)) 287890075Sobrien { 2879132727Skan dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), 2880132727Skan builtin_memcpy_read_str, 2881132727Skan (void *) src_str, dest_align, 0); 2882117404Skan dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); 2883132727Skan dest_mem = convert_memory_address (ptr_mode, dest_mem); 2884117404Skan return dest_mem; 288590075Sobrien } 288690075Sobrien 2887169699Skan src_mem = get_memory_rtx (src, len); 288890075Sobrien set_mem_align (src_mem, src_align); 288990075Sobrien 289090075Sobrien /* Copy word part most expediently. */ 2891117404Skan dest_addr = emit_block_move (dest_mem, src_mem, len_rtx, 2892169699Skan CALL_EXPR_TAILCALL (exp) 2893169699Skan ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL); 289490075Sobrien 289590075Sobrien if (dest_addr == 0) 2896117404Skan { 2897117404Skan dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX); 2898132727Skan dest_addr = convert_memory_address (ptr_mode, dest_addr); 2899117404Skan } 290090075Sobrien return dest_addr; 290190075Sobrien } 290290075Sobrien} 290390075Sobrien 2904132727Skan/* Expand a call to the mempcpy builtin, with arguments in ARGLIST. 2905169699Skan Return 0 if we failed; the caller should emit a normal call, 2906132727Skan otherwise try to get the result in TARGET, if convenient (and in 2907132727Skan mode MODE if that's convenient). If ENDP is 0 return the 2908132727Skan destination pointer, if ENDP is 1 return the end pointer ala 2909132727Skan mempcpy, and if ENDP is 2 return the end pointer minus one ala 2910132727Skan stpcpy. */ 2911132727Skan 2912132727Skanstatic rtx 2913169699Skanexpand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode mode, 2914132727Skan int endp) 2915132727Skan{ 2916132727Skan if (!validate_arglist (arglist, 2917132727Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 2918132727Skan return 0; 2919132727Skan /* If return value is ignored, transform mempcpy into memcpy. */ 2920132727Skan else if (target == const0_rtx) 2921132727Skan { 2922132727Skan tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; 2923132727Skan 2924132727Skan if (!fn) 2925132727Skan return 0; 2926132727Skan 2927132727Skan return expand_expr (build_function_call_expr (fn, arglist), 2928132727Skan target, mode, EXPAND_NORMAL); 2929132727Skan } 2930132727Skan else 2931132727Skan { 2932132727Skan tree dest = TREE_VALUE (arglist); 2933132727Skan tree src = TREE_VALUE (TREE_CHAIN (arglist)); 2934132727Skan tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 2935132727Skan const char *src_str; 2936132727Skan unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); 2937132727Skan unsigned int dest_align 2938132727Skan = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); 2939132727Skan rtx dest_mem, src_mem, len_rtx; 2940169699Skan tree result = fold_builtin_memory_op (arglist, type, false, endp); 2941132727Skan 2942169699Skan if (result) 2943132727Skan { 2944169699Skan while (TREE_CODE (result) == COMPOUND_EXPR) 2945132727Skan { 2946169699Skan expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode, 2947169699Skan EXPAND_NORMAL); 2948169699Skan result = TREE_OPERAND (result, 1); 2949132727Skan } 2950169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 2951132727Skan } 2952132727Skan 2953169699Skan /* If either SRC or DEST is not a pointer type, don't do this 2954169699Skan operation in-line. */ 2955169699Skan if (dest_align == 0 || src_align == 0) 2956169699Skan return 0; 2957169699Skan 2958132727Skan /* If LEN is not constant, call the normal function. */ 2959132727Skan if (! host_integerp (len, 1)) 2960132727Skan return 0; 2961132727Skan 2962169699Skan len_rtx = expand_normal (len); 2963132727Skan src_str = c_getstr (src); 2964132727Skan 2965132727Skan /* If SRC is a string constant and block move would be done 2966132727Skan by pieces, we can avoid loading the string from memory 2967132727Skan and only stored the computed constants. */ 2968132727Skan if (src_str 2969132727Skan && GET_CODE (len_rtx) == CONST_INT 2970132727Skan && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 2971132727Skan && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, 2972132727Skan (void *) src_str, dest_align)) 2973132727Skan { 2974169699Skan dest_mem = get_memory_rtx (dest, len); 2975132727Skan set_mem_align (dest_mem, dest_align); 2976132727Skan dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), 2977132727Skan builtin_memcpy_read_str, 2978132727Skan (void *) src_str, dest_align, endp); 2979132727Skan dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); 2980132727Skan dest_mem = convert_memory_address (ptr_mode, dest_mem); 2981132727Skan return dest_mem; 2982132727Skan } 2983132727Skan 2984132727Skan if (GET_CODE (len_rtx) == CONST_INT 2985132727Skan && can_move_by_pieces (INTVAL (len_rtx), 2986132727Skan MIN (dest_align, src_align))) 2987132727Skan { 2988169699Skan dest_mem = get_memory_rtx (dest, len); 2989132727Skan set_mem_align (dest_mem, dest_align); 2990169699Skan src_mem = get_memory_rtx (src, len); 2991132727Skan set_mem_align (src_mem, src_align); 2992132727Skan dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx), 2993132727Skan MIN (dest_align, src_align), endp); 2994132727Skan dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); 2995132727Skan dest_mem = convert_memory_address (ptr_mode, dest_mem); 2996132727Skan return dest_mem; 2997132727Skan } 2998132727Skan 2999132727Skan return 0; 3000132727Skan } 3001132727Skan} 3002132727Skan 3003132727Skan/* Expand expression EXP, which is a call to the memmove builtin. Return 0 3004169699Skan if we failed; the caller should emit a normal call. */ 3005132727Skan 3006132727Skanstatic rtx 3007169699Skanexpand_builtin_memmove (tree arglist, tree type, rtx target, 3008169699Skan enum machine_mode mode, tree orig_exp) 3009132727Skan{ 3010132727Skan if (!validate_arglist (arglist, 3011132727Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 3012132727Skan return 0; 3013132727Skan else 3014132727Skan { 3015132727Skan tree dest = TREE_VALUE (arglist); 3016132727Skan tree src = TREE_VALUE (TREE_CHAIN (arglist)); 3017132727Skan tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 3018132727Skan 3019132727Skan unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); 3020132727Skan unsigned int dest_align 3021132727Skan = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); 3022169699Skan tree result = fold_builtin_memory_op (arglist, type, false, /*endp=*/3); 3023132727Skan 3024169699Skan if (result) 3025169699Skan { 3026169699Skan while (TREE_CODE (result) == COMPOUND_EXPR) 3027169699Skan { 3028169699Skan expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode, 3029169699Skan EXPAND_NORMAL); 3030169699Skan result = TREE_OPERAND (result, 1); 3031169699Skan } 3032169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 3033169699Skan } 3034169699Skan 3035132727Skan /* If DEST is not a pointer type, call the normal function. */ 3036132727Skan if (dest_align == 0) 3037132727Skan return 0; 3038132727Skan 3039132727Skan /* If either SRC is not a pointer type, don't do this 3040169699Skan operation in-line. */ 3041132727Skan if (src_align == 0) 3042132727Skan return 0; 3043132727Skan 3044132727Skan /* If src is categorized for a readonly section we can use 3045132727Skan normal memcpy. */ 3046132727Skan if (readonly_data_expr (src)) 3047169699Skan { 3048169699Skan tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; 3049132727Skan if (!fn) 3050132727Skan return 0; 3051169699Skan fn = build_function_call_expr (fn, arglist); 3052169699Skan if (TREE_CODE (fn) == CALL_EXPR) 3053169699Skan CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp); 3054169699Skan return expand_expr (fn, target, mode, EXPAND_NORMAL); 3055132727Skan } 3056132727Skan 3057169699Skan /* If length is 1 and we can expand memcpy call inline, 3058169699Skan it is ok to use memcpy as well. */ 3059169699Skan if (integer_onep (len)) 3060169699Skan { 3061169699Skan rtx ret = expand_builtin_mempcpy (arglist, type, target, mode, 3062169699Skan /*endp=*/0); 3063169699Skan if (ret) 3064169699Skan return ret; 3065169699Skan } 3066169699Skan 3067132727Skan /* Otherwise, call the normal function. */ 3068132727Skan return 0; 3069132727Skan } 3070132727Skan} 3071132727Skan 3072132727Skan/* Expand expression EXP, which is a call to the bcopy builtin. Return 0 3073132727Skan if we failed the caller should emit a normal call. */ 3074132727Skan 3075132727Skanstatic rtx 3076169699Skanexpand_builtin_bcopy (tree exp) 3077132727Skan{ 3078169699Skan tree arglist = TREE_OPERAND (exp, 1); 3079169699Skan tree type = TREE_TYPE (exp); 3080132727Skan tree src, dest, size, newarglist; 3081132727Skan 3082132727Skan if (!validate_arglist (arglist, 3083132727Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 3084132727Skan return NULL_RTX; 3085132727Skan 3086132727Skan src = TREE_VALUE (arglist); 3087132727Skan dest = TREE_VALUE (TREE_CHAIN (arglist)); 3088132727Skan size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 3089132727Skan 3090132727Skan /* New argument list transforming bcopy(ptr x, ptr y, int z) to 3091132727Skan memmove(ptr y, ptr x, size_t z). This is done this way 3092132727Skan so that if it isn't expanded inline, we fallback to 3093132727Skan calling bcopy instead of memmove. */ 3094132727Skan 3095169699Skan newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size)); 3096132727Skan newarglist = tree_cons (NULL_TREE, src, newarglist); 3097132727Skan newarglist = tree_cons (NULL_TREE, dest, newarglist); 3098132727Skan 3099169699Skan return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode, exp); 3100132727Skan} 3101132727Skan 3102169699Skan#ifndef HAVE_movstr 3103169699Skan# define HAVE_movstr 0 3104169699Skan# define CODE_FOR_movstr CODE_FOR_nothing 3105169699Skan#endif 310690075Sobrien 3107169699Skan/* Expand into a movstr instruction, if one is available. Return 0 if 3108169699Skan we failed, the caller should emit a normal call, otherwise try to 3109169699Skan get the result in TARGET, if convenient. If ENDP is 0 return the 3110169699Skan destination pointer, if ENDP is 1 return the end pointer ala 3111169699Skan mempcpy, and if ENDP is 2 return the end pointer minus one ala 3112169699Skan stpcpy. */ 3113169699Skan 311490075Sobrienstatic rtx 3115169699Skanexpand_movstr (tree dest, tree src, rtx target, int endp) 311690075Sobrien{ 3117169699Skan rtx end; 3118169699Skan rtx dest_mem; 3119169699Skan rtx src_mem; 3120169699Skan rtx insn; 3121169699Skan const struct insn_data * data; 312290075Sobrien 3123169699Skan if (!HAVE_movstr) 312490075Sobrien return 0; 312590075Sobrien 3126169699Skan dest_mem = get_memory_rtx (dest, NULL); 3127169699Skan src_mem = get_memory_rtx (src, NULL); 3128169699Skan if (!endp) 3129169699Skan { 3130169699Skan target = force_reg (Pmode, XEXP (dest_mem, 0)); 3131169699Skan dest_mem = replace_equiv_address (dest_mem, target); 3132169699Skan end = gen_reg_rtx (Pmode); 3133169699Skan } 3134169699Skan else 3135169699Skan { 3136169699Skan if (target == 0 || target == const0_rtx) 3137169699Skan { 3138169699Skan end = gen_reg_rtx (Pmode); 3139169699Skan if (target == 0) 3140169699Skan target = end; 3141169699Skan } 3142169699Skan else 3143169699Skan end = target; 3144169699Skan } 3145132727Skan 3146169699Skan data = insn_data + CODE_FOR_movstr; 3147132727Skan 3148169699Skan if (data->operand[0].mode != VOIDmode) 3149169699Skan end = gen_lowpart (data->operand[0].mode, end); 315090075Sobrien 3151169699Skan insn = data->genfun (end, dest_mem, src_mem); 315290075Sobrien 3153169699Skan gcc_assert (insn); 3154169699Skan 3155169699Skan emit_insn (insn); 3156169699Skan 3157169699Skan /* movstr is supposed to set end to the address of the NUL 3158169699Skan terminator. If the caller requested a mempcpy-like return value, 3159169699Skan adjust it. */ 3160169699Skan if (endp == 1 && target != const0_rtx) 3161169699Skan { 3162169699Skan rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1); 3163169699Skan emit_move_insn (target, force_operand (tem, NULL_RTX)); 3164169699Skan } 3165169699Skan 3166169699Skan return target; 316790075Sobrien} 316890075Sobrien 3169169699Skan/* Expand expression EXP, which is a call to the strcpy builtin. Return 0 3170169699Skan if we failed the caller should emit a normal call, otherwise try to get 3171169699Skan the result in TARGET, if convenient (and in mode MODE if that's 3172169699Skan convenient). */ 3173169699Skan 3174169699Skanstatic rtx 3175169699Skanexpand_builtin_strcpy (tree fndecl, tree arglist, rtx target, enum machine_mode mode) 3176169699Skan{ 3177169699Skan if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 3178169699Skan { 3179169699Skan tree result = fold_builtin_strcpy (fndecl, arglist, 0); 3180169699Skan if (result) 3181169699Skan { 3182169699Skan while (TREE_CODE (result) == COMPOUND_EXPR) 3183169699Skan { 3184169699Skan expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode, 3185169699Skan EXPAND_NORMAL); 3186169699Skan result = TREE_OPERAND (result, 1); 3187169699Skan } 3188169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 3189169699Skan } 3190169699Skan 3191169699Skan return expand_movstr (TREE_VALUE (arglist), 3192169699Skan TREE_VALUE (TREE_CHAIN (arglist)), 3193169699Skan target, /*endp=*/0); 3194169699Skan } 3195169699Skan return 0; 3196169699Skan} 3197169699Skan 3198132727Skan/* Expand a call to the stpcpy builtin, with arguments in ARGLIST. 3199132727Skan Return 0 if we failed the caller should emit a normal call, 3200132727Skan otherwise try to get the result in TARGET, if convenient (and in 3201132727Skan mode MODE if that's convenient). */ 3202132727Skan 3203132727Skanstatic rtx 3204169699Skanexpand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode) 3205132727Skan{ 3206169699Skan tree arglist = TREE_OPERAND (exp, 1); 3207169699Skan /* If return value is ignored, transform stpcpy into strcpy. */ 3208169699Skan if (target == const0_rtx) 3209169699Skan { 3210169699Skan tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; 3211169699Skan if (!fn) 3212169699Skan return 0; 3213169699Skan 3214169699Skan return expand_expr (build_function_call_expr (fn, arglist), 3215169699Skan target, mode, EXPAND_NORMAL); 3216169699Skan } 3217169699Skan 3218132727Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 3219132727Skan return 0; 3220132727Skan else 3221132727Skan { 3222169699Skan tree dst, src, len, lenp1; 3223169699Skan tree narglist; 3224169699Skan rtx ret; 3225132727Skan 3226132727Skan /* Ensure we get an actual string whose length can be evaluated at 3227169699Skan compile-time, not an expression containing a string. This is 3228169699Skan because the latter will potentially produce pessimized code 3229169699Skan when used to produce the return value. */ 3230132727Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 3231132727Skan if (! c_getstr (src) || ! (len = c_strlen (src, 0))) 3232169699Skan return expand_movstr (TREE_VALUE (arglist), 3233169699Skan TREE_VALUE (TREE_CHAIN (arglist)), 3234169699Skan target, /*endp=*/2); 3235132727Skan 3236132727Skan dst = TREE_VALUE (arglist); 3237169699Skan lenp1 = size_binop (PLUS_EXPR, len, ssize_int (1)); 3238169699Skan narglist = build_tree_list (NULL_TREE, lenp1); 3239169699Skan narglist = tree_cons (NULL_TREE, src, narglist); 3240169699Skan narglist = tree_cons (NULL_TREE, dst, narglist); 3241169699Skan ret = expand_builtin_mempcpy (narglist, TREE_TYPE (exp), 3242169699Skan target, mode, /*endp=*/2); 3243169699Skan 3244169699Skan if (ret) 3245169699Skan return ret; 3246169699Skan 3247169699Skan if (TREE_CODE (len) == INTEGER_CST) 3248169699Skan { 3249169699Skan rtx len_rtx = expand_normal (len); 3250169699Skan 3251169699Skan if (GET_CODE (len_rtx) == CONST_INT) 3252169699Skan { 3253169699Skan ret = expand_builtin_strcpy (get_callee_fndecl (exp), 3254169699Skan arglist, target, mode); 3255169699Skan 3256169699Skan if (ret) 3257169699Skan { 3258169699Skan if (! target) 3259169699Skan { 3260169699Skan if (mode != VOIDmode) 3261169699Skan target = gen_reg_rtx (mode); 3262169699Skan else 3263169699Skan target = gen_reg_rtx (GET_MODE (ret)); 3264169699Skan } 3265169699Skan if (GET_MODE (target) != GET_MODE (ret)) 3266169699Skan ret = gen_lowpart (GET_MODE (target), ret); 3267169699Skan 3268169699Skan ret = plus_constant (ret, INTVAL (len_rtx)); 3269169699Skan ret = emit_move_insn (target, force_operand (ret, NULL_RTX)); 3270169699Skan gcc_assert (ret); 3271169699Skan 3272169699Skan return target; 3273169699Skan } 3274169699Skan } 3275169699Skan } 3276169699Skan 3277169699Skan return expand_movstr (TREE_VALUE (arglist), 3278169699Skan TREE_VALUE (TREE_CHAIN (arglist)), 3279169699Skan target, /*endp=*/2); 3280132727Skan } 3281132727Skan} 3282132727Skan 328390075Sobrien/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) 328490075Sobrien bytes from constant string DATA + OFFSET and return it as target 328590075Sobrien constant. */ 328690075Sobrien 328790075Sobrienstatic rtx 3288132727Skanbuiltin_strncpy_read_str (void *data, HOST_WIDE_INT offset, 3289132727Skan enum machine_mode mode) 329090075Sobrien{ 329190075Sobrien const char *str = (const char *) data; 329290075Sobrien 329390075Sobrien if ((unsigned HOST_WIDE_INT) offset > strlen (str)) 329490075Sobrien return const0_rtx; 329590075Sobrien 329690075Sobrien return c_readstr (str + offset, mode); 329790075Sobrien} 329890075Sobrien 329990075Sobrien/* Expand expression EXP, which is a call to the strncpy builtin. Return 0 330090075Sobrien if we failed the caller should emit a normal call. */ 330190075Sobrien 330290075Sobrienstatic rtx 3303169699Skanexpand_builtin_strncpy (tree exp, rtx target, enum machine_mode mode) 330490075Sobrien{ 3305169699Skan tree fndecl = get_callee_fndecl (exp); 3306169699Skan tree arglist = TREE_OPERAND (exp, 1); 3307169699Skan if (validate_arglist (arglist, 3308169699Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 330990075Sobrien { 3310132727Skan tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1); 331190075Sobrien tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 3312169699Skan tree result = fold_builtin_strncpy (fndecl, arglist, slen); 331390075Sobrien 3314169699Skan if (result) 3315117404Skan { 3316169699Skan while (TREE_CODE (result) == COMPOUND_EXPR) 3317169699Skan { 3318169699Skan expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode, 3319169699Skan EXPAND_NORMAL); 3320169699Skan result = TREE_OPERAND (result, 1); 3321169699Skan } 3322169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 332390075Sobrien } 332490075Sobrien 3325169699Skan /* We must be passed a constant len and src parameter. */ 3326169699Skan if (!host_integerp (len, 1) || !slen || !host_integerp (slen, 1)) 332790075Sobrien return 0; 332890075Sobrien 332990075Sobrien slen = size_binop (PLUS_EXPR, slen, ssize_int (1)); 333090075Sobrien 333190075Sobrien /* We're required to pad with trailing zeros if the requested 3332169699Skan len is greater than strlen(s2)+1. In that case try to 333390075Sobrien use store_by_pieces, if it fails, punt. */ 333490075Sobrien if (tree_int_cst_lt (slen, len)) 333590075Sobrien { 333690075Sobrien tree dest = TREE_VALUE (arglist); 333790075Sobrien unsigned int dest_align 333890075Sobrien = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); 333990075Sobrien const char *p = c_getstr (TREE_VALUE (TREE_CHAIN (arglist))); 334090075Sobrien rtx dest_mem; 334190075Sobrien 334290075Sobrien if (!p || dest_align == 0 || !host_integerp (len, 1) 334390075Sobrien || !can_store_by_pieces (tree_low_cst (len, 1), 334490075Sobrien builtin_strncpy_read_str, 3345132727Skan (void *) p, dest_align)) 334690075Sobrien return 0; 334790075Sobrien 3348169699Skan dest_mem = get_memory_rtx (dest, len); 334990075Sobrien store_by_pieces (dest_mem, tree_low_cst (len, 1), 335090075Sobrien builtin_strncpy_read_str, 3351132727Skan (void *) p, dest_align, 0); 3352117404Skan dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); 3353132727Skan dest_mem = convert_memory_address (ptr_mode, dest_mem); 3354117404Skan return dest_mem; 335590075Sobrien } 335690075Sobrien } 3357169699Skan return 0; 335890075Sobrien} 335990075Sobrien 336090075Sobrien/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) 336190075Sobrien bytes from constant string DATA + OFFSET and return it as target 336290075Sobrien constant. */ 336390075Sobrien 336490075Sobrienstatic rtx 3365132727Skanbuiltin_memset_read_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, 3366132727Skan enum machine_mode mode) 336790075Sobrien{ 336890075Sobrien const char *c = (const char *) data; 336990075Sobrien char *p = alloca (GET_MODE_SIZE (mode)); 337090075Sobrien 337190075Sobrien memset (p, *c, GET_MODE_SIZE (mode)); 337290075Sobrien 337390075Sobrien return c_readstr (p, mode); 337490075Sobrien} 337590075Sobrien 3376117404Skan/* Callback routine for store_by_pieces. Return the RTL of a register 3377117404Skan containing GET_MODE_SIZE (MODE) consecutive copies of the unsigned 3378117404Skan char value given in the RTL register data. For example, if mode is 3379117404Skan 4 bytes wide, return the RTL for 0x01010101*data. */ 3380117404Skan 3381117404Skanstatic rtx 3382132727Skanbuiltin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, 3383132727Skan enum machine_mode mode) 3384117404Skan{ 3385117404Skan rtx target, coeff; 3386117404Skan size_t size; 3387117404Skan char *p; 3388117404Skan 3389117404Skan size = GET_MODE_SIZE (mode); 3390117404Skan if (size == 1) 3391117404Skan return (rtx) data; 3392117404Skan 3393117404Skan p = alloca (size); 3394117404Skan memset (p, 1, size); 3395117404Skan coeff = c_readstr (p, mode); 3396117404Skan 3397117404Skan target = convert_to_mode (mode, (rtx) data, 1); 3398117404Skan target = expand_mult (mode, target, coeff, NULL_RTX, 1); 3399117404Skan return force_reg (mode, target); 3400117404Skan} 3401117404Skan 340290075Sobrien/* Expand expression EXP, which is a call to the memset builtin. Return 0 340390075Sobrien if we failed the caller should emit a normal call, otherwise try to get 340490075Sobrien the result in TARGET, if convenient (and in mode MODE if that's 340590075Sobrien convenient). */ 340690075Sobrien 340790075Sobrienstatic rtx 3408169699Skanexpand_builtin_memset (tree arglist, rtx target, enum machine_mode mode, 3409169699Skan tree orig_exp) 341090075Sobrien{ 341190075Sobrien if (!validate_arglist (arglist, 341290075Sobrien POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) 341390075Sobrien return 0; 341490075Sobrien else 341590075Sobrien { 341690075Sobrien tree dest = TREE_VALUE (arglist); 341790075Sobrien tree val = TREE_VALUE (TREE_CHAIN (arglist)); 341890075Sobrien tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 3419169699Skan tree fndecl, fn; 3420169699Skan enum built_in_function fcode; 342190075Sobrien char c; 3422169699Skan unsigned int dest_align; 342390075Sobrien rtx dest_mem, dest_addr, len_rtx; 342490075Sobrien 3425169699Skan dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); 3426169699Skan 342790075Sobrien /* If DEST is not a pointer type, don't do this 342890075Sobrien operation in-line. */ 342990075Sobrien if (dest_align == 0) 343090075Sobrien return 0; 343190075Sobrien 343290075Sobrien /* If the LEN parameter is zero, return DEST. */ 3433132727Skan if (integer_zerop (len)) 3434117404Skan { 3435117404Skan /* Evaluate and ignore VAL in case it has side-effects. */ 3436117404Skan expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL); 3437117404Skan return expand_expr (dest, target, mode, EXPAND_NORMAL); 3438117404Skan } 343990075Sobrien 3440169699Skan /* Stabilize the arguments in case we fail. */ 3441169699Skan dest = builtin_save_expr (dest); 3442169699Skan val = builtin_save_expr (val); 3443169699Skan len = builtin_save_expr (len); 3444169699Skan 3445169699Skan len_rtx = expand_normal (len); 3446169699Skan dest_mem = get_memory_rtx (dest, len); 3447169699Skan 344890075Sobrien if (TREE_CODE (val) != INTEGER_CST) 3449117404Skan { 3450117404Skan rtx val_rtx; 345190075Sobrien 3452169699Skan val_rtx = expand_normal (val); 3453169699Skan val_rtx = convert_to_mode (TYPE_MODE (unsigned_char_type_node), 3454169699Skan val_rtx, 0); 3455117404Skan 3456117404Skan /* Assume that we can memset by pieces if we can store the 3457117404Skan * the coefficients by pieces (in the required modes). 3458117404Skan * We can't pass builtin_memset_gen_str as that emits RTL. */ 3459117404Skan c = 1; 3460169699Skan if (host_integerp (len, 1) 3461169699Skan && !(optimize_size && tree_low_cst (len, 1) > 1) 3462169699Skan && can_store_by_pieces (tree_low_cst (len, 1), 3463169699Skan builtin_memset_read_str, &c, dest_align)) 3464169699Skan { 3465169699Skan val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node), 3466169699Skan val_rtx); 3467169699Skan store_by_pieces (dest_mem, tree_low_cst (len, 1), 3468169699Skan builtin_memset_gen_str, val_rtx, dest_align, 0); 3469169699Skan } 3470169699Skan else if (!set_storage_via_setmem (dest_mem, len_rtx, val_rtx, 3471169699Skan dest_align)) 3472169699Skan goto do_libcall; 3473117404Skan 3474117404Skan dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); 3475132727Skan dest_mem = convert_memory_address (ptr_mode, dest_mem); 3476117404Skan return dest_mem; 3477117404Skan } 3478117404Skan 347990075Sobrien if (target_char_cast (val, &c)) 3480169699Skan goto do_libcall; 348190075Sobrien 348290075Sobrien if (c) 348390075Sobrien { 3484169699Skan if (host_integerp (len, 1) 3485169699Skan && !(optimize_size && tree_low_cst (len, 1) > 1) 3486169699Skan && can_store_by_pieces (tree_low_cst (len, 1), 3487169699Skan builtin_memset_read_str, &c, dest_align)) 3488169699Skan store_by_pieces (dest_mem, tree_low_cst (len, 1), 3489169699Skan builtin_memset_read_str, &c, dest_align, 0); 3490169699Skan else if (!set_storage_via_setmem (dest_mem, len_rtx, GEN_INT (c), 3491169699Skan dest_align)) 3492169699Skan goto do_libcall; 349390075Sobrien 3494117404Skan dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); 3495132727Skan dest_mem = convert_memory_address (ptr_mode, dest_mem); 3496117404Skan return dest_mem; 349790075Sobrien } 349890075Sobrien 349990075Sobrien set_mem_align (dest_mem, dest_align); 3500169699Skan dest_addr = clear_storage (dest_mem, len_rtx, 3501169699Skan CALL_EXPR_TAILCALL (orig_exp) 3502169699Skan ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL); 350390075Sobrien 350490075Sobrien if (dest_addr == 0) 3505117404Skan { 3506117404Skan dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX); 3507132727Skan dest_addr = convert_memory_address (ptr_mode, dest_addr); 3508117404Skan } 350990075Sobrien 351090075Sobrien return dest_addr; 3511169699Skan 3512169699Skan do_libcall: 3513169699Skan fndecl = get_callee_fndecl (orig_exp); 3514169699Skan fcode = DECL_FUNCTION_CODE (fndecl); 3515169699Skan gcc_assert (fcode == BUILT_IN_MEMSET || fcode == BUILT_IN_BZERO); 3516169699Skan arglist = build_tree_list (NULL_TREE, len); 3517169699Skan if (fcode == BUILT_IN_MEMSET) 3518169699Skan arglist = tree_cons (NULL_TREE, val, arglist); 3519169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 3520169699Skan fn = build_function_call_expr (fndecl, arglist); 3521169699Skan if (TREE_CODE (fn) == CALL_EXPR) 3522169699Skan CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp); 3523169699Skan return expand_call (fn, target, target == const0_rtx); 352490075Sobrien } 352590075Sobrien} 352690075Sobrien 352790075Sobrien/* Expand expression EXP, which is a call to the bzero builtin. Return 0 352890075Sobrien if we failed the caller should emit a normal call. */ 352990075Sobrien 353090075Sobrienstatic rtx 3531169699Skanexpand_builtin_bzero (tree exp) 353290075Sobrien{ 3533169699Skan tree arglist = TREE_OPERAND (exp, 1); 353490075Sobrien tree dest, size, newarglist; 353590075Sobrien 353690075Sobrien if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 353790075Sobrien return NULL_RTX; 353890075Sobrien 353990075Sobrien dest = TREE_VALUE (arglist); 354090075Sobrien size = TREE_VALUE (TREE_CHAIN (arglist)); 354190075Sobrien 354290075Sobrien /* New argument list transforming bzero(ptr x, int y) to 354390075Sobrien memset(ptr x, int 0, size_t y). This is done this way 354490075Sobrien so that if it isn't expanded inline, we fallback to 354590075Sobrien calling bzero instead of memset. */ 354690075Sobrien 3547169699Skan newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size)); 354890075Sobrien newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist); 354990075Sobrien newarglist = tree_cons (NULL_TREE, dest, newarglist); 355090075Sobrien 3551169699Skan return expand_builtin_memset (newarglist, const0_rtx, VOIDmode, exp); 355290075Sobrien} 355390075Sobrien 3554122190Skan/* Expand expression EXP, which is a call to the memcmp built-in function. 355590075Sobrien ARGLIST is the argument list for this call. Return 0 if we failed and the 355690075Sobrien caller should emit a normal call, otherwise try to get the result in 355790075Sobrien TARGET, if convenient (and in mode MODE, if that's convenient). */ 355890075Sobrien 355990075Sobrienstatic rtx 3560132727Skanexpand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, 3561132727Skan enum machine_mode mode) 356290075Sobrien{ 356390075Sobrien if (!validate_arglist (arglist, 3564117404Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 356590075Sobrien return 0; 3566169699Skan else 356790075Sobrien { 3568169699Skan tree result = fold_builtin_memcmp (arglist); 3569169699Skan if (result) 3570169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 357190075Sobrien } 357290075Sobrien 3573169699Skan#if defined HAVE_cmpmemsi || defined HAVE_cmpstrnsi 357490075Sobrien { 3575169699Skan tree arg1 = TREE_VALUE (arglist); 3576169699Skan tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); 3577169699Skan tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 357890075Sobrien rtx arg1_rtx, arg2_rtx, arg3_rtx; 357990075Sobrien rtx result; 358090075Sobrien rtx insn; 358190075Sobrien 358290075Sobrien int arg1_align 358390075Sobrien = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; 358490075Sobrien int arg2_align 358590075Sobrien = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; 3586132727Skan enum machine_mode insn_mode; 358790075Sobrien 3588132727Skan#ifdef HAVE_cmpmemsi 3589132727Skan if (HAVE_cmpmemsi) 3590132727Skan insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode; 3591132727Skan else 3592132727Skan#endif 3593169699Skan#ifdef HAVE_cmpstrnsi 3594169699Skan if (HAVE_cmpstrnsi) 3595169699Skan insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; 3596132727Skan else 3597132727Skan#endif 3598169699Skan return 0; 3599132727Skan 360090075Sobrien /* If we don't have POINTER_TYPE, call the function. */ 360190075Sobrien if (arg1_align == 0 || arg2_align == 0) 360290075Sobrien return 0; 360390075Sobrien 360490075Sobrien /* Make a place to write the result of the instruction. */ 360590075Sobrien result = target; 360690075Sobrien if (! (result != 0 3607169699Skan && REG_P (result) && GET_MODE (result) == insn_mode 360890075Sobrien && REGNO (result) >= FIRST_PSEUDO_REGISTER)) 360990075Sobrien result = gen_reg_rtx (insn_mode); 361090075Sobrien 3611169699Skan arg1_rtx = get_memory_rtx (arg1, len); 3612169699Skan arg2_rtx = get_memory_rtx (arg2, len); 3613169699Skan arg3_rtx = expand_normal (len); 3614169699Skan 3615169699Skan /* Set MEM_SIZE as appropriate. */ 3616169699Skan if (GET_CODE (arg3_rtx) == CONST_INT) 3617169699Skan { 3618169699Skan set_mem_size (arg1_rtx, arg3_rtx); 3619169699Skan set_mem_size (arg2_rtx, arg3_rtx); 3620169699Skan } 3621169699Skan 3622132727Skan#ifdef HAVE_cmpmemsi 3623132727Skan if (HAVE_cmpmemsi) 3624132727Skan insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx, 3625132727Skan GEN_INT (MIN (arg1_align, arg2_align))); 362690075Sobrien else 3627132727Skan#endif 3628169699Skan#ifdef HAVE_cmpstrnsi 3629169699Skan if (HAVE_cmpstrnsi) 3630169699Skan insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx, 3631169699Skan GEN_INT (MIN (arg1_align, arg2_align))); 3632132727Skan else 3633132727Skan#endif 3634169699Skan gcc_unreachable (); 363590075Sobrien 363690075Sobrien if (insn) 363790075Sobrien emit_insn (insn); 363890075Sobrien else 363990075Sobrien emit_library_call_value (memcmp_libfunc, result, LCT_PURE_MAKE_BLOCK, 364090075Sobrien TYPE_MODE (integer_type_node), 3, 364190075Sobrien XEXP (arg1_rtx, 0), Pmode, 364290075Sobrien XEXP (arg2_rtx, 0), Pmode, 364390075Sobrien convert_to_mode (TYPE_MODE (sizetype), arg3_rtx, 3644169699Skan TYPE_UNSIGNED (sizetype)), 364590075Sobrien TYPE_MODE (sizetype)); 364690075Sobrien 364790075Sobrien /* Return the value in the proper mode for this function. */ 364890075Sobrien mode = TYPE_MODE (TREE_TYPE (exp)); 364990075Sobrien if (GET_MODE (result) == mode) 365090075Sobrien return result; 365190075Sobrien else if (target != 0) 365290075Sobrien { 365390075Sobrien convert_move (target, result, 0); 365490075Sobrien return target; 365590075Sobrien } 365690075Sobrien else 365790075Sobrien return convert_to_mode (mode, result, 0); 365890075Sobrien } 365990075Sobrien#endif 366090075Sobrien 366190075Sobrien return 0; 366290075Sobrien} 366390075Sobrien 366490075Sobrien/* Expand expression EXP, which is a call to the strcmp builtin. Return 0 366590075Sobrien if we failed the caller should emit a normal call, otherwise try to get 366690075Sobrien the result in TARGET, if convenient. */ 366790075Sobrien 366890075Sobrienstatic rtx 3669132727Skanexpand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode) 367090075Sobrien{ 367190075Sobrien tree arglist = TREE_OPERAND (exp, 1); 367290075Sobrien 367390075Sobrien if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 367490075Sobrien return 0; 3675169699Skan else 3676169699Skan { 3677169699Skan tree result = fold_builtin_strcmp (arglist); 3678169699Skan if (result) 3679169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 3680169699Skan } 368190075Sobrien 3682169699Skan#if defined HAVE_cmpstrsi || defined HAVE_cmpstrnsi 3683169699Skan if (cmpstr_optab[SImode] != CODE_FOR_nothing 3684169699Skan || cmpstrn_optab[SImode] != CODE_FOR_nothing) 3685169699Skan { 3686169699Skan rtx arg1_rtx, arg2_rtx; 3687169699Skan rtx result, insn = NULL_RTX; 3688169699Skan tree fndecl, fn; 368990075Sobrien 3690169699Skan tree arg1 = TREE_VALUE (arglist); 3691169699Skan tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); 3692169699Skan int arg1_align 3693169699Skan = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; 3694169699Skan int arg2_align 3695169699Skan = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; 3696132727Skan 3697169699Skan /* If we don't have POINTER_TYPE, call the function. */ 3698169699Skan if (arg1_align == 0 || arg2_align == 0) 3699169699Skan return 0; 370090075Sobrien 3701169699Skan /* Stabilize the arguments in case gen_cmpstr(n)si fail. */ 3702169699Skan arg1 = builtin_save_expr (arg1); 3703169699Skan arg2 = builtin_save_expr (arg2); 370490075Sobrien 3705169699Skan arg1_rtx = get_memory_rtx (arg1, NULL); 3706169699Skan arg2_rtx = get_memory_rtx (arg2, NULL); 370790075Sobrien 3708122190Skan#ifdef HAVE_cmpstrsi 3709169699Skan /* Try to call cmpstrsi. */ 3710169699Skan if (HAVE_cmpstrsi) 3711169699Skan { 3712169699Skan enum machine_mode insn_mode 3713169699Skan = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; 371490075Sobrien 3715169699Skan /* Make a place to write the result of the instruction. */ 3716169699Skan result = target; 3717169699Skan if (! (result != 0 3718169699Skan && REG_P (result) && GET_MODE (result) == insn_mode 3719169699Skan && REGNO (result) >= FIRST_PSEUDO_REGISTER)) 3720169699Skan result = gen_reg_rtx (insn_mode); 372190075Sobrien 3722169699Skan insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, 3723169699Skan GEN_INT (MIN (arg1_align, arg2_align))); 3724169699Skan } 3725169699Skan#endif 3726169699Skan#ifdef HAVE_cmpstrnsi 3727169699Skan /* Try to determine at least one length and call cmpstrnsi. */ 3728169699Skan if (!insn && HAVE_cmpstrnsi) 3729169699Skan { 3730169699Skan tree len; 3731169699Skan rtx arg3_rtx; 373290075Sobrien 3733169699Skan enum machine_mode insn_mode 3734169699Skan = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; 3735169699Skan tree len1 = c_strlen (arg1, 1); 3736169699Skan tree len2 = c_strlen (arg2, 1); 373790075Sobrien 3738169699Skan if (len1) 3739169699Skan len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); 3740169699Skan if (len2) 3741169699Skan len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); 374290075Sobrien 3743169699Skan /* If we don't have a constant length for the first, use the length 3744169699Skan of the second, if we know it. We don't require a constant for 3745169699Skan this case; some cost analysis could be done if both are available 3746169699Skan but neither is constant. For now, assume they're equally cheap, 3747169699Skan unless one has side effects. If both strings have constant lengths, 3748169699Skan use the smaller. */ 3749122190Skan 3750169699Skan if (!len1) 3751169699Skan len = len2; 3752169699Skan else if (!len2) 3753169699Skan len = len1; 3754169699Skan else if (TREE_SIDE_EFFECTS (len1)) 3755169699Skan len = len2; 3756169699Skan else if (TREE_SIDE_EFFECTS (len2)) 3757169699Skan len = len1; 3758169699Skan else if (TREE_CODE (len1) != INTEGER_CST) 3759169699Skan len = len2; 3760169699Skan else if (TREE_CODE (len2) != INTEGER_CST) 3761169699Skan len = len1; 3762169699Skan else if (tree_int_cst_lt (len1, len2)) 3763169699Skan len = len1; 3764169699Skan else 3765169699Skan len = len2; 376690075Sobrien 3767169699Skan /* If both arguments have side effects, we cannot optimize. */ 3768169699Skan if (!len || TREE_SIDE_EFFECTS (len)) 3769169699Skan goto do_libcall; 377090075Sobrien 3771169699Skan arg3_rtx = expand_normal (len); 377290075Sobrien 3773169699Skan /* Make a place to write the result of the instruction. */ 3774169699Skan result = target; 3775169699Skan if (! (result != 0 3776169699Skan && REG_P (result) && GET_MODE (result) == insn_mode 3777169699Skan && REGNO (result) >= FIRST_PSEUDO_REGISTER)) 3778169699Skan result = gen_reg_rtx (insn_mode); 3779122190Skan 3780169699Skan insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx, 3781169699Skan GEN_INT (MIN (arg1_align, arg2_align))); 3782169699Skan } 3783169699Skan#endif 3784122190Skan 3785169699Skan if (insn) 3786169699Skan { 3787169699Skan emit_insn (insn); 3788132727Skan 3789169699Skan /* Return the value in the proper mode for this function. */ 3790169699Skan mode = TYPE_MODE (TREE_TYPE (exp)); 3791169699Skan if (GET_MODE (result) == mode) 3792169699Skan return result; 3793169699Skan if (target == 0) 3794169699Skan return convert_to_mode (mode, result, 0); 3795169699Skan convert_move (target, result, 0); 3796169699Skan return target; 3797169699Skan } 3798169699Skan 3799169699Skan /* Expand the library call ourselves using a stabilized argument 3800169699Skan list to avoid re-evaluating the function's arguments twice. */ 3801169699Skan#ifdef HAVE_cmpstrnsi 3802169699Skan do_libcall: 3803122190Skan#endif 3804169699Skan arglist = build_tree_list (NULL_TREE, arg2); 3805169699Skan arglist = tree_cons (NULL_TREE, arg1, arglist); 3806169699Skan fndecl = get_callee_fndecl (exp); 3807169699Skan fn = build_function_call_expr (fndecl, arglist); 3808169699Skan if (TREE_CODE (fn) == CALL_EXPR) 3809169699Skan CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); 3810169699Skan return expand_call (fn, target, target == const0_rtx); 3811169699Skan } 3812169699Skan#endif 3813122190Skan return 0; 381490075Sobrien} 381590075Sobrien 381690075Sobrien/* Expand expression EXP, which is a call to the strncmp builtin. Return 0 381790075Sobrien if we failed the caller should emit a normal call, otherwise try to get 381890075Sobrien the result in TARGET, if convenient. */ 381990075Sobrien 382090075Sobrienstatic rtx 3821132727Skanexpand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) 382290075Sobrien{ 382390075Sobrien tree arglist = TREE_OPERAND (exp, 1); 382490075Sobrien 382590075Sobrien if (!validate_arglist (arglist, 382690075Sobrien POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 382790075Sobrien return 0; 3828169699Skan else 3829117404Skan { 3830169699Skan tree result = fold_builtin_strncmp (arglist); 3831169699Skan if (result) 3832169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 3833117404Skan } 383490075Sobrien 383590075Sobrien /* If c_strlen can determine an expression for one of the string 3836169699Skan lengths, and it doesn't have side effects, then emit cmpstrnsi 3837122190Skan using length MIN(strlen(string)+1, arg3). */ 3838169699Skan#ifdef HAVE_cmpstrnsi 3839169699Skan if (HAVE_cmpstrnsi) 3840132727Skan { 3841169699Skan tree arg1 = TREE_VALUE (arglist); 3842169699Skan tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); 3843169699Skan tree arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 3844132727Skan tree len, len1, len2; 3845132727Skan rtx arg1_rtx, arg2_rtx, arg3_rtx; 3846132727Skan rtx result, insn; 3847169699Skan tree fndecl, fn; 384890075Sobrien 3849132727Skan int arg1_align 3850132727Skan = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; 3851132727Skan int arg2_align 3852132727Skan = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; 3853132727Skan enum machine_mode insn_mode 3854169699Skan = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; 385590075Sobrien 3856132727Skan len1 = c_strlen (arg1, 1); 3857132727Skan len2 = c_strlen (arg2, 1); 385890075Sobrien 3859132727Skan if (len1) 3860132727Skan len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); 3861132727Skan if (len2) 3862132727Skan len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); 386390075Sobrien 3864132727Skan /* If we don't have a constant length for the first, use the length 3865132727Skan of the second, if we know it. We don't require a constant for 3866132727Skan this case; some cost analysis could be done if both are available 3867132727Skan but neither is constant. For now, assume they're equally cheap, 3868132727Skan unless one has side effects. If both strings have constant lengths, 3869132727Skan use the smaller. */ 387090075Sobrien 3871132727Skan if (!len1) 3872132727Skan len = len2; 3873132727Skan else if (!len2) 3874132727Skan len = len1; 3875132727Skan else if (TREE_SIDE_EFFECTS (len1)) 3876132727Skan len = len2; 3877132727Skan else if (TREE_SIDE_EFFECTS (len2)) 3878132727Skan len = len1; 3879132727Skan else if (TREE_CODE (len1) != INTEGER_CST) 3880132727Skan len = len2; 3881132727Skan else if (TREE_CODE (len2) != INTEGER_CST) 3882132727Skan len = len1; 3883132727Skan else if (tree_int_cst_lt (len1, len2)) 3884132727Skan len = len1; 3885132727Skan else 3886132727Skan len = len2; 388790075Sobrien 3888132727Skan /* If both arguments have side effects, we cannot optimize. */ 3889132727Skan if (!len || TREE_SIDE_EFFECTS (len)) 3890132727Skan return 0; 3891122190Skan 3892132727Skan /* The actual new length parameter is MIN(len,arg3). */ 3893169699Skan len = fold_build2 (MIN_EXPR, TREE_TYPE (len), len, 3894169699Skan fold_convert (TREE_TYPE (len), arg3)); 3895122190Skan 3896132727Skan /* If we don't have POINTER_TYPE, call the function. */ 3897132727Skan if (arg1_align == 0 || arg2_align == 0) 3898132727Skan return 0; 3899122190Skan 3900132727Skan /* Make a place to write the result of the instruction. */ 3901132727Skan result = target; 3902132727Skan if (! (result != 0 3903169699Skan && REG_P (result) && GET_MODE (result) == insn_mode 3904132727Skan && REGNO (result) >= FIRST_PSEUDO_REGISTER)) 3905132727Skan result = gen_reg_rtx (insn_mode); 3906122190Skan 3907169699Skan /* Stabilize the arguments in case gen_cmpstrnsi fails. */ 3908169699Skan arg1 = builtin_save_expr (arg1); 3909169699Skan arg2 = builtin_save_expr (arg2); 3910169699Skan len = builtin_save_expr (len); 3911122190Skan 3912169699Skan arg1_rtx = get_memory_rtx (arg1, len); 3913169699Skan arg2_rtx = get_memory_rtx (arg2, len); 3914169699Skan arg3_rtx = expand_normal (len); 3915169699Skan insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx, 3916169699Skan GEN_INT (MIN (arg1_align, arg2_align))); 3917132727Skan if (insn) 3918132727Skan { 3919132727Skan emit_insn (insn); 3920122190Skan 3921132727Skan /* Return the value in the proper mode for this function. */ 3922132727Skan mode = TYPE_MODE (TREE_TYPE (exp)); 3923132727Skan if (GET_MODE (result) == mode) 3924132727Skan return result; 3925132727Skan if (target == 0) 3926132727Skan return convert_to_mode (mode, result, 0); 3927132727Skan convert_move (target, result, 0); 3928132727Skan return target; 3929132727Skan } 3930132727Skan 3931132727Skan /* Expand the library call ourselves using a stabilized argument 3932132727Skan list to avoid re-evaluating the function's arguments twice. */ 3933132727Skan arglist = build_tree_list (NULL_TREE, len); 3934132727Skan arglist = tree_cons (NULL_TREE, arg2, arglist); 3935132727Skan arglist = tree_cons (NULL_TREE, arg1, arglist); 3936132727Skan fndecl = get_callee_fndecl (exp); 3937169699Skan fn = build_function_call_expr (fndecl, arglist); 3938169699Skan if (TREE_CODE (fn) == CALL_EXPR) 3939169699Skan CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); 3940169699Skan return expand_call (fn, target, target == const0_rtx); 3941132727Skan } 3942122190Skan#endif 3943122190Skan return 0; 394490075Sobrien} 394590075Sobrien 394690075Sobrien/* Expand expression EXP, which is a call to the strcat builtin. 394790075Sobrien Return 0 if we failed the caller should emit a normal call, 394890075Sobrien otherwise try to get the result in TARGET, if convenient. */ 394990075Sobrien 395090075Sobrienstatic rtx 3951169699Skanexpand_builtin_strcat (tree fndecl, tree arglist, rtx target, enum machine_mode mode) 395290075Sobrien{ 395390075Sobrien if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 395490075Sobrien return 0; 395590075Sobrien else 395690075Sobrien { 395790075Sobrien tree dst = TREE_VALUE (arglist), 3958169699Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 395990075Sobrien const char *p = c_getstr (src); 396090075Sobrien 3961169699Skan /* If the string length is zero, return the dst parameter. */ 3962169699Skan if (p && *p == '\0') 3963169699Skan return expand_expr (dst, target, mode, EXPAND_NORMAL); 3964169699Skan 3965169699Skan if (!optimize_size) 3966132727Skan { 3967169699Skan /* See if we can store by pieces into (dst + strlen(dst)). */ 3968169699Skan tree newsrc, newdst, 3969169699Skan strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN]; 3970169699Skan rtx insns; 3971169699Skan 3972169699Skan /* Stabilize the argument list. */ 3973169699Skan newsrc = builtin_save_expr (src); 3974169699Skan if (newsrc != src) 3975169699Skan arglist = build_tree_list (NULL_TREE, newsrc); 3976169699Skan else 3977169699Skan arglist = TREE_CHAIN (arglist); /* Reusing arglist if safe. */ 3978169699Skan 3979169699Skan dst = builtin_save_expr (dst); 3980169699Skan 3981169699Skan start_sequence (); 3982169699Skan 3983169699Skan /* Create strlen (dst). */ 3984169699Skan newdst = 3985169699Skan build_function_call_expr (strlen_fn, 3986169699Skan build_tree_list (NULL_TREE, dst)); 3987169699Skan /* Create (dst + (cast) strlen (dst)). */ 3988169699Skan newdst = fold_convert (TREE_TYPE (dst), newdst); 3989169699Skan newdst = fold_build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst); 3990169699Skan 3991169699Skan newdst = builtin_save_expr (newdst); 3992169699Skan arglist = tree_cons (NULL_TREE, newdst, arglist); 3993169699Skan 3994169699Skan if (!expand_builtin_strcpy (fndecl, arglist, target, mode)) 3995132727Skan { 3996169699Skan end_sequence (); /* Stop sequence. */ 3997169699Skan return 0; 3998169699Skan } 399990075Sobrien 4000169699Skan /* Output the entire sequence. */ 4001169699Skan insns = get_insns (); 4002169699Skan end_sequence (); 4003169699Skan emit_insn (insns); 4004132727Skan 4005169699Skan return expand_expr (dst, target, mode, EXPAND_NORMAL); 4006132727Skan } 4007132727Skan 400890075Sobrien return 0; 400990075Sobrien } 401090075Sobrien} 401190075Sobrien 401290075Sobrien/* Expand expression EXP, which is a call to the strncat builtin. 401390075Sobrien Return 0 if we failed the caller should emit a normal call, 401490075Sobrien otherwise try to get the result in TARGET, if convenient. */ 401590075Sobrien 401690075Sobrienstatic rtx 4017132727Skanexpand_builtin_strncat (tree arglist, rtx target, enum machine_mode mode) 401890075Sobrien{ 4019169699Skan if (validate_arglist (arglist, 4020169699Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 402190075Sobrien { 4022169699Skan tree result = fold_builtin_strncat (arglist); 4023169699Skan if (result) 4024169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 402590075Sobrien } 4026169699Skan return 0; 402790075Sobrien} 402890075Sobrien 402990075Sobrien/* Expand expression EXP, which is a call to the strspn builtin. 403090075Sobrien Return 0 if we failed the caller should emit a normal call, 403190075Sobrien otherwise try to get the result in TARGET, if convenient. */ 403290075Sobrien 403390075Sobrienstatic rtx 4034132727Skanexpand_builtin_strspn (tree arglist, rtx target, enum machine_mode mode) 403590075Sobrien{ 4036169699Skan if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 403790075Sobrien { 4038169699Skan tree result = fold_builtin_strspn (arglist); 4039169699Skan if (result) 4040169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 404190075Sobrien } 4042169699Skan return 0; 404390075Sobrien} 404490075Sobrien 404590075Sobrien/* Expand expression EXP, which is a call to the strcspn builtin. 404690075Sobrien Return 0 if we failed the caller should emit a normal call, 404790075Sobrien otherwise try to get the result in TARGET, if convenient. */ 404890075Sobrien 404990075Sobrienstatic rtx 4050132727Skanexpand_builtin_strcspn (tree arglist, rtx target, enum machine_mode mode) 405190075Sobrien{ 4052169699Skan if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 405390075Sobrien { 4054169699Skan tree result = fold_builtin_strcspn (arglist); 4055169699Skan if (result) 4056169699Skan return expand_expr (result, target, mode, EXPAND_NORMAL); 405790075Sobrien } 4058169699Skan return 0; 405990075Sobrien} 406090075Sobrien 406190075Sobrien/* Expand a call to __builtin_saveregs, generating the result in TARGET, 406290075Sobrien if that's convenient. */ 406390075Sobrien 406490075Sobrienrtx 4065132727Skanexpand_builtin_saveregs (void) 406690075Sobrien{ 406790075Sobrien rtx val, seq; 406890075Sobrien 406990075Sobrien /* Don't do __builtin_saveregs more than once in a function. 407090075Sobrien Save the result of the first call and reuse it. */ 407190075Sobrien if (saveregs_value != 0) 407290075Sobrien return saveregs_value; 407390075Sobrien 407490075Sobrien /* When this function is called, it means that registers must be 407590075Sobrien saved on entry to this function. So we migrate the call to the 407690075Sobrien first insn of this function. */ 407790075Sobrien 407890075Sobrien start_sequence (); 407990075Sobrien 408090075Sobrien /* Do whatever the machine needs done in this case. */ 4081132727Skan val = targetm.calls.expand_builtin_saveregs (); 408290075Sobrien 408390075Sobrien seq = get_insns (); 408490075Sobrien end_sequence (); 408590075Sobrien 408690075Sobrien saveregs_value = val; 408790075Sobrien 4088117404Skan /* Put the insns after the NOTE that starts the function. If this 4089117404Skan is inside a start_sequence, make the outer-level insn chain current, so 409090075Sobrien the code is placed at the start of the function. */ 409190075Sobrien push_topmost_sequence (); 4092169699Skan emit_insn_after (seq, entry_of_function ()); 409390075Sobrien pop_topmost_sequence (); 409490075Sobrien 409590075Sobrien return val; 409690075Sobrien} 409790075Sobrien 409890075Sobrien/* __builtin_args_info (N) returns word N of the arg space info 409990075Sobrien for the current function. The number and meanings of words 410090075Sobrien is controlled by the definition of CUMULATIVE_ARGS. */ 410190075Sobrien 410290075Sobrienstatic rtx 4103132727Skanexpand_builtin_args_info (tree arglist) 410490075Sobrien{ 410590075Sobrien int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int); 410690075Sobrien int *word_ptr = (int *) ¤t_function_args_info; 410790075Sobrien 4108169699Skan gcc_assert (sizeof (CUMULATIVE_ARGS) % sizeof (int) == 0); 410990075Sobrien 411090075Sobrien if (arglist != 0) 411190075Sobrien { 411290075Sobrien if (!host_integerp (TREE_VALUE (arglist), 0)) 4113169699Skan error ("argument of %<__builtin_args_info%> must be constant"); 411490075Sobrien else 411590075Sobrien { 411690075Sobrien HOST_WIDE_INT wordnum = tree_low_cst (TREE_VALUE (arglist), 0); 411790075Sobrien 411890075Sobrien if (wordnum < 0 || wordnum >= nwords) 4119169699Skan error ("argument of %<__builtin_args_info%> out of range"); 412090075Sobrien else 412190075Sobrien return GEN_INT (word_ptr[wordnum]); 412290075Sobrien } 412390075Sobrien } 412490075Sobrien else 4125169699Skan error ("missing argument in %<__builtin_args_info%>"); 412690075Sobrien 412790075Sobrien return const0_rtx; 412890075Sobrien} 412990075Sobrien 4130169699Skan/* Expand a call to __builtin_next_arg. */ 413190075Sobrien 413290075Sobrienstatic rtx 4133169699Skanexpand_builtin_next_arg (void) 413490075Sobrien{ 4135169699Skan /* Checking arguments is already done in fold_builtin_next_arg 4136169699Skan that must be called before this function. */ 413790075Sobrien return expand_binop (Pmode, add_optab, 413890075Sobrien current_function_internal_arg_pointer, 413990075Sobrien current_function_arg_offset_rtx, 414090075Sobrien NULL_RTX, 0, OPTAB_LIB_WIDEN); 414190075Sobrien} 414290075Sobrien 414390075Sobrien/* Make it easier for the backends by protecting the valist argument 414490075Sobrien from multiple evaluations. */ 414590075Sobrien 414690075Sobrienstatic tree 4147132727Skanstabilize_va_list (tree valist, int needs_lvalue) 414890075Sobrien{ 414990075Sobrien if (TREE_CODE (va_list_type_node) == ARRAY_TYPE) 415090075Sobrien { 415190075Sobrien if (TREE_SIDE_EFFECTS (valist)) 415290075Sobrien valist = save_expr (valist); 415390075Sobrien 415490075Sobrien /* For this case, the backends will be expecting a pointer to 415590075Sobrien TREE_TYPE (va_list_type_node), but it's possible we've 415690075Sobrien actually been given an array (an actual va_list_type_node). 415790075Sobrien So fix it. */ 415890075Sobrien if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE) 415990075Sobrien { 416090075Sobrien tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node)); 4161169699Skan valist = build_fold_addr_expr_with_type (valist, p1); 416290075Sobrien } 416390075Sobrien } 416490075Sobrien else 416590075Sobrien { 416690075Sobrien tree pt; 416790075Sobrien 416890075Sobrien if (! needs_lvalue) 416990075Sobrien { 417090075Sobrien if (! TREE_SIDE_EFFECTS (valist)) 417190075Sobrien return valist; 417290075Sobrien 417390075Sobrien pt = build_pointer_type (va_list_type_node); 4174169699Skan valist = fold_build1 (ADDR_EXPR, pt, valist); 417590075Sobrien TREE_SIDE_EFFECTS (valist) = 1; 417690075Sobrien } 417790075Sobrien 417890075Sobrien if (TREE_SIDE_EFFECTS (valist)) 417990075Sobrien valist = save_expr (valist); 4180169699Skan valist = build_fold_indirect_ref (valist); 418190075Sobrien } 418290075Sobrien 418390075Sobrien return valist; 418490075Sobrien} 418590075Sobrien 4186132727Skan/* The "standard" definition of va_list is void*. */ 4187132727Skan 4188132727Skantree 4189132727Skanstd_build_builtin_va_list (void) 4190132727Skan{ 4191132727Skan return ptr_type_node; 4192132727Skan} 4193132727Skan 419490075Sobrien/* The "standard" implementation of va_start: just assign `nextarg' to 419590075Sobrien the variable. */ 419690075Sobrien 419790075Sobrienvoid 4198132727Skanstd_expand_builtin_va_start (tree valist, rtx nextarg) 419990075Sobrien{ 420090075Sobrien tree t; 420190075Sobrien 4202169699Skan t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, 4203169699Skan make_tree (ptr_type_node, nextarg)); 420490075Sobrien TREE_SIDE_EFFECTS (t) = 1; 420590075Sobrien 420690075Sobrien expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); 420790075Sobrien} 420890075Sobrien 4209117404Skan/* Expand ARGLIST, from a call to __builtin_va_start. */ 421090075Sobrien 421190075Sobrienstatic rtx 4212132727Skanexpand_builtin_va_start (tree arglist) 421390075Sobrien{ 421490075Sobrien rtx nextarg; 4215117404Skan tree chain, valist; 421690075Sobrien 4217117404Skan chain = TREE_CHAIN (arglist); 421890075Sobrien 4219169699Skan if (!chain) 4220169699Skan { 4221169699Skan error ("too few arguments to function %<va_start%>"); 4222169699Skan return const0_rtx; 4223169699Skan } 422490075Sobrien 4225169699Skan if (fold_builtin_next_arg (chain)) 4226169699Skan return const0_rtx; 4227169699Skan 4228169699Skan nextarg = expand_builtin_next_arg (); 422990075Sobrien valist = stabilize_va_list (TREE_VALUE (arglist), 1); 423090075Sobrien 423190075Sobrien#ifdef EXPAND_BUILTIN_VA_START 4232117404Skan EXPAND_BUILTIN_VA_START (valist, nextarg); 423390075Sobrien#else 4234117404Skan std_expand_builtin_va_start (valist, nextarg); 423590075Sobrien#endif 423690075Sobrien 423790075Sobrien return const0_rtx; 423890075Sobrien} 423990075Sobrien 424090075Sobrien/* The "standard" implementation of va_arg: read the value from the 424190075Sobrien current (padded) address and increment by the (padded) size. */ 424290075Sobrien 4243169699Skantree 4244169699Skanstd_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) 424590075Sobrien{ 4246169699Skan tree addr, t, type_size, rounded_size, valist_tmp; 4247169699Skan unsigned HOST_WIDE_INT align, boundary; 4248169699Skan bool indirect; 424990075Sobrien 4250169699Skan#ifdef ARGS_GROW_DOWNWARD 4251169699Skan /* All of the alignment and movement below is for args-grow-up machines. 4252169699Skan As of 2004, there are only 3 ARGS_GROW_DOWNWARD targets, and they all 4253169699Skan implement their own specialized gimplify_va_arg_expr routines. */ 4254169699Skan gcc_unreachable (); 4255169699Skan#endif 4256132727Skan 4257169699Skan indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false); 4258169699Skan if (indirect) 4259169699Skan type = build_pointer_type (type); 4260169699Skan 4261169699Skan align = PARM_BOUNDARY / BITS_PER_UNIT; 4262169699Skan boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type) / BITS_PER_UNIT; 4263169699Skan 4264169699Skan /* Hoist the valist value into a temporary for the moment. */ 4265169699Skan valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL); 4266169699Skan 4267132727Skan /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually 4268132727Skan requires greater alignment, we must perform dynamic alignment. */ 4269169699Skan if (boundary > align 4270169699Skan && !integer_zerop (TYPE_SIZE (type))) 4271169699Skan { 4272169699Skan t = fold_convert (TREE_TYPE (valist), size_int (boundary - 1)); 4273169699Skan t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, 4274169699Skan build2 (PLUS_EXPR, TREE_TYPE (valist), valist_tmp, t)); 4275169699Skan gimplify_and_add (t, pre_p); 4276132727Skan 4277169699Skan t = fold_convert (TREE_TYPE (valist), size_int (-boundary)); 4278169699Skan t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, 4279169699Skan build2 (BIT_AND_EXPR, TREE_TYPE (valist), valist_tmp, t)); 4280169699Skan gimplify_and_add (t, pre_p); 4281132727Skan } 428296263Sobrien else 4283169699Skan boundary = align; 428490075Sobrien 4285169699Skan /* If the actual alignment is less than the alignment of the type, 4286169699Skan adjust the type accordingly so that we don't assume strict alignment 4287169699Skan when deferencing the pointer. */ 4288169699Skan boundary *= BITS_PER_UNIT; 4289169699Skan if (boundary < TYPE_ALIGN (type)) 429090075Sobrien { 4291169699Skan type = build_variant_type_copy (type); 4292169699Skan TYPE_ALIGN (type) = boundary; 429390075Sobrien } 429490075Sobrien 4295169699Skan /* Compute the rounded size of the type. */ 4296169699Skan type_size = size_in_bytes (type); 4297169699Skan rounded_size = round_up (type_size, align); 429890075Sobrien 4299169699Skan /* Reduce rounded_size so it's sharable with the postqueue. */ 4300169699Skan gimplify_expr (&rounded_size, pre_p, post_p, is_gimple_val, fb_rvalue); 4301169699Skan 4302169699Skan /* Get AP. */ 4303169699Skan addr = valist_tmp; 4304169699Skan if (PAD_VARARGS_DOWN && !integer_zerop (rounded_size)) 430596263Sobrien { 4306169699Skan /* Small args are padded downward. */ 4307169699Skan t = fold_build2 (GT_EXPR, sizetype, rounded_size, size_int (align)); 4308169699Skan t = fold_build3 (COND_EXPR, sizetype, t, size_zero_node, 4309169699Skan size_binop (MINUS_EXPR, rounded_size, type_size)); 4310169699Skan t = fold_convert (TREE_TYPE (addr), t); 4311169699Skan addr = fold_build2 (PLUS_EXPR, TREE_TYPE (addr), addr, t); 431296263Sobrien } 431390075Sobrien 4314169699Skan /* Compute new value for AP. */ 4315169699Skan t = fold_convert (TREE_TYPE (valist), rounded_size); 4316169699Skan t = build2 (PLUS_EXPR, TREE_TYPE (valist), valist_tmp, t); 4317169699Skan t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, t); 4318169699Skan gimplify_and_add (t, pre_p); 4319169699Skan 4320169699Skan addr = fold_convert (build_pointer_type (type), addr); 4321169699Skan 4322169699Skan if (indirect) 4323169699Skan addr = build_va_arg_indirect_ref (addr); 4324169699Skan 4325169699Skan return build_va_arg_indirect_ref (addr); 4326169699Skan} 4327169699Skan 4328169699Skan/* Build an indirect-ref expression over the given TREE, which represents a 4329169699Skan piece of a va_arg() expansion. */ 4330169699Skantree 4331169699Skanbuild_va_arg_indirect_ref (tree addr) 4332169699Skan{ 4333169699Skan addr = build_fold_indirect_ref (addr); 4334169699Skan 4335169699Skan if (flag_mudflap) /* Don't instrument va_arg INDIRECT_REF. */ 4336169699Skan mf_mark (addr); 4337169699Skan 433890075Sobrien return addr; 433990075Sobrien} 434090075Sobrien 4341169699Skan/* Return a dummy expression of type TYPE in order to keep going after an 4342169699Skan error. */ 434390075Sobrien 4344169699Skanstatic tree 4345169699Skandummy_object (tree type) 434690075Sobrien{ 4347169699Skan tree t = build_int_cst (build_pointer_type (type), 0); 4348169699Skan return build1 (INDIRECT_REF, type, t); 4349169699Skan} 4350169699Skan 4351169699Skan/* Gimplify __builtin_va_arg, aka VA_ARG_EXPR, which is not really a 4352169699Skan builtin function, but a very special sort of operator. */ 4353169699Skan 4354169699Skanenum gimplify_status 4355169699Skangimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p) 4356169699Skan{ 435790075Sobrien tree promoted_type, want_va_type, have_va_type; 4358169699Skan tree valist = TREE_OPERAND (*expr_p, 0); 4359169699Skan tree type = TREE_TYPE (*expr_p); 4360169699Skan tree t; 436190075Sobrien 436290075Sobrien /* Verify that valist is of the proper type. */ 436390075Sobrien want_va_type = va_list_type_node; 436490075Sobrien have_va_type = TREE_TYPE (valist); 4365169699Skan 4366169699Skan if (have_va_type == error_mark_node) 4367169699Skan return GS_ERROR; 4368169699Skan 436990075Sobrien if (TREE_CODE (want_va_type) == ARRAY_TYPE) 437090075Sobrien { 437190075Sobrien /* If va_list is an array type, the argument may have decayed 437290075Sobrien to a pointer type, e.g. by being passed to another function. 4373169699Skan In that case, unwrap both types so that we can compare the 437490075Sobrien underlying records. */ 437590075Sobrien if (TREE_CODE (have_va_type) == ARRAY_TYPE 4376169699Skan || POINTER_TYPE_P (have_va_type)) 437790075Sobrien { 437890075Sobrien want_va_type = TREE_TYPE (want_va_type); 437990075Sobrien have_va_type = TREE_TYPE (have_va_type); 438090075Sobrien } 438190075Sobrien } 4382169699Skan 438390075Sobrien if (TYPE_MAIN_VARIANT (want_va_type) != TYPE_MAIN_VARIANT (have_va_type)) 438490075Sobrien { 4385169699Skan error ("first argument to %<va_arg%> not of type %<va_list%>"); 4386169699Skan return GS_ERROR; 438790075Sobrien } 438890075Sobrien 438990075Sobrien /* Generate a diagnostic for requesting data of a type that cannot 439090075Sobrien be passed through `...' due to type promotion at the call site. */ 4391169699Skan else if ((promoted_type = lang_hooks.types.type_promotes_to (type)) 4392117404Skan != type) 439390075Sobrien { 439496263Sobrien static bool gave_help; 439590075Sobrien 439696263Sobrien /* Unfortunately, this is merely undefined, rather than a constraint 439796263Sobrien violation, so we cannot make this an error. If this call is never 439896263Sobrien executed, the program is still strictly conforming. */ 4399169699Skan warning (0, "%qT is promoted to %qT when passed through %<...%>", 4400169699Skan type, promoted_type); 440190075Sobrien if (! gave_help) 440290075Sobrien { 440396263Sobrien gave_help = true; 4404169699Skan warning (0, "(so you should pass %qT not %qT to %<va_arg%>)", 4405169699Skan promoted_type, type); 440690075Sobrien } 440790075Sobrien 440896263Sobrien /* We can, however, treat "undefined" any way we please. 440996263Sobrien Call abort to encourage the user to fix the program. */ 4410132727Skan inform ("if this code is reached, the program will abort"); 4411169699Skan t = build_function_call_expr (implicit_built_in_decls[BUILT_IN_TRAP], 4412169699Skan NULL); 4413169699Skan append_to_statement_list (t, pre_p); 441496263Sobrien 441596263Sobrien /* This is dead code, but go ahead and finish so that the 441696263Sobrien mode of the result comes out right. */ 4417169699Skan *expr_p = dummy_object (type); 4418169699Skan return GS_ALL_DONE; 441990075Sobrien } 442090075Sobrien else 442190075Sobrien { 442290075Sobrien /* Make it easier for the backends by protecting the valist argument 4423169699Skan from multiple evaluations. */ 4424169699Skan if (TREE_CODE (va_list_type_node) == ARRAY_TYPE) 4425169699Skan { 4426169699Skan /* For this case, the backends will be expecting a pointer to 4427169699Skan TREE_TYPE (va_list_type_node), but it's possible we've 4428169699Skan actually been given an array (an actual va_list_type_node). 4429169699Skan So fix it. */ 4430169699Skan if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE) 4431169699Skan { 4432169699Skan tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node)); 4433169699Skan valist = build_fold_addr_expr_with_type (valist, p1); 4434169699Skan } 4435169699Skan gimplify_expr (&valist, pre_p, post_p, is_gimple_val, fb_rvalue); 4436169699Skan } 4437169699Skan else 4438169699Skan gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue); 443990075Sobrien 4440169699Skan if (!targetm.gimplify_va_arg_expr) 4441169699Skan /* FIXME:Once most targets are converted we should merely 4442169699Skan assert this is non-null. */ 4443169699Skan return GS_ALL_DONE; 4444169699Skan 4445169699Skan *expr_p = targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p); 4446169699Skan return GS_OK; 444790075Sobrien } 444890075Sobrien} 444990075Sobrien 445090075Sobrien/* Expand ARGLIST, from a call to __builtin_va_end. */ 445190075Sobrien 445290075Sobrienstatic rtx 4453132727Skanexpand_builtin_va_end (tree arglist) 445490075Sobrien{ 445590075Sobrien tree valist = TREE_VALUE (arglist); 445690075Sobrien 445790075Sobrien /* Evaluate for side effects, if needed. I hate macros that don't 445890075Sobrien do that. */ 445990075Sobrien if (TREE_SIDE_EFFECTS (valist)) 446090075Sobrien expand_expr (valist, const0_rtx, VOIDmode, EXPAND_NORMAL); 446190075Sobrien 446290075Sobrien return const0_rtx; 446390075Sobrien} 446490075Sobrien 446590075Sobrien/* Expand ARGLIST, from a call to __builtin_va_copy. We do this as a 446690075Sobrien builtin rather than just as an assignment in stdarg.h because of the 446790075Sobrien nastiness of array-type va_list types. */ 446890075Sobrien 446990075Sobrienstatic rtx 4470132727Skanexpand_builtin_va_copy (tree arglist) 447190075Sobrien{ 447290075Sobrien tree dst, src, t; 447390075Sobrien 447490075Sobrien dst = TREE_VALUE (arglist); 447590075Sobrien src = TREE_VALUE (TREE_CHAIN (arglist)); 447690075Sobrien 447790075Sobrien dst = stabilize_va_list (dst, 1); 447890075Sobrien src = stabilize_va_list (src, 0); 447990075Sobrien 448090075Sobrien if (TREE_CODE (va_list_type_node) != ARRAY_TYPE) 448190075Sobrien { 4482169699Skan t = build2 (MODIFY_EXPR, va_list_type_node, dst, src); 448390075Sobrien TREE_SIDE_EFFECTS (t) = 1; 448490075Sobrien expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); 448590075Sobrien } 448690075Sobrien else 448790075Sobrien { 448890075Sobrien rtx dstb, srcb, size; 448990075Sobrien 449090075Sobrien /* Evaluate to pointers. */ 449190075Sobrien dstb = expand_expr (dst, NULL_RTX, Pmode, EXPAND_NORMAL); 449290075Sobrien srcb = expand_expr (src, NULL_RTX, Pmode, EXPAND_NORMAL); 449390075Sobrien size = expand_expr (TYPE_SIZE_UNIT (va_list_type_node), NULL_RTX, 449490075Sobrien VOIDmode, EXPAND_NORMAL); 449590075Sobrien 4496132727Skan dstb = convert_memory_address (Pmode, dstb); 4497132727Skan srcb = convert_memory_address (Pmode, srcb); 449890075Sobrien 449990075Sobrien /* "Dereference" to BLKmode memories. */ 450090075Sobrien dstb = gen_rtx_MEM (BLKmode, dstb); 450190075Sobrien set_mem_alias_set (dstb, get_alias_set (TREE_TYPE (TREE_TYPE (dst)))); 450290075Sobrien set_mem_align (dstb, TYPE_ALIGN (va_list_type_node)); 450390075Sobrien srcb = gen_rtx_MEM (BLKmode, srcb); 450490075Sobrien set_mem_alias_set (srcb, get_alias_set (TREE_TYPE (TREE_TYPE (src)))); 450590075Sobrien set_mem_align (srcb, TYPE_ALIGN (va_list_type_node)); 450690075Sobrien 450790075Sobrien /* Copy. */ 4508117404Skan emit_block_move (dstb, srcb, size, BLOCK_OP_NORMAL); 450990075Sobrien } 451090075Sobrien 451190075Sobrien return const0_rtx; 451290075Sobrien} 451390075Sobrien 451490075Sobrien/* Expand a call to one of the builtin functions __builtin_frame_address or 451590075Sobrien __builtin_return_address. */ 451690075Sobrien 451790075Sobrienstatic rtx 4518132727Skanexpand_builtin_frame_address (tree fndecl, tree arglist) 451990075Sobrien{ 452090075Sobrien /* The argument must be a nonnegative integer constant. 452190075Sobrien It counts the number of frames to scan up the stack. 452290075Sobrien The value is the return address saved in that frame. */ 452390075Sobrien if (arglist == 0) 452490075Sobrien /* Warning about missing arg was already issued. */ 452590075Sobrien return const0_rtx; 452690075Sobrien else if (! host_integerp (TREE_VALUE (arglist), 1)) 452790075Sobrien { 452890075Sobrien if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) 4529169699Skan error ("invalid argument to %<__builtin_frame_address%>"); 453090075Sobrien else 4531169699Skan error ("invalid argument to %<__builtin_return_address%>"); 453290075Sobrien return const0_rtx; 453390075Sobrien } 453490075Sobrien else 453590075Sobrien { 453690075Sobrien rtx tem 453790075Sobrien = expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl), 4538169699Skan tree_low_cst (TREE_VALUE (arglist), 1)); 453990075Sobrien 454090075Sobrien /* Some ports cannot access arbitrary stack frames. */ 454190075Sobrien if (tem == NULL) 454290075Sobrien { 454390075Sobrien if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) 4544169699Skan warning (0, "unsupported argument to %<__builtin_frame_address%>"); 454590075Sobrien else 4546169699Skan warning (0, "unsupported argument to %<__builtin_return_address%>"); 454790075Sobrien return const0_rtx; 454890075Sobrien } 454990075Sobrien 455090075Sobrien /* For __builtin_frame_address, return what we've got. */ 455190075Sobrien if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) 455290075Sobrien return tem; 455390075Sobrien 4554169699Skan if (!REG_P (tem) 455590075Sobrien && ! CONSTANT_P (tem)) 455690075Sobrien tem = copy_to_mode_reg (Pmode, tem); 455790075Sobrien return tem; 455890075Sobrien } 455990075Sobrien} 456090075Sobrien 456190075Sobrien/* Expand a call to the alloca builtin, with arguments ARGLIST. Return 0 if 456290075Sobrien we failed and the caller should emit a normal call, otherwise try to get 456390075Sobrien the result in TARGET, if convenient. */ 456490075Sobrien 456590075Sobrienstatic rtx 4566132727Skanexpand_builtin_alloca (tree arglist, rtx target) 456790075Sobrien{ 456890075Sobrien rtx op0; 456990075Sobrien rtx result; 457090075Sobrien 4571169699Skan /* In -fmudflap-instrumented code, alloca() and __builtin_alloca() 4572169699Skan should always expand to function calls. These can be intercepted 4573169699Skan in libmudflap. */ 4574169699Skan if (flag_mudflap) 4575169699Skan return 0; 4576169699Skan 457790075Sobrien if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 457890075Sobrien return 0; 457990075Sobrien 458090075Sobrien /* Compute the argument. */ 4581169699Skan op0 = expand_normal (TREE_VALUE (arglist)); 458290075Sobrien 458390075Sobrien /* Allocate the desired space. */ 458490075Sobrien result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT); 4585132727Skan result = convert_memory_address (ptr_mode, result); 458690075Sobrien 458790075Sobrien return result; 458890075Sobrien} 458990075Sobrien 4590259563Spfg/* Expand a call to a bswap builtin. The arguments are in ARGLIST. MODE 4591259563Spfg is the mode to expand with. */ 4592259563Spfg 4593259563Spfgstatic rtx 4594259563Spfgexpand_builtin_bswap (tree arglist, rtx target, rtx subtarget) 4595259563Spfg{ 4596259563Spfg enum machine_mode mode; 4597259563Spfg tree arg; 4598259563Spfg rtx op0; 4599259563Spfg 4600259563Spfg if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 4601259563Spfg return 0; 4602259563Spfg 4603259563Spfg arg = TREE_VALUE (arglist); 4604259563Spfg mode = TYPE_MODE (TREE_TYPE (arg)); 4605259563Spfg op0 = expand_expr (arg, subtarget, VOIDmode, 0); 4606259563Spfg 4607259563Spfg target = expand_unop (mode, bswap_optab, op0, target, 1); 4608259563Spfg 4609259563Spfg gcc_assert (target); 4610259563Spfg 4611259563Spfg return convert_to_mode (mode, target, 0); 4612259563Spfg} 4613259563Spfg 4614132727Skan/* Expand a call to a unary builtin. The arguments are in ARGLIST. 461590075Sobrien Return 0 if a normal call should be emitted rather than expanding the 461690075Sobrien function in-line. If convenient, the result should be placed in TARGET. 461790075Sobrien SUBTARGET may be used as the target for computing one of EXP's operands. */ 461890075Sobrien 461990075Sobrienstatic rtx 4620132727Skanexpand_builtin_unop (enum machine_mode target_mode, tree arglist, rtx target, 4621132727Skan rtx subtarget, optab op_optab) 462290075Sobrien{ 462390075Sobrien rtx op0; 462490075Sobrien if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 462590075Sobrien return 0; 462690075Sobrien 462790075Sobrien /* Compute the argument. */ 462890075Sobrien op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); 4629132727Skan /* Compute op, into TARGET if possible. 463090075Sobrien Set TARGET to wherever the result comes back. */ 463190075Sobrien target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))), 4632132727Skan op_optab, op0, target, 1); 4633169699Skan gcc_assert (target); 4634132727Skan 4635132727Skan return convert_to_mode (target_mode, target, 0); 463690075Sobrien} 463790075Sobrien 463890075Sobrien/* If the string passed to fputs is a constant and is one character 463990075Sobrien long, we attempt to transform this call into __builtin_fputc(). */ 464090075Sobrien 464190075Sobrienstatic rtx 4642132727Skanexpand_builtin_fputs (tree arglist, rtx target, bool unlocked) 464390075Sobrien{ 464490075Sobrien /* Verify the arguments in the original call. */ 4645169699Skan if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 464690075Sobrien { 4647169699Skan tree result = fold_builtin_fputs (arglist, (target == const0_rtx), 4648169699Skan unlocked, NULL_TREE); 4649169699Skan if (result) 4650169699Skan return expand_expr (result, target, VOIDmode, EXPAND_NORMAL); 465190075Sobrien } 4652169699Skan return 0; 465390075Sobrien} 465490075Sobrien 465590075Sobrien/* Expand a call to __builtin_expect. We return our argument and emit a 465690075Sobrien NOTE_INSN_EXPECTED_VALUE note. This is the expansion of __builtin_expect in 465790075Sobrien a non-jump context. */ 465890075Sobrien 465990075Sobrienstatic rtx 4660132727Skanexpand_builtin_expect (tree arglist, rtx target) 466190075Sobrien{ 466290075Sobrien tree exp, c; 466390075Sobrien rtx note, rtx_c; 466490075Sobrien 466590075Sobrien if (arglist == NULL_TREE 466690075Sobrien || TREE_CHAIN (arglist) == NULL_TREE) 466790075Sobrien return const0_rtx; 466890075Sobrien exp = TREE_VALUE (arglist); 466990075Sobrien c = TREE_VALUE (TREE_CHAIN (arglist)); 467090075Sobrien 467190075Sobrien if (TREE_CODE (c) != INTEGER_CST) 467290075Sobrien { 4673169699Skan error ("second argument to %<__builtin_expect%> must be a constant"); 467490075Sobrien c = integer_zero_node; 467590075Sobrien } 467690075Sobrien 467790075Sobrien target = expand_expr (exp, target, VOIDmode, EXPAND_NORMAL); 467890075Sobrien 467990075Sobrien /* Don't bother with expected value notes for integral constants. */ 4680117404Skan if (flag_guess_branch_prob && GET_CODE (target) != CONST_INT) 468190075Sobrien { 468290075Sobrien /* We do need to force this into a register so that we can be 468390075Sobrien moderately sure to be able to correctly interpret the branch 468490075Sobrien condition later. */ 468590075Sobrien target = force_reg (GET_MODE (target), target); 468690075Sobrien 468790075Sobrien rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL); 468890075Sobrien 4689132727Skan note = emit_note (NOTE_INSN_EXPECTED_VALUE); 469090075Sobrien NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c); 469190075Sobrien } 469290075Sobrien 469390075Sobrien return target; 469490075Sobrien} 469590075Sobrien 469690075Sobrien/* Like expand_builtin_expect, except do this in a jump context. This is 469790075Sobrien called from do_jump if the conditional is a __builtin_expect. Return either 4698117404Skan a list of insns to emit the jump or NULL if we cannot optimize 469990075Sobrien __builtin_expect. We need to optimize this at jump time so that machines 470090075Sobrien like the PowerPC don't turn the test into a SCC operation, and then jump 470190075Sobrien based on the test being 0/1. */ 470290075Sobrien 470390075Sobrienrtx 4704132727Skanexpand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) 470590075Sobrien{ 470690075Sobrien tree arglist = TREE_OPERAND (exp, 1); 470790075Sobrien tree arg0 = TREE_VALUE (arglist); 470890075Sobrien tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 470990075Sobrien rtx ret = NULL_RTX; 471090075Sobrien 471190075Sobrien /* Only handle __builtin_expect (test, 0) and 471290075Sobrien __builtin_expect (test, 1). */ 471390075Sobrien if (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE 471490075Sobrien && (integer_zerop (arg1) || integer_onep (arg1))) 471590075Sobrien { 4716132727Skan rtx insn, drop_through_label, temp; 471790075Sobrien 471890075Sobrien /* Expand the jump insns. */ 471990075Sobrien start_sequence (); 472090075Sobrien do_jump (arg0, if_false_label, if_true_label); 4721117404Skan ret = get_insns (); 4722132727Skan 4723132727Skan drop_through_label = get_last_insn (); 4724169699Skan if (drop_through_label && NOTE_P (drop_through_label)) 4725132727Skan drop_through_label = prev_nonnote_insn (drop_through_label); 4726169699Skan if (drop_through_label && !LABEL_P (drop_through_label)) 4727132727Skan drop_through_label = NULL_RTX; 472890075Sobrien end_sequence (); 472990075Sobrien 4730132727Skan if (! if_true_label) 4731132727Skan if_true_label = drop_through_label; 4732132727Skan if (! if_false_label) 4733132727Skan if_false_label = drop_through_label; 4734132727Skan 4735132727Skan /* Go through and add the expect's to each of the conditional jumps. */ 4736117404Skan insn = ret; 4737117404Skan while (insn != NULL_RTX) 473890075Sobrien { 4739117404Skan rtx next = NEXT_INSN (insn); 474090075Sobrien 4741169699Skan if (JUMP_P (insn) && any_condjump_p (insn)) 474290075Sobrien { 4743132727Skan rtx ifelse = SET_SRC (pc_set (insn)); 4744132727Skan rtx then_dest = XEXP (ifelse, 1); 4745132727Skan rtx else_dest = XEXP (ifelse, 2); 4746132727Skan int taken = -1; 474790075Sobrien 4748132727Skan /* First check if we recognize any of the labels. */ 4749132727Skan if (GET_CODE (then_dest) == LABEL_REF 4750132727Skan && XEXP (then_dest, 0) == if_true_label) 4751132727Skan taken = 1; 4752132727Skan else if (GET_CODE (then_dest) == LABEL_REF 4753132727Skan && XEXP (then_dest, 0) == if_false_label) 4754132727Skan taken = 0; 4755132727Skan else if (GET_CODE (else_dest) == LABEL_REF 4756132727Skan && XEXP (else_dest, 0) == if_false_label) 4757132727Skan taken = 1; 4758132727Skan else if (GET_CODE (else_dest) == LABEL_REF 4759132727Skan && XEXP (else_dest, 0) == if_true_label) 4760132727Skan taken = 0; 4761132727Skan /* Otherwise check where we drop through. */ 4762132727Skan else if (else_dest == pc_rtx) 4763132727Skan { 4764169699Skan if (next && NOTE_P (next)) 4765132727Skan next = next_nonnote_insn (next); 476690075Sobrien 4767169699Skan if (next && JUMP_P (next) 4768132727Skan && any_uncondjump_p (next)) 4769132727Skan temp = XEXP (SET_SRC (pc_set (next)), 0); 4770132727Skan else 4771132727Skan temp = next; 4772132727Skan 4773132727Skan /* TEMP is either a CODE_LABEL, NULL_RTX or something 4774132727Skan else that can't possibly match either target label. */ 4775132727Skan if (temp == if_false_label) 4776132727Skan taken = 1; 4777132727Skan else if (temp == if_true_label) 4778132727Skan taken = 0; 477990075Sobrien } 4780132727Skan else if (then_dest == pc_rtx) 478190075Sobrien { 4782169699Skan if (next && NOTE_P (next)) 4783132727Skan next = next_nonnote_insn (next); 4784132727Skan 4785169699Skan if (next && JUMP_P (next) 4786132727Skan && any_uncondjump_p (next)) 4787132727Skan temp = XEXP (SET_SRC (pc_set (next)), 0); 4788132727Skan else 4789132727Skan temp = next; 4790132727Skan 4791132727Skan if (temp == if_false_label) 4792132727Skan taken = 0; 4793132727Skan else if (temp == if_true_label) 4794132727Skan taken = 1; 479590075Sobrien } 4796132727Skan 4797132727Skan if (taken != -1) 479890075Sobrien { 4799132727Skan /* If the test is expected to fail, reverse the 4800132727Skan probabilities. */ 4801132727Skan if (integer_zerop (arg1)) 4802132727Skan taken = 1 - taken; 4803169699Skan predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken); 480490075Sobrien } 480590075Sobrien } 4806117404Skan 4807117404Skan insn = next; 480890075Sobrien } 480990075Sobrien } 481090075Sobrien 481190075Sobrien return ret; 481290075Sobrien} 481396263Sobrien 481496263Sobrienvoid 4815132727Skanexpand_builtin_trap (void) 481696263Sobrien{ 481796263Sobrien#ifdef HAVE_trap 481896263Sobrien if (HAVE_trap) 481996263Sobrien emit_insn (gen_trap ()); 482096263Sobrien else 482196263Sobrien#endif 482296263Sobrien emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0); 482396263Sobrien emit_barrier (); 482496263Sobrien} 4825132727Skan 4826132727Skan/* Expand a call to fabs, fabsf or fabsl with arguments ARGLIST. 4827132727Skan Return 0 if a normal call should be emitted rather than expanding 4828132727Skan the function inline. If convenient, the result should be placed 4829132727Skan in TARGET. SUBTARGET may be used as the target for computing 4830132727Skan the operand. */ 4831132727Skan 4832132727Skanstatic rtx 4833132727Skanexpand_builtin_fabs (tree arglist, rtx target, rtx subtarget) 4834132727Skan{ 4835132727Skan enum machine_mode mode; 4836132727Skan tree arg; 4837132727Skan rtx op0; 4838132727Skan 4839132727Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 4840132727Skan return 0; 4841132727Skan 4842132727Skan arg = TREE_VALUE (arglist); 4843132727Skan mode = TYPE_MODE (TREE_TYPE (arg)); 4844132727Skan op0 = expand_expr (arg, subtarget, VOIDmode, 0); 4845132727Skan return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1)); 4846132727Skan} 4847132727Skan 4848169699Skan/* Expand a call to copysign, copysignf, or copysignl with arguments ARGLIST. 4849169699Skan Return NULL is a normal call should be emitted rather than expanding the 4850169699Skan function inline. If convenient, the result should be placed in TARGET. 4851169699Skan SUBTARGET may be used as the target for computing the operand. */ 4852132727Skan 4853132727Skanstatic rtx 4854169699Skanexpand_builtin_copysign (tree arglist, rtx target, rtx subtarget) 4855132727Skan{ 4856169699Skan rtx op0, op1; 4857132727Skan tree arg; 4858132727Skan 4859169699Skan if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) 4860132727Skan return 0; 4861169699Skan 4862132727Skan arg = TREE_VALUE (arglist); 4863169699Skan op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL); 4864132727Skan 4865169699Skan arg = TREE_VALUE (TREE_CHAIN (arglist)); 4866169699Skan op1 = expand_normal (arg); 4867169699Skan 4868169699Skan return expand_copysign (op0, op1, target); 4869132727Skan} 4870132727Skan 4871132727Skan/* Create a new constant string literal and return a char* pointer to it. 4872132727Skan The STRING_CST value is the LEN characters at STR. */ 4873169699Skantree 4874132727Skanbuild_string_literal (int len, const char *str) 4875132727Skan{ 4876132727Skan tree t, elem, index, type; 4877132727Skan 4878132727Skan t = build_string (len, str); 4879132727Skan elem = build_type_variant (char_type_node, 1, 0); 4880169699Skan index = build_index_type (build_int_cst (NULL_TREE, len - 1)); 4881132727Skan type = build_array_type (elem, index); 4882132727Skan TREE_TYPE (t) = type; 4883132727Skan TREE_CONSTANT (t) = 1; 4884169699Skan TREE_INVARIANT (t) = 1; 4885132727Skan TREE_READONLY (t) = 1; 4886132727Skan TREE_STATIC (t) = 1; 4887132727Skan 4888132727Skan type = build_pointer_type (type); 4889132727Skan t = build1 (ADDR_EXPR, type, t); 4890132727Skan 4891132727Skan type = build_pointer_type (elem); 4892132727Skan t = build1 (NOP_EXPR, type, t); 4893132727Skan return t; 4894132727Skan} 4895132727Skan 4896169699Skan/* Expand EXP, a call to printf or printf_unlocked. 4897132727Skan Return 0 if a normal call should be emitted rather than transforming 4898132727Skan the function inline. If convenient, the result should be placed in 4899169699Skan TARGET with mode MODE. UNLOCKED indicates this is a printf_unlocked 4900132727Skan call. */ 4901132727Skanstatic rtx 4902169699Skanexpand_builtin_printf (tree exp, rtx target, enum machine_mode mode, 4903132727Skan bool unlocked) 4904132727Skan{ 4905169699Skan tree arglist = TREE_OPERAND (exp, 1); 4906161660Skan /* If we're using an unlocked function, assume the other unlocked 4907161660Skan functions exist explicitly. */ 4908161660Skan tree const fn_putchar = unlocked ? built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED] 4909161660Skan : implicit_built_in_decls[BUILT_IN_PUTCHAR]; 4910161660Skan tree const fn_puts = unlocked ? built_in_decls[BUILT_IN_PUTS_UNLOCKED] 4911161660Skan : implicit_built_in_decls[BUILT_IN_PUTS]; 4912132727Skan const char *fmt_str; 4913132727Skan tree fn, fmt, arg; 4914132727Skan 4915132727Skan /* If the return value is used, don't do the transformation. */ 4916132727Skan if (target != const0_rtx) 4917132727Skan return 0; 4918132727Skan 4919132727Skan /* Verify the required arguments in the original call. */ 4920132727Skan if (! arglist) 4921132727Skan return 0; 4922132727Skan fmt = TREE_VALUE (arglist); 4923169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fmt))) 4924132727Skan return 0; 4925132727Skan arglist = TREE_CHAIN (arglist); 4926132727Skan 4927132727Skan /* Check whether the format is a literal string constant. */ 4928132727Skan fmt_str = c_getstr (fmt); 4929132727Skan if (fmt_str == NULL) 4930132727Skan return 0; 4931132727Skan 4932169699Skan if (!init_target_chars()) 4933169699Skan return 0; 4934169699Skan 4935132727Skan /* If the format specifier was "%s\n", call __builtin_puts(arg). */ 4936169699Skan if (strcmp (fmt_str, target_percent_s_newline) == 0) 4937132727Skan { 4938132727Skan if (! arglist 4939169699Skan || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) 4940132727Skan || TREE_CHAIN (arglist)) 4941132727Skan return 0; 4942132727Skan fn = fn_puts; 4943132727Skan } 4944132727Skan /* If the format specifier was "%c", call __builtin_putchar(arg). */ 4945169699Skan else if (strcmp (fmt_str, target_percent_c) == 0) 4946132727Skan { 4947132727Skan if (! arglist 4948132727Skan || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE 4949132727Skan || TREE_CHAIN (arglist)) 4950132727Skan return 0; 4951132727Skan fn = fn_putchar; 4952132727Skan } 4953132727Skan else 4954132727Skan { 4955132727Skan /* We can't handle anything else with % args or %% ... yet. */ 4956169699Skan if (strchr (fmt_str, target_percent)) 4957169699Skan return 0; 4958132727Skan 4959132727Skan if (arglist) 4960132727Skan return 0; 4961132727Skan 4962132727Skan /* If the format specifier was "", printf does nothing. */ 4963132727Skan if (fmt_str[0] == '\0') 4964132727Skan return const0_rtx; 4965132727Skan /* If the format specifier has length of 1, call putchar. */ 4966132727Skan if (fmt_str[1] == '\0') 4967132727Skan { 4968132727Skan /* Given printf("c"), (where c is any one character,) 4969132727Skan convert "c"[0] to an int and pass that to the replacement 4970132727Skan function. */ 4971169699Skan arg = build_int_cst (NULL_TREE, fmt_str[0]); 4972132727Skan arglist = build_tree_list (NULL_TREE, arg); 4973132727Skan fn = fn_putchar; 4974132727Skan } 4975132727Skan else 4976132727Skan { 4977132727Skan /* If the format specifier was "string\n", call puts("string"). */ 4978132727Skan size_t len = strlen (fmt_str); 4979169699Skan if ((unsigned char)fmt_str[len - 1] == target_newline) 4980132727Skan { 4981132727Skan /* Create a NUL-terminated string that's one char shorter 4982132727Skan than the original, stripping off the trailing '\n'. */ 4983169699Skan char *newstr = alloca (len); 4984132727Skan memcpy (newstr, fmt_str, len - 1); 4985132727Skan newstr[len - 1] = 0; 4986132727Skan 4987132727Skan arg = build_string_literal (len, newstr); 4988132727Skan arglist = build_tree_list (NULL_TREE, arg); 4989132727Skan fn = fn_puts; 4990132727Skan } 4991132727Skan else 4992132727Skan /* We'd like to arrange to call fputs(string,stdout) here, 4993132727Skan but we need stdout and don't have a way to get it yet. */ 4994132727Skan return 0; 4995132727Skan } 4996132727Skan } 4997132727Skan 4998132727Skan if (!fn) 4999132727Skan return 0; 5000169699Skan fn = build_function_call_expr (fn, arglist); 5001169699Skan if (TREE_CODE (fn) == CALL_EXPR) 5002169699Skan CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); 5003169699Skan return expand_expr (fn, target, mode, EXPAND_NORMAL); 5004132727Skan} 5005132727Skan 5006169699Skan/* Expand EXP, a call to fprintf or fprintf_unlocked. 5007132727Skan Return 0 if a normal call should be emitted rather than transforming 5008132727Skan the function inline. If convenient, the result should be placed in 5009169699Skan TARGET with mode MODE. UNLOCKED indicates this is a fprintf_unlocked 5010132727Skan call. */ 5011132727Skanstatic rtx 5012169699Skanexpand_builtin_fprintf (tree exp, rtx target, enum machine_mode mode, 5013169699Skan bool unlocked) 5014132727Skan{ 5015169699Skan tree arglist = TREE_OPERAND (exp, 1); 5016161660Skan /* If we're using an unlocked function, assume the other unlocked 5017161660Skan functions exist explicitly. */ 5018161660Skan tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED] 5019161660Skan : implicit_built_in_decls[BUILT_IN_FPUTC]; 5020161660Skan tree const fn_fputs = unlocked ? built_in_decls[BUILT_IN_FPUTS_UNLOCKED] 5021161660Skan : implicit_built_in_decls[BUILT_IN_FPUTS]; 5022132727Skan const char *fmt_str; 5023132727Skan tree fn, fmt, fp, arg; 5024132727Skan 5025132727Skan /* If the return value is used, don't do the transformation. */ 5026132727Skan if (target != const0_rtx) 5027132727Skan return 0; 5028132727Skan 5029132727Skan /* Verify the required arguments in the original call. */ 5030132727Skan if (! arglist) 5031132727Skan return 0; 5032132727Skan fp = TREE_VALUE (arglist); 5033169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fp))) 5034132727Skan return 0; 5035132727Skan arglist = TREE_CHAIN (arglist); 5036132727Skan if (! arglist) 5037132727Skan return 0; 5038132727Skan fmt = TREE_VALUE (arglist); 5039169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fmt))) 5040132727Skan return 0; 5041132727Skan arglist = TREE_CHAIN (arglist); 5042132727Skan 5043132727Skan /* Check whether the format is a literal string constant. */ 5044132727Skan fmt_str = c_getstr (fmt); 5045132727Skan if (fmt_str == NULL) 5046132727Skan return 0; 5047132727Skan 5048169699Skan if (!init_target_chars()) 5049169699Skan return 0; 5050169699Skan 5051132727Skan /* If the format specifier was "%s", call __builtin_fputs(arg,fp). */ 5052169699Skan if (strcmp (fmt_str, target_percent_s) == 0) 5053132727Skan { 5054132727Skan if (! arglist 5055169699Skan || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) 5056132727Skan || TREE_CHAIN (arglist)) 5057132727Skan return 0; 5058132727Skan arg = TREE_VALUE (arglist); 5059132727Skan arglist = build_tree_list (NULL_TREE, fp); 5060132727Skan arglist = tree_cons (NULL_TREE, arg, arglist); 5061132727Skan fn = fn_fputs; 5062132727Skan } 5063132727Skan /* If the format specifier was "%c", call __builtin_fputc(arg,fp). */ 5064169699Skan else if (strcmp (fmt_str, target_percent_c) == 0) 5065132727Skan { 5066132727Skan if (! arglist 5067132727Skan || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE 5068132727Skan || TREE_CHAIN (arglist)) 5069132727Skan return 0; 5070132727Skan arg = TREE_VALUE (arglist); 5071132727Skan arglist = build_tree_list (NULL_TREE, fp); 5072132727Skan arglist = tree_cons (NULL_TREE, arg, arglist); 5073132727Skan fn = fn_fputc; 5074132727Skan } 5075132727Skan else 5076132727Skan { 5077132727Skan /* We can't handle anything else with % args or %% ... yet. */ 5078169699Skan if (strchr (fmt_str, target_percent)) 5079169699Skan return 0; 5080132727Skan 5081132727Skan if (arglist) 5082132727Skan return 0; 5083132727Skan 5084132727Skan /* If the format specifier was "", fprintf does nothing. */ 5085132727Skan if (fmt_str[0] == '\0') 5086132727Skan { 5087132727Skan /* Evaluate and ignore FILE* argument for side-effects. */ 5088132727Skan expand_expr (fp, const0_rtx, VOIDmode, EXPAND_NORMAL); 5089132727Skan return const0_rtx; 5090132727Skan } 5091132727Skan 5092132727Skan /* When "string" doesn't contain %, replace all cases of 5093132727Skan fprintf(stream,string) with fputs(string,stream). The fputs 5094132727Skan builtin will take care of special cases like length == 1. */ 5095132727Skan arglist = build_tree_list (NULL_TREE, fp); 5096132727Skan arglist = tree_cons (NULL_TREE, fmt, arglist); 5097132727Skan fn = fn_fputs; 5098132727Skan } 5099132727Skan 5100132727Skan if (!fn) 5101132727Skan return 0; 5102169699Skan fn = build_function_call_expr (fn, arglist); 5103169699Skan if (TREE_CODE (fn) == CALL_EXPR) 5104169699Skan CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); 5105169699Skan return expand_expr (fn, target, mode, EXPAND_NORMAL); 5106132727Skan} 5107132727Skan 5108132727Skan/* Expand a call to sprintf with argument list ARGLIST. Return 0 if 5109132727Skan a normal call should be emitted rather than expanding the function 5110132727Skan inline. If convenient, the result should be placed in TARGET with 5111132727Skan mode MODE. */ 5112132727Skan 5113132727Skanstatic rtx 5114132727Skanexpand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) 5115132727Skan{ 5116132727Skan tree orig_arglist, dest, fmt; 5117132727Skan const char *fmt_str; 5118132727Skan 5119132727Skan orig_arglist = arglist; 5120132727Skan 5121132727Skan /* Verify the required arguments in the original call. */ 5122132727Skan if (! arglist) 5123132727Skan return 0; 5124132727Skan dest = TREE_VALUE (arglist); 5125169699Skan if (! POINTER_TYPE_P (TREE_TYPE (dest))) 5126132727Skan return 0; 5127132727Skan arglist = TREE_CHAIN (arglist); 5128132727Skan if (! arglist) 5129132727Skan return 0; 5130132727Skan fmt = TREE_VALUE (arglist); 5131169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fmt))) 5132132727Skan return 0; 5133132727Skan arglist = TREE_CHAIN (arglist); 5134132727Skan 5135132727Skan /* Check whether the format is a literal string constant. */ 5136132727Skan fmt_str = c_getstr (fmt); 5137132727Skan if (fmt_str == NULL) 5138132727Skan return 0; 5139132727Skan 5140169699Skan if (!init_target_chars()) 5141169699Skan return 0; 5142169699Skan 5143132727Skan /* If the format doesn't contain % args or %%, use strcpy. */ 5144169699Skan if (strchr (fmt_str, target_percent) == 0) 5145132727Skan { 5146132727Skan tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; 5147132727Skan tree exp; 5148132727Skan 5149132727Skan if (arglist || ! fn) 5150132727Skan return 0; 5151132727Skan expand_expr (build_function_call_expr (fn, orig_arglist), 5152132727Skan const0_rtx, VOIDmode, EXPAND_NORMAL); 5153132727Skan if (target == const0_rtx) 5154132727Skan return const0_rtx; 5155169699Skan exp = build_int_cst (NULL_TREE, strlen (fmt_str)); 5156132727Skan return expand_expr (exp, target, mode, EXPAND_NORMAL); 5157132727Skan } 5158132727Skan /* If the format is "%s", use strcpy if the result isn't used. */ 5159169699Skan else if (strcmp (fmt_str, target_percent_s) == 0) 5160132727Skan { 5161132727Skan tree fn, arg, len; 5162132727Skan fn = implicit_built_in_decls[BUILT_IN_STRCPY]; 5163132727Skan 5164132727Skan if (! fn) 5165132727Skan return 0; 5166132727Skan 5167132727Skan if (! arglist || TREE_CHAIN (arglist)) 5168132727Skan return 0; 5169132727Skan arg = TREE_VALUE (arglist); 5170169699Skan if (! POINTER_TYPE_P (TREE_TYPE (arg))) 5171132727Skan return 0; 5172132727Skan 5173132727Skan if (target != const0_rtx) 5174132727Skan { 5175132727Skan len = c_strlen (arg, 1); 5176132727Skan if (! len || TREE_CODE (len) != INTEGER_CST) 5177132727Skan return 0; 5178132727Skan } 5179132727Skan else 5180132727Skan len = NULL_TREE; 5181132727Skan 5182132727Skan arglist = build_tree_list (NULL_TREE, arg); 5183132727Skan arglist = tree_cons (NULL_TREE, dest, arglist); 5184132727Skan expand_expr (build_function_call_expr (fn, arglist), 5185132727Skan const0_rtx, VOIDmode, EXPAND_NORMAL); 5186132727Skan 5187132727Skan if (target == const0_rtx) 5188132727Skan return const0_rtx; 5189132727Skan return expand_expr (len, target, mode, EXPAND_NORMAL); 5190132727Skan } 5191132727Skan 5192132727Skan return 0; 5193132727Skan} 5194169699Skan 5195169699Skan/* Expand a call to either the entry or exit function profiler. */ 5196169699Skan 5197169699Skanstatic rtx 5198169699Skanexpand_builtin_profile_func (bool exitp) 5199169699Skan{ 5200169699Skan rtx this, which; 5201169699Skan 5202169699Skan this = DECL_RTL (current_function_decl); 5203169699Skan gcc_assert (MEM_P (this)); 5204169699Skan this = XEXP (this, 0); 5205169699Skan 5206169699Skan if (exitp) 5207169699Skan which = profile_function_exit_libfunc; 5208169699Skan else 5209169699Skan which = profile_function_entry_libfunc; 5210169699Skan 5211169699Skan emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this, Pmode, 5212169699Skan expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, 5213169699Skan 0), 5214169699Skan Pmode); 5215169699Skan 5216169699Skan return const0_rtx; 5217169699Skan} 5218169699Skan 5219169699Skan/* Given a trampoline address, make sure it satisfies TRAMPOLINE_ALIGNMENT. */ 5220169699Skan 5221169699Skanstatic rtx 5222169699Skanround_trampoline_addr (rtx tramp) 5223169699Skan{ 5224169699Skan rtx temp, addend, mask; 5225169699Skan 5226169699Skan /* If we don't need too much alignment, we'll have been guaranteed 5227169699Skan proper alignment by get_trampoline_type. */ 5228169699Skan if (TRAMPOLINE_ALIGNMENT <= STACK_BOUNDARY) 5229169699Skan return tramp; 5230169699Skan 5231169699Skan /* Round address up to desired boundary. */ 5232169699Skan temp = gen_reg_rtx (Pmode); 5233169699Skan addend = GEN_INT (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1); 5234169699Skan mask = GEN_INT (-TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT); 5235169699Skan 5236169699Skan temp = expand_simple_binop (Pmode, PLUS, tramp, addend, 5237169699Skan temp, 0, OPTAB_LIB_WIDEN); 5238169699Skan tramp = expand_simple_binop (Pmode, AND, temp, mask, 5239169699Skan temp, 0, OPTAB_LIB_WIDEN); 5240169699Skan 5241169699Skan return tramp; 5242169699Skan} 5243169699Skan 5244169699Skanstatic rtx 5245169699Skanexpand_builtin_init_trampoline (tree arglist) 5246169699Skan{ 5247169699Skan tree t_tramp, t_func, t_chain; 5248169699Skan rtx r_tramp, r_func, r_chain; 5249169699Skan#ifdef TRAMPOLINE_TEMPLATE 5250169699Skan rtx blktramp; 5251169699Skan#endif 5252169699Skan 5253169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, 5254169699Skan POINTER_TYPE, VOID_TYPE)) 5255169699Skan return NULL_RTX; 5256169699Skan 5257169699Skan t_tramp = TREE_VALUE (arglist); 5258169699Skan arglist = TREE_CHAIN (arglist); 5259169699Skan t_func = TREE_VALUE (arglist); 5260169699Skan arglist = TREE_CHAIN (arglist); 5261169699Skan t_chain = TREE_VALUE (arglist); 5262169699Skan 5263169699Skan r_tramp = expand_normal (t_tramp); 5264169699Skan r_func = expand_normal (t_func); 5265169699Skan r_chain = expand_normal (t_chain); 5266169699Skan 5267169699Skan /* Generate insns to initialize the trampoline. */ 5268169699Skan r_tramp = round_trampoline_addr (r_tramp); 5269169699Skan#ifdef TRAMPOLINE_TEMPLATE 5270169699Skan blktramp = gen_rtx_MEM (BLKmode, r_tramp); 5271169699Skan set_mem_align (blktramp, TRAMPOLINE_ALIGNMENT); 5272169699Skan emit_block_move (blktramp, assemble_trampoline_template (), 5273169699Skan GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL); 5274169699Skan#endif 5275169699Skan trampolines_created = 1; 5276169699Skan INITIALIZE_TRAMPOLINE (r_tramp, r_func, r_chain); 5277169699Skan 5278169699Skan return const0_rtx; 5279169699Skan} 5280169699Skan 5281169699Skanstatic rtx 5282169699Skanexpand_builtin_adjust_trampoline (tree arglist) 5283169699Skan{ 5284169699Skan rtx tramp; 5285169699Skan 5286169699Skan if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) 5287169699Skan return NULL_RTX; 5288169699Skan 5289169699Skan tramp = expand_normal (TREE_VALUE (arglist)); 5290169699Skan tramp = round_trampoline_addr (tramp); 5291169699Skan#ifdef TRAMPOLINE_ADJUST_ADDRESS 5292169699Skan TRAMPOLINE_ADJUST_ADDRESS (tramp); 5293169699Skan#endif 5294169699Skan 5295169699Skan return tramp; 5296169699Skan} 5297169699Skan 5298169699Skan/* Expand a call to the built-in signbit, signbitf or signbitl function. 5299169699Skan Return NULL_RTX if a normal call should be emitted rather than expanding 5300169699Skan the function in-line. EXP is the expression that is a call to the builtin 5301169699Skan function; if convenient, the result should be placed in TARGET. */ 5302169699Skan 5303169699Skanstatic rtx 5304169699Skanexpand_builtin_signbit (tree exp, rtx target) 5305169699Skan{ 5306169699Skan const struct real_format *fmt; 5307169699Skan enum machine_mode fmode, imode, rmode; 5308169699Skan HOST_WIDE_INT hi, lo; 5309169699Skan tree arg, arglist; 5310169699Skan int word, bitpos; 5311169699Skan rtx temp; 5312169699Skan 5313169699Skan arglist = TREE_OPERAND (exp, 1); 5314169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 5315169699Skan return 0; 5316169699Skan 5317169699Skan arg = TREE_VALUE (arglist); 5318169699Skan fmode = TYPE_MODE (TREE_TYPE (arg)); 5319169699Skan rmode = TYPE_MODE (TREE_TYPE (exp)); 5320169699Skan fmt = REAL_MODE_FORMAT (fmode); 5321169699Skan 5322169699Skan /* For floating point formats without a sign bit, implement signbit 5323169699Skan as "ARG < 0.0". */ 5324169699Skan bitpos = fmt->signbit_ro; 5325169699Skan if (bitpos < 0) 5326169699Skan { 5327169699Skan /* But we can't do this if the format supports signed zero. */ 5328169699Skan if (fmt->has_signed_zero && HONOR_SIGNED_ZEROS (fmode)) 5329169699Skan return 0; 5330169699Skan 5331169699Skan arg = fold_build2 (LT_EXPR, TREE_TYPE (exp), arg, 5332169699Skan build_real (TREE_TYPE (arg), dconst0)); 5333169699Skan return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL); 5334169699Skan } 5335169699Skan 5336169699Skan temp = expand_normal (arg); 5337169699Skan if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD) 5338169699Skan { 5339169699Skan imode = int_mode_for_mode (fmode); 5340169699Skan if (imode == BLKmode) 5341169699Skan return 0; 5342169699Skan temp = gen_lowpart (imode, temp); 5343169699Skan } 5344169699Skan else 5345169699Skan { 5346169699Skan imode = word_mode; 5347169699Skan /* Handle targets with different FP word orders. */ 5348169699Skan if (FLOAT_WORDS_BIG_ENDIAN) 5349169699Skan word = (GET_MODE_BITSIZE (fmode) - bitpos) / BITS_PER_WORD; 5350169699Skan else 5351169699Skan word = bitpos / BITS_PER_WORD; 5352169699Skan temp = operand_subword_force (temp, word, fmode); 5353169699Skan bitpos = bitpos % BITS_PER_WORD; 5354169699Skan } 5355169699Skan 5356169699Skan /* Force the intermediate word_mode (or narrower) result into a 5357169699Skan register. This avoids attempting to create paradoxical SUBREGs 5358169699Skan of floating point modes below. */ 5359169699Skan temp = force_reg (imode, temp); 5360169699Skan 5361169699Skan /* If the bitpos is within the "result mode" lowpart, the operation 5362169699Skan can be implement with a single bitwise AND. Otherwise, we need 5363169699Skan a right shift and an AND. */ 5364169699Skan 5365169699Skan if (bitpos < GET_MODE_BITSIZE (rmode)) 5366169699Skan { 5367169699Skan if (bitpos < HOST_BITS_PER_WIDE_INT) 5368169699Skan { 5369169699Skan hi = 0; 5370169699Skan lo = (HOST_WIDE_INT) 1 << bitpos; 5371169699Skan } 5372169699Skan else 5373169699Skan { 5374169699Skan hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT); 5375169699Skan lo = 0; 5376169699Skan } 5377169699Skan 5378169699Skan if (imode != rmode) 5379169699Skan temp = gen_lowpart (rmode, temp); 5380169699Skan temp = expand_binop (rmode, and_optab, temp, 5381169699Skan immed_double_const (lo, hi, rmode), 5382169699Skan NULL_RTX, 1, OPTAB_LIB_WIDEN); 5383169699Skan } 5384169699Skan else 5385169699Skan { 5386169699Skan /* Perform a logical right shift to place the signbit in the least 5387169699Skan significant bit, then truncate the result to the desired mode 5388169699Skan and mask just this bit. */ 5389169699Skan temp = expand_shift (RSHIFT_EXPR, imode, temp, 5390169699Skan build_int_cst (NULL_TREE, bitpos), NULL_RTX, 1); 5391169699Skan temp = gen_lowpart (rmode, temp); 5392169699Skan temp = expand_binop (rmode, and_optab, temp, const1_rtx, 5393169699Skan NULL_RTX, 1, OPTAB_LIB_WIDEN); 5394169699Skan } 5395169699Skan 5396169699Skan return temp; 5397169699Skan} 5398169699Skan 5399169699Skan/* Expand fork or exec calls. TARGET is the desired target of the 5400169699Skan call. ARGLIST is the list of arguments of the call. FN is the 5401169699Skan identificator of the actual function. IGNORE is nonzero if the 5402169699Skan value is to be ignored. */ 5403169699Skan 5404169699Skanstatic rtx 5405169699Skanexpand_builtin_fork_or_exec (tree fn, tree arglist, rtx target, int ignore) 5406169699Skan{ 5407169699Skan tree id, decl; 5408169699Skan tree call; 5409169699Skan 5410169699Skan /* If we are not profiling, just call the function. */ 5411169699Skan if (!profile_arc_flag) 5412169699Skan return NULL_RTX; 5413169699Skan 5414169699Skan /* Otherwise call the wrapper. This should be equivalent for the rest of 5415169699Skan compiler, so the code does not diverge, and the wrapper may run the 5416169699Skan code necessary for keeping the profiling sane. */ 5417169699Skan 5418169699Skan switch (DECL_FUNCTION_CODE (fn)) 5419169699Skan { 5420169699Skan case BUILT_IN_FORK: 5421169699Skan id = get_identifier ("__gcov_fork"); 5422169699Skan break; 5423169699Skan 5424169699Skan case BUILT_IN_EXECL: 5425169699Skan id = get_identifier ("__gcov_execl"); 5426169699Skan break; 5427169699Skan 5428169699Skan case BUILT_IN_EXECV: 5429169699Skan id = get_identifier ("__gcov_execv"); 5430169699Skan break; 5431169699Skan 5432169699Skan case BUILT_IN_EXECLP: 5433169699Skan id = get_identifier ("__gcov_execlp"); 5434169699Skan break; 5435169699Skan 5436169699Skan case BUILT_IN_EXECLE: 5437169699Skan id = get_identifier ("__gcov_execle"); 5438169699Skan break; 5439169699Skan 5440169699Skan case BUILT_IN_EXECVP: 5441169699Skan id = get_identifier ("__gcov_execvp"); 5442169699Skan break; 5443169699Skan 5444169699Skan case BUILT_IN_EXECVE: 5445169699Skan id = get_identifier ("__gcov_execve"); 5446169699Skan break; 5447169699Skan 5448169699Skan default: 5449169699Skan gcc_unreachable (); 5450169699Skan } 5451169699Skan 5452169699Skan decl = build_decl (FUNCTION_DECL, id, TREE_TYPE (fn)); 5453169699Skan DECL_EXTERNAL (decl) = 1; 5454169699Skan TREE_PUBLIC (decl) = 1; 5455169699Skan DECL_ARTIFICIAL (decl) = 1; 5456169699Skan TREE_NOTHROW (decl) = 1; 5457169699Skan DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; 5458169699Skan DECL_VISIBILITY_SPECIFIED (decl) = 1; 5459169699Skan call = build_function_call_expr (decl, arglist); 5460169699Skan 5461169699Skan return expand_call (call, target, ignore); 5462169699Skan} 5463169699Skan 546490075Sobrien 5465169699Skan/* Reconstitute a mode for a __sync intrinsic operation. Since the type of 5466169699Skan the pointer in these functions is void*, the tree optimizers may remove 5467169699Skan casts. The mode computed in expand_builtin isn't reliable either, due 5468169699Skan to __sync_bool_compare_and_swap. 5469169699Skan 5470169699Skan FCODE_DIFF should be fcode - base, where base is the FOO_1 code for the 5471169699Skan group of builtins. This gives us log2 of the mode size. */ 5472169699Skan 5473169699Skanstatic inline enum machine_mode 5474169699Skanget_builtin_sync_mode (int fcode_diff) 5475169699Skan{ 5476169699Skan /* The size is not negotiable, so ask not to get BLKmode in return 5477169699Skan if the target indicates that a smaller size would be better. */ 5478169699Skan return mode_for_size (BITS_PER_UNIT << fcode_diff, MODE_INT, 0); 5479169699Skan} 5480169699Skan 5481169699Skan/* Expand the memory expression LOC and return the appropriate memory operand 5482169699Skan for the builtin_sync operations. */ 5483169699Skan 5484169699Skanstatic rtx 5485169699Skanget_builtin_sync_mem (tree loc, enum machine_mode mode) 5486169699Skan{ 5487169699Skan rtx addr, mem; 5488169699Skan 5489169699Skan addr = expand_expr (loc, NULL, Pmode, EXPAND_SUM); 5490169699Skan 5491169699Skan /* Note that we explicitly do not want any alias information for this 5492169699Skan memory, so that we kill all other live memories. Otherwise we don't 5493169699Skan satisfy the full barrier semantics of the intrinsic. */ 5494169699Skan mem = validize_mem (gen_rtx_MEM (mode, addr)); 5495169699Skan 5496169699Skan set_mem_align (mem, get_pointer_alignment (loc, BIGGEST_ALIGNMENT)); 5497169699Skan set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER); 5498169699Skan MEM_VOLATILE_P (mem) = 1; 5499169699Skan 5500169699Skan return mem; 5501169699Skan} 5502169699Skan 5503169699Skan/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics. 5504169699Skan ARGLIST is the operands list to the function. CODE is the rtx code 5505169699Skan that corresponds to the arithmetic or logical operation from the name; 5506169699Skan an exception here is that NOT actually means NAND. TARGET is an optional 5507169699Skan place for us to store the results; AFTER is true if this is the 5508169699Skan fetch_and_xxx form. IGNORE is true if we don't actually care about 5509169699Skan the result of the operation at all. */ 5510169699Skan 5511169699Skanstatic rtx 5512169699Skanexpand_builtin_sync_operation (enum machine_mode mode, tree arglist, 5513169699Skan enum rtx_code code, bool after, 5514169699Skan rtx target, bool ignore) 5515169699Skan{ 5516169699Skan rtx val, mem; 5517169699Skan enum machine_mode old_mode; 5518169699Skan 5519169699Skan /* Expand the operands. */ 5520169699Skan mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode); 5521169699Skan 5522169699Skan arglist = TREE_CHAIN (arglist); 5523169699Skan val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); 5524169699Skan /* If VAL is promoted to a wider mode, convert it back to MODE. Take care 5525169699Skan of CONST_INTs, where we know the old_mode only from the call argument. */ 5526169699Skan old_mode = GET_MODE (val); 5527169699Skan if (old_mode == VOIDmode) 5528169699Skan old_mode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))); 5529169699Skan val = convert_modes (mode, old_mode, val, 1); 5530169699Skan 5531169699Skan if (ignore) 5532169699Skan return expand_sync_operation (mem, val, code); 5533169699Skan else 5534169699Skan return expand_sync_fetch_operation (mem, val, code, after, target); 5535169699Skan} 5536169699Skan 5537169699Skan/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap 5538169699Skan intrinsics. ARGLIST is the operands list to the function. IS_BOOL is 5539169699Skan true if this is the boolean form. TARGET is a place for us to store the 5540169699Skan results; this is NOT optional if IS_BOOL is true. */ 5541169699Skan 5542169699Skanstatic rtx 5543169699Skanexpand_builtin_compare_and_swap (enum machine_mode mode, tree arglist, 5544169699Skan bool is_bool, rtx target) 5545169699Skan{ 5546169699Skan rtx old_val, new_val, mem; 5547169699Skan enum machine_mode old_mode; 5548169699Skan 5549169699Skan /* Expand the operands. */ 5550169699Skan mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode); 5551169699Skan 5552169699Skan arglist = TREE_CHAIN (arglist); 5553169699Skan old_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); 5554169699Skan /* If VAL is promoted to a wider mode, convert it back to MODE. Take care 5555169699Skan of CONST_INTs, where we know the old_mode only from the call argument. */ 5556169699Skan old_mode = GET_MODE (old_val); 5557169699Skan if (old_mode == VOIDmode) 5558169699Skan old_mode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))); 5559169699Skan old_val = convert_modes (mode, old_mode, old_val, 1); 5560169699Skan 5561169699Skan arglist = TREE_CHAIN (arglist); 5562169699Skan new_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); 5563169699Skan /* If VAL is promoted to a wider mode, convert it back to MODE. Take care 5564169699Skan of CONST_INTs, where we know the old_mode only from the call argument. */ 5565169699Skan old_mode = GET_MODE (new_val); 5566169699Skan if (old_mode == VOIDmode) 5567169699Skan old_mode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))); 5568169699Skan new_val = convert_modes (mode, old_mode, new_val, 1); 5569169699Skan 5570169699Skan if (is_bool) 5571169699Skan return expand_bool_compare_and_swap (mem, old_val, new_val, target); 5572169699Skan else 5573169699Skan return expand_val_compare_and_swap (mem, old_val, new_val, target); 5574169699Skan} 5575169699Skan 5576169699Skan/* Expand the __sync_lock_test_and_set intrinsic. Note that the most 5577169699Skan general form is actually an atomic exchange, and some targets only 5578169699Skan support a reduced form with the second argument being a constant 1. 5579169699Skan ARGLIST is the operands list to the function; TARGET is an optional 5580169699Skan place for us to store the results. */ 5581169699Skan 5582169699Skanstatic rtx 5583169699Skanexpand_builtin_lock_test_and_set (enum machine_mode mode, tree arglist, 5584169699Skan rtx target) 5585169699Skan{ 5586169699Skan rtx val, mem; 5587169699Skan enum machine_mode old_mode; 5588169699Skan 5589169699Skan /* Expand the operands. */ 5590169699Skan mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode); 5591169699Skan 5592169699Skan arglist = TREE_CHAIN (arglist); 5593169699Skan val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); 5594169699Skan /* If VAL is promoted to a wider mode, convert it back to MODE. Take care 5595169699Skan of CONST_INTs, where we know the old_mode only from the call argument. */ 5596169699Skan old_mode = GET_MODE (val); 5597169699Skan if (old_mode == VOIDmode) 5598169699Skan old_mode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))); 5599169699Skan val = convert_modes (mode, old_mode, val, 1); 5600169699Skan 5601169699Skan return expand_sync_lock_test_and_set (mem, val, target); 5602169699Skan} 5603169699Skan 5604169699Skan/* Expand the __sync_synchronize intrinsic. */ 5605169699Skan 5606169699Skanstatic void 5607169699Skanexpand_builtin_synchronize (void) 5608169699Skan{ 5609169699Skan tree x; 5610169699Skan 5611169699Skan#ifdef HAVE_memory_barrier 5612169699Skan if (HAVE_memory_barrier) 5613169699Skan { 5614169699Skan emit_insn (gen_memory_barrier ()); 5615169699Skan return; 5616169699Skan } 5617169699Skan#endif 5618169699Skan 5619169699Skan /* If no explicit memory barrier instruction is available, create an 5620169699Skan empty asm stmt with a memory clobber. */ 5621169699Skan x = build4 (ASM_EXPR, void_type_node, build_string (0, ""), NULL, NULL, 5622169699Skan tree_cons (NULL, build_string (6, "memory"), NULL)); 5623169699Skan ASM_VOLATILE_P (x) = 1; 5624169699Skan expand_asm_expr (x); 5625169699Skan} 5626169699Skan 5627169699Skan/* Expand the __sync_lock_release intrinsic. ARGLIST is the operands list 5628169699Skan to the function. */ 5629169699Skan 5630169699Skanstatic void 5631169699Skanexpand_builtin_lock_release (enum machine_mode mode, tree arglist) 5632169699Skan{ 5633169699Skan enum insn_code icode; 5634169699Skan rtx mem, insn; 5635169699Skan rtx val = const0_rtx; 5636169699Skan 5637169699Skan /* Expand the operands. */ 5638169699Skan mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode); 5639169699Skan 5640169699Skan /* If there is an explicit operation in the md file, use it. */ 5641169699Skan icode = sync_lock_release[mode]; 5642169699Skan if (icode != CODE_FOR_nothing) 5643169699Skan { 5644169699Skan if (!insn_data[icode].operand[1].predicate (val, mode)) 5645169699Skan val = force_reg (mode, val); 5646169699Skan 5647169699Skan insn = GEN_FCN (icode) (mem, val); 5648169699Skan if (insn) 5649169699Skan { 5650169699Skan emit_insn (insn); 5651169699Skan return; 5652169699Skan } 5653169699Skan } 5654169699Skan 5655169699Skan /* Otherwise we can implement this operation by emitting a barrier 5656169699Skan followed by a store of zero. */ 5657169699Skan expand_builtin_synchronize (); 5658169699Skan emit_move_insn (mem, val); 5659169699Skan} 5660169699Skan 566190075Sobrien/* Expand an expression EXP that calls a built-in function, 566290075Sobrien with result going to TARGET if that's convenient 566390075Sobrien (and in mode MODE if that's convenient). 566490075Sobrien SUBTARGET may be used as the target for computing one of EXP's operands. 566590075Sobrien IGNORE is nonzero if the value is to be ignored. */ 566690075Sobrien 566790075Sobrienrtx 5668132727Skanexpand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, 5669132727Skan int ignore) 567090075Sobrien{ 5671132727Skan tree fndecl = get_callee_fndecl (exp); 567290075Sobrien tree arglist = TREE_OPERAND (exp, 1); 567390075Sobrien enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); 5674132727Skan enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp)); 567590075Sobrien 567690075Sobrien if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) 5677169699Skan return targetm.expand_builtin (exp, target, subtarget, mode, ignore); 567890075Sobrien 567990075Sobrien /* When not optimizing, generate calls to library functions for a certain 568090075Sobrien set of builtins. */ 5681132727Skan if (!optimize 5682169699Skan && !called_as_built_in (fndecl) 5683132727Skan && DECL_ASSEMBLER_NAME_SET_P (fndecl) 5684132727Skan && fcode != BUILT_IN_ALLOCA) 5685132727Skan return expand_call (exp, target, ignore); 568690075Sobrien 5687132727Skan /* The built-in function expanders test for target == const0_rtx 5688132727Skan to determine whether the function's result will be ignored. */ 5689132727Skan if (ignore) 5690132727Skan target = const0_rtx; 569190075Sobrien 5692132727Skan /* If the result of a pure or const built-in function is ignored, and 5693132727Skan none of its arguments are volatile, we can avoid expanding the 5694132727Skan built-in call and just evaluate the arguments for side-effects. */ 5695132727Skan if (target == const0_rtx 5696132727Skan && (DECL_IS_PURE (fndecl) || TREE_READONLY (fndecl))) 5697132727Skan { 5698132727Skan bool volatilep = false; 5699132727Skan tree arg; 5700132727Skan 5701132727Skan for (arg = arglist; arg; arg = TREE_CHAIN (arg)) 5702132727Skan if (TREE_THIS_VOLATILE (TREE_VALUE (arg))) 5703132727Skan { 5704132727Skan volatilep = true; 5705132727Skan break; 5706132727Skan } 5707132727Skan 5708132727Skan if (! volatilep) 5709132727Skan { 5710132727Skan for (arg = arglist; arg; arg = TREE_CHAIN (arg)) 5711132727Skan expand_expr (TREE_VALUE (arg), const0_rtx, 5712132727Skan VOIDmode, EXPAND_NORMAL); 5713132727Skan return const0_rtx; 5714132727Skan } 5715132727Skan } 5716132727Skan 571790075Sobrien switch (fcode) 571890075Sobrien { 5719169699Skan CASE_FLT_FN (BUILT_IN_FABS): 5720132727Skan target = expand_builtin_fabs (arglist, target, subtarget); 5721132727Skan if (target) 5722169699Skan return target; 5723132727Skan break; 572490075Sobrien 5725169699Skan CASE_FLT_FN (BUILT_IN_COPYSIGN): 5726169699Skan target = expand_builtin_copysign (arglist, target, subtarget); 5727169699Skan if (target) 5728169699Skan return target; 5729132727Skan break; 5730132727Skan 5731169699Skan /* Just do a normal library call if we were unable to fold 5732169699Skan the values. */ 5733169699Skan CASE_FLT_FN (BUILT_IN_CABS): 5734169699Skan break; 573590075Sobrien 5736169699Skan CASE_FLT_FN (BUILT_IN_EXP): 5737169699Skan CASE_FLT_FN (BUILT_IN_EXP10): 5738169699Skan CASE_FLT_FN (BUILT_IN_POW10): 5739169699Skan CASE_FLT_FN (BUILT_IN_EXP2): 5740169699Skan CASE_FLT_FN (BUILT_IN_EXPM1): 5741169699Skan CASE_FLT_FN (BUILT_IN_LOGB): 5742169699Skan CASE_FLT_FN (BUILT_IN_ILOGB): 5743169699Skan CASE_FLT_FN (BUILT_IN_LOG): 5744169699Skan CASE_FLT_FN (BUILT_IN_LOG10): 5745169699Skan CASE_FLT_FN (BUILT_IN_LOG2): 5746169699Skan CASE_FLT_FN (BUILT_IN_LOG1P): 5747169699Skan CASE_FLT_FN (BUILT_IN_TAN): 5748169699Skan CASE_FLT_FN (BUILT_IN_ASIN): 5749169699Skan CASE_FLT_FN (BUILT_IN_ACOS): 5750169699Skan CASE_FLT_FN (BUILT_IN_ATAN): 575190075Sobrien /* Treat these like sqrt only if unsafe math optimizations are allowed, 575290075Sobrien because of possible accuracy problems. */ 575390075Sobrien if (! flag_unsafe_math_optimizations) 575490075Sobrien break; 5755169699Skan CASE_FLT_FN (BUILT_IN_SQRT): 5756169699Skan CASE_FLT_FN (BUILT_IN_FLOOR): 5757169699Skan CASE_FLT_FN (BUILT_IN_CEIL): 5758169699Skan CASE_FLT_FN (BUILT_IN_TRUNC): 5759169699Skan CASE_FLT_FN (BUILT_IN_ROUND): 5760169699Skan CASE_FLT_FN (BUILT_IN_NEARBYINT): 5761169699Skan CASE_FLT_FN (BUILT_IN_RINT): 5762169699Skan CASE_FLT_FN (BUILT_IN_LRINT): 5763169699Skan CASE_FLT_FN (BUILT_IN_LLRINT): 576490075Sobrien target = expand_builtin_mathfn (exp, target, subtarget); 576590075Sobrien if (target) 576690075Sobrien return target; 576790075Sobrien break; 576890075Sobrien 5769169699Skan CASE_FLT_FN (BUILT_IN_LCEIL): 5770169699Skan CASE_FLT_FN (BUILT_IN_LLCEIL): 5771169699Skan CASE_FLT_FN (BUILT_IN_LFLOOR): 5772169699Skan CASE_FLT_FN (BUILT_IN_LLFLOOR): 5773169699Skan target = expand_builtin_int_roundingfn (exp, target, subtarget); 5774169699Skan if (target) 5775169699Skan return target; 5776169699Skan break; 5777169699Skan 5778169699Skan CASE_FLT_FN (BUILT_IN_POW): 5779132727Skan target = expand_builtin_pow (exp, target, subtarget); 5780132727Skan if (target) 5781132727Skan return target; 5782132727Skan break; 5783132727Skan 5784169699Skan CASE_FLT_FN (BUILT_IN_POWI): 5785169699Skan target = expand_builtin_powi (exp, target, subtarget); 5786169699Skan if (target) 5787169699Skan return target; 5788169699Skan break; 5789169699Skan 5790169699Skan CASE_FLT_FN (BUILT_IN_ATAN2): 5791169699Skan CASE_FLT_FN (BUILT_IN_LDEXP): 5792169699Skan CASE_FLT_FN (BUILT_IN_FMOD): 5793169699Skan CASE_FLT_FN (BUILT_IN_DREM): 5794132727Skan if (! flag_unsafe_math_optimizations) 5795132727Skan break; 5796132727Skan target = expand_builtin_mathfn_2 (exp, target, subtarget); 5797132727Skan if (target) 5798132727Skan return target; 5799132727Skan break; 5800132727Skan 5801169699Skan CASE_FLT_FN (BUILT_IN_SIN): 5802169699Skan CASE_FLT_FN (BUILT_IN_COS): 5803169699Skan if (! flag_unsafe_math_optimizations) 5804169699Skan break; 5805169699Skan target = expand_builtin_mathfn_3 (exp, target, subtarget); 5806169699Skan if (target) 5807169699Skan return target; 5808169699Skan break; 5809169699Skan 5810169699Skan CASE_FLT_FN (BUILT_IN_SINCOS): 5811169699Skan if (! flag_unsafe_math_optimizations) 5812169699Skan break; 5813169699Skan target = expand_builtin_sincos (exp); 5814169699Skan if (target) 5815169699Skan return target; 5816169699Skan break; 5817169699Skan 581890075Sobrien case BUILT_IN_APPLY_ARGS: 581990075Sobrien return expand_builtin_apply_args (); 582090075Sobrien 582190075Sobrien /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes 582290075Sobrien FUNCTION with a copy of the parameters described by 582390075Sobrien ARGUMENTS, and ARGSIZE. It returns a block of memory 582490075Sobrien allocated on the stack into which is stored all the registers 582590075Sobrien that might possibly be used for returning the result of a 582690075Sobrien function. ARGUMENTS is the value returned by 582790075Sobrien __builtin_apply_args. ARGSIZE is the number of bytes of 582890075Sobrien arguments that must be copied. ??? How should this value be 582990075Sobrien computed? We'll also need a safe worst case value for varargs 583090075Sobrien functions. */ 583190075Sobrien case BUILT_IN_APPLY: 583290075Sobrien if (!validate_arglist (arglist, POINTER_TYPE, 583390075Sobrien POINTER_TYPE, INTEGER_TYPE, VOID_TYPE) 583490075Sobrien && !validate_arglist (arglist, REFERENCE_TYPE, 583590075Sobrien POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 583690075Sobrien return const0_rtx; 583790075Sobrien else 583890075Sobrien { 583990075Sobrien int i; 584090075Sobrien tree t; 584190075Sobrien rtx ops[3]; 584290075Sobrien 584390075Sobrien for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++) 5844169699Skan ops[i] = expand_normal (TREE_VALUE (t)); 584590075Sobrien 584690075Sobrien return expand_builtin_apply (ops[0], ops[1], ops[2]); 584790075Sobrien } 584890075Sobrien 584990075Sobrien /* __builtin_return (RESULT) causes the function to return the 585090075Sobrien value described by RESULT. RESULT is address of the block of 585190075Sobrien memory returned by __builtin_apply. */ 585290075Sobrien case BUILT_IN_RETURN: 585390075Sobrien if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) 5854169699Skan expand_builtin_return (expand_normal (TREE_VALUE (arglist))); 585590075Sobrien return const0_rtx; 585690075Sobrien 585790075Sobrien case BUILT_IN_SAVEREGS: 585890075Sobrien return expand_builtin_saveregs (); 585990075Sobrien 586090075Sobrien case BUILT_IN_ARGS_INFO: 5861132727Skan return expand_builtin_args_info (arglist); 586290075Sobrien 586390075Sobrien /* Return the address of the first anonymous stack arg. */ 586490075Sobrien case BUILT_IN_NEXT_ARG: 5865169699Skan if (fold_builtin_next_arg (arglist)) 5866169699Skan return const0_rtx; 5867169699Skan return expand_builtin_next_arg (); 586890075Sobrien 586990075Sobrien case BUILT_IN_CLASSIFY_TYPE: 587090075Sobrien return expand_builtin_classify_type (arglist); 587190075Sobrien 587290075Sobrien case BUILT_IN_CONSTANT_P: 5873169699Skan return const0_rtx; 587490075Sobrien 587590075Sobrien case BUILT_IN_FRAME_ADDRESS: 587690075Sobrien case BUILT_IN_RETURN_ADDRESS: 5877132727Skan return expand_builtin_frame_address (fndecl, arglist); 587890075Sobrien 587990075Sobrien /* Returns the address of the area where the structure is returned. 588090075Sobrien 0 otherwise. */ 588190075Sobrien case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: 588290075Sobrien if (arglist != 0 5883117404Skan || ! AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))) 5884169699Skan || !MEM_P (DECL_RTL (DECL_RESULT (current_function_decl)))) 5885117404Skan return const0_rtx; 588690075Sobrien else 5887117404Skan return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0); 588890075Sobrien 588990075Sobrien case BUILT_IN_ALLOCA: 589090075Sobrien target = expand_builtin_alloca (arglist, target); 589190075Sobrien if (target) 589290075Sobrien return target; 589390075Sobrien break; 589490075Sobrien 5895169699Skan case BUILT_IN_STACK_SAVE: 5896169699Skan return expand_stack_save (); 5897169699Skan 5898169699Skan case BUILT_IN_STACK_RESTORE: 5899169699Skan expand_stack_restore (TREE_VALUE (arglist)); 5900169699Skan return const0_rtx; 5901169699Skan 5902259563Spfg case BUILT_IN_BSWAP32: 5903259563Spfg case BUILT_IN_BSWAP64: 5904259563Spfg target = expand_builtin_bswap (arglist, target, subtarget); 5905259563Spfg 5906259563Spfg if (target) 5907259563Spfg return target; 5908259563Spfg break; 5909259563Spfg 5910169699Skan CASE_INT_FN (BUILT_IN_FFS): 5911169699Skan case BUILT_IN_FFSIMAX: 5912132727Skan target = expand_builtin_unop (target_mode, arglist, target, 5913132727Skan subtarget, ffs_optab); 591490075Sobrien if (target) 591590075Sobrien return target; 591690075Sobrien break; 591790075Sobrien 5918169699Skan CASE_INT_FN (BUILT_IN_CLZ): 5919169699Skan case BUILT_IN_CLZIMAX: 5920132727Skan target = expand_builtin_unop (target_mode, arglist, target, 5921132727Skan subtarget, clz_optab); 5922132727Skan if (target) 5923132727Skan return target; 5924132727Skan break; 5925132727Skan 5926169699Skan CASE_INT_FN (BUILT_IN_CTZ): 5927169699Skan case BUILT_IN_CTZIMAX: 5928132727Skan target = expand_builtin_unop (target_mode, arglist, target, 5929132727Skan subtarget, ctz_optab); 5930132727Skan if (target) 5931132727Skan return target; 5932132727Skan break; 5933132727Skan 5934169699Skan CASE_INT_FN (BUILT_IN_POPCOUNT): 5935169699Skan case BUILT_IN_POPCOUNTIMAX: 5936132727Skan target = expand_builtin_unop (target_mode, arglist, target, 5937132727Skan subtarget, popcount_optab); 5938132727Skan if (target) 5939132727Skan return target; 5940132727Skan break; 5941132727Skan 5942169699Skan CASE_INT_FN (BUILT_IN_PARITY): 5943169699Skan case BUILT_IN_PARITYIMAX: 5944132727Skan target = expand_builtin_unop (target_mode, arglist, target, 5945132727Skan subtarget, parity_optab); 5946132727Skan if (target) 5947132727Skan return target; 5948132727Skan break; 5949132727Skan 595090075Sobrien case BUILT_IN_STRLEN: 5951132727Skan target = expand_builtin_strlen (arglist, target, target_mode); 595290075Sobrien if (target) 595390075Sobrien return target; 595490075Sobrien break; 595590075Sobrien 595690075Sobrien case BUILT_IN_STRCPY: 5957169699Skan target = expand_builtin_strcpy (fndecl, arglist, target, mode); 595890075Sobrien if (target) 595990075Sobrien return target; 596090075Sobrien break; 596190075Sobrien 596290075Sobrien case BUILT_IN_STRNCPY: 5963169699Skan target = expand_builtin_strncpy (exp, target, mode); 596490075Sobrien if (target) 596590075Sobrien return target; 596690075Sobrien break; 596790075Sobrien 5968132727Skan case BUILT_IN_STPCPY: 5969169699Skan target = expand_builtin_stpcpy (exp, target, mode); 5970132727Skan if (target) 5971132727Skan return target; 5972132727Skan break; 5973132727Skan 597490075Sobrien case BUILT_IN_STRCAT: 5975169699Skan target = expand_builtin_strcat (fndecl, arglist, target, mode); 597690075Sobrien if (target) 597790075Sobrien return target; 597890075Sobrien break; 597990075Sobrien 598090075Sobrien case BUILT_IN_STRNCAT: 598190075Sobrien target = expand_builtin_strncat (arglist, target, mode); 598290075Sobrien if (target) 598390075Sobrien return target; 598490075Sobrien break; 598590075Sobrien 598690075Sobrien case BUILT_IN_STRSPN: 598790075Sobrien target = expand_builtin_strspn (arglist, target, mode); 598890075Sobrien if (target) 598990075Sobrien return target; 599090075Sobrien break; 599190075Sobrien 599290075Sobrien case BUILT_IN_STRCSPN: 599390075Sobrien target = expand_builtin_strcspn (arglist, target, mode); 599490075Sobrien if (target) 599590075Sobrien return target; 599690075Sobrien break; 599790075Sobrien 599890075Sobrien case BUILT_IN_STRSTR: 5999169699Skan target = expand_builtin_strstr (arglist, TREE_TYPE (exp), target, mode); 600090075Sobrien if (target) 600190075Sobrien return target; 600290075Sobrien break; 600390075Sobrien 600490075Sobrien case BUILT_IN_STRPBRK: 6005169699Skan target = expand_builtin_strpbrk (arglist, TREE_TYPE (exp), target, mode); 600690075Sobrien if (target) 600790075Sobrien return target; 600890075Sobrien break; 600990075Sobrien 601090075Sobrien case BUILT_IN_INDEX: 601190075Sobrien case BUILT_IN_STRCHR: 6012169699Skan target = expand_builtin_strchr (arglist, TREE_TYPE (exp), target, mode); 601390075Sobrien if (target) 601490075Sobrien return target; 601590075Sobrien break; 601690075Sobrien 601790075Sobrien case BUILT_IN_RINDEX: 601890075Sobrien case BUILT_IN_STRRCHR: 6019169699Skan target = expand_builtin_strrchr (arglist, TREE_TYPE (exp), target, mode); 602090075Sobrien if (target) 602190075Sobrien return target; 602290075Sobrien break; 602390075Sobrien 602490075Sobrien case BUILT_IN_MEMCPY: 6025169699Skan target = expand_builtin_memcpy (exp, target, mode); 602690075Sobrien if (target) 602790075Sobrien return target; 602890075Sobrien break; 602990075Sobrien 6030132727Skan case BUILT_IN_MEMPCPY: 6031169699Skan target = expand_builtin_mempcpy (arglist, TREE_TYPE (exp), target, mode, /*endp=*/ 1); 6032132727Skan if (target) 6033132727Skan return target; 6034132727Skan break; 6035132727Skan 6036132727Skan case BUILT_IN_MEMMOVE: 6037169699Skan target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target, 6038169699Skan mode, exp); 6039132727Skan if (target) 6040132727Skan return target; 6041132727Skan break; 6042132727Skan 6043132727Skan case BUILT_IN_BCOPY: 6044169699Skan target = expand_builtin_bcopy (exp); 6045132727Skan if (target) 6046132727Skan return target; 6047132727Skan break; 6048132727Skan 604990075Sobrien case BUILT_IN_MEMSET: 6050169699Skan target = expand_builtin_memset (arglist, target, mode, exp); 605190075Sobrien if (target) 605290075Sobrien return target; 605390075Sobrien break; 605490075Sobrien 605590075Sobrien case BUILT_IN_BZERO: 6056169699Skan target = expand_builtin_bzero (exp); 605790075Sobrien if (target) 605890075Sobrien return target; 605990075Sobrien break; 606090075Sobrien 606190075Sobrien case BUILT_IN_STRCMP: 606290075Sobrien target = expand_builtin_strcmp (exp, target, mode); 606390075Sobrien if (target) 606490075Sobrien return target; 606590075Sobrien break; 606690075Sobrien 606790075Sobrien case BUILT_IN_STRNCMP: 606890075Sobrien target = expand_builtin_strncmp (exp, target, mode); 606990075Sobrien if (target) 607090075Sobrien return target; 607190075Sobrien break; 607290075Sobrien 607390075Sobrien case BUILT_IN_BCMP: 607490075Sobrien case BUILT_IN_MEMCMP: 607590075Sobrien target = expand_builtin_memcmp (exp, arglist, target, mode); 607690075Sobrien if (target) 607790075Sobrien return target; 607890075Sobrien break; 607990075Sobrien 608090075Sobrien case BUILT_IN_SETJMP: 6081169699Skan /* This should have been lowered to the builtins below. */ 6082169699Skan gcc_unreachable (); 6083169699Skan 6084169699Skan case BUILT_IN_SETJMP_SETUP: 6085169699Skan /* __builtin_setjmp_setup is passed a pointer to an array of five words 6086169699Skan and the receiver label. */ 6087169699Skan if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 6088169699Skan { 6089169699Skan rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget, 6090169699Skan VOIDmode, EXPAND_NORMAL); 6091169699Skan tree label = TREE_OPERAND (TREE_VALUE (TREE_CHAIN (arglist)), 0); 6092169699Skan rtx label_r = label_rtx (label); 6093169699Skan 6094169699Skan /* This is copied from the handling of non-local gotos. */ 6095169699Skan expand_builtin_setjmp_setup (buf_addr, label_r); 6096169699Skan nonlocal_goto_handler_labels 6097169699Skan = gen_rtx_EXPR_LIST (VOIDmode, label_r, 6098169699Skan nonlocal_goto_handler_labels); 6099169699Skan /* ??? Do not let expand_label treat us as such since we would 6100169699Skan not want to be both on the list of non-local labels and on 6101169699Skan the list of forced labels. */ 6102169699Skan FORCED_LABEL (label) = 0; 6103169699Skan return const0_rtx; 6104169699Skan } 610590075Sobrien break; 610690075Sobrien 6107169699Skan case BUILT_IN_SETJMP_DISPATCHER: 6108169699Skan /* __builtin_setjmp_dispatcher is passed the dispatcher label. */ 6109169699Skan if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) 6110169699Skan { 6111169699Skan tree label = TREE_OPERAND (TREE_VALUE (arglist), 0); 6112169699Skan rtx label_r = label_rtx (label); 6113169699Skan 6114169699Skan /* Remove the dispatcher label from the list of non-local labels 6115169699Skan since the receiver labels have been added to it above. */ 6116169699Skan remove_node_from_expr_list (label_r, &nonlocal_goto_handler_labels); 6117169699Skan return const0_rtx; 6118169699Skan } 6119169699Skan break; 6120169699Skan 6121169699Skan case BUILT_IN_SETJMP_RECEIVER: 6122169699Skan /* __builtin_setjmp_receiver is passed the receiver label. */ 6123169699Skan if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) 6124169699Skan { 6125169699Skan tree label = TREE_OPERAND (TREE_VALUE (arglist), 0); 6126169699Skan rtx label_r = label_rtx (label); 6127169699Skan 6128169699Skan expand_builtin_setjmp_receiver (label_r); 6129169699Skan return const0_rtx; 6130169699Skan } 6131169699Skan break; 6132169699Skan 613390075Sobrien /* __builtin_longjmp is passed a pointer to an array of five words. 613490075Sobrien It's similar to the C library longjmp function but works with 613590075Sobrien __builtin_setjmp above. */ 613690075Sobrien case BUILT_IN_LONGJMP: 6137169699Skan if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 613890075Sobrien { 613990075Sobrien rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget, 6140169699Skan VOIDmode, EXPAND_NORMAL); 6141169699Skan rtx value = expand_normal (TREE_VALUE (TREE_CHAIN (arglist))); 614290075Sobrien 614390075Sobrien if (value != const1_rtx) 614490075Sobrien { 6145169699Skan error ("%<__builtin_longjmp%> second argument must be 1"); 614690075Sobrien return const0_rtx; 614790075Sobrien } 614890075Sobrien 614990075Sobrien expand_builtin_longjmp (buf_addr, value); 615090075Sobrien return const0_rtx; 615190075Sobrien } 6152169699Skan break; 615390075Sobrien 6154169699Skan case BUILT_IN_NONLOCAL_GOTO: 6155169699Skan target = expand_builtin_nonlocal_goto (arglist); 6156169699Skan if (target) 6157169699Skan return target; 6158169699Skan break; 6159169699Skan 6160169699Skan /* This updates the setjmp buffer that is its argument with the value 6161169699Skan of the current stack pointer. */ 6162169699Skan case BUILT_IN_UPDATE_SETJMP_BUF: 6163169699Skan if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) 6164169699Skan { 6165169699Skan rtx buf_addr 6166169699Skan = expand_normal (TREE_VALUE (arglist)); 6167169699Skan 6168169699Skan expand_builtin_update_setjmp_buf (buf_addr); 6169169699Skan return const0_rtx; 6170169699Skan } 6171169699Skan break; 6172169699Skan 617390075Sobrien case BUILT_IN_TRAP: 617496263Sobrien expand_builtin_trap (); 617590075Sobrien return const0_rtx; 617690075Sobrien 6177132727Skan case BUILT_IN_PRINTF: 6178169699Skan target = expand_builtin_printf (exp, target, mode, false); 6179132727Skan if (target) 6180132727Skan return target; 6181132727Skan break; 6182132727Skan 6183132727Skan case BUILT_IN_PRINTF_UNLOCKED: 6184169699Skan target = expand_builtin_printf (exp, target, mode, true); 6185132727Skan if (target) 6186132727Skan return target; 6187132727Skan break; 6188132727Skan 618990075Sobrien case BUILT_IN_FPUTS: 6190132727Skan target = expand_builtin_fputs (arglist, target, false); 619190075Sobrien if (target) 619290075Sobrien return target; 619390075Sobrien break; 619490075Sobrien case BUILT_IN_FPUTS_UNLOCKED: 6195132727Skan target = expand_builtin_fputs (arglist, target, true); 619690075Sobrien if (target) 619790075Sobrien return target; 619890075Sobrien break; 619990075Sobrien 6200132727Skan case BUILT_IN_FPRINTF: 6201169699Skan target = expand_builtin_fprintf (exp, target, mode, false); 6202132727Skan if (target) 6203132727Skan return target; 6204132727Skan break; 6205132727Skan 6206132727Skan case BUILT_IN_FPRINTF_UNLOCKED: 6207169699Skan target = expand_builtin_fprintf (exp, target, mode, true); 6208132727Skan if (target) 6209132727Skan return target; 6210132727Skan break; 6211132727Skan 6212132727Skan case BUILT_IN_SPRINTF: 6213132727Skan target = expand_builtin_sprintf (arglist, target, mode); 6214132727Skan if (target) 6215132727Skan return target; 6216132727Skan break; 6217132727Skan 6218169699Skan CASE_FLT_FN (BUILT_IN_SIGNBIT): 6219169699Skan target = expand_builtin_signbit (exp, target); 6220169699Skan if (target) 6221169699Skan return target; 6222169699Skan break; 6223169699Skan 622490075Sobrien /* Various hooks for the DWARF 2 __throw routine. */ 622590075Sobrien case BUILT_IN_UNWIND_INIT: 622690075Sobrien expand_builtin_unwind_init (); 622790075Sobrien return const0_rtx; 622890075Sobrien case BUILT_IN_DWARF_CFA: 622990075Sobrien return virtual_cfa_rtx; 623090075Sobrien#ifdef DWARF2_UNWIND_INFO 6231117404Skan case BUILT_IN_DWARF_SP_COLUMN: 6232117404Skan return expand_builtin_dwarf_sp_column (); 623390075Sobrien case BUILT_IN_INIT_DWARF_REG_SIZES: 623490075Sobrien expand_builtin_init_dwarf_reg_sizes (TREE_VALUE (arglist)); 623590075Sobrien return const0_rtx; 623690075Sobrien#endif 623790075Sobrien case BUILT_IN_FROB_RETURN_ADDR: 623890075Sobrien return expand_builtin_frob_return_addr (TREE_VALUE (arglist)); 623990075Sobrien case BUILT_IN_EXTRACT_RETURN_ADDR: 624090075Sobrien return expand_builtin_extract_return_addr (TREE_VALUE (arglist)); 624190075Sobrien case BUILT_IN_EH_RETURN: 624290075Sobrien expand_builtin_eh_return (TREE_VALUE (arglist), 624390075Sobrien TREE_VALUE (TREE_CHAIN (arglist))); 624490075Sobrien return const0_rtx; 624590075Sobrien#ifdef EH_RETURN_DATA_REGNO 624690075Sobrien case BUILT_IN_EH_RETURN_DATA_REGNO: 624790075Sobrien return expand_builtin_eh_return_data_regno (arglist); 624890075Sobrien#endif 6249132727Skan case BUILT_IN_EXTEND_POINTER: 6250132727Skan return expand_builtin_extend_pointer (TREE_VALUE (arglist)); 6251132727Skan 6252117404Skan case BUILT_IN_VA_START: 625390075Sobrien case BUILT_IN_STDARG_START: 6254117404Skan return expand_builtin_va_start (arglist); 625590075Sobrien case BUILT_IN_VA_END: 625690075Sobrien return expand_builtin_va_end (arglist); 625790075Sobrien case BUILT_IN_VA_COPY: 625890075Sobrien return expand_builtin_va_copy (arglist); 625990075Sobrien case BUILT_IN_EXPECT: 626090075Sobrien return expand_builtin_expect (arglist, target); 626190075Sobrien case BUILT_IN_PREFETCH: 626290075Sobrien expand_builtin_prefetch (arglist); 626390075Sobrien return const0_rtx; 626490075Sobrien 6265169699Skan case BUILT_IN_PROFILE_FUNC_ENTER: 6266169699Skan return expand_builtin_profile_func (false); 6267169699Skan case BUILT_IN_PROFILE_FUNC_EXIT: 6268169699Skan return expand_builtin_profile_func (true); 626990075Sobrien 6270169699Skan case BUILT_IN_INIT_TRAMPOLINE: 6271169699Skan return expand_builtin_init_trampoline (arglist); 6272169699Skan case BUILT_IN_ADJUST_TRAMPOLINE: 6273169699Skan return expand_builtin_adjust_trampoline (arglist); 6274169699Skan 6275169699Skan case BUILT_IN_FORK: 6276169699Skan case BUILT_IN_EXECL: 6277169699Skan case BUILT_IN_EXECV: 6278169699Skan case BUILT_IN_EXECLP: 6279169699Skan case BUILT_IN_EXECLE: 6280169699Skan case BUILT_IN_EXECVP: 6281169699Skan case BUILT_IN_EXECVE: 6282169699Skan target = expand_builtin_fork_or_exec (fndecl, arglist, target, ignore); 6283169699Skan if (target) 6284169699Skan return target; 6285169699Skan break; 6286169699Skan 6287169699Skan case BUILT_IN_FETCH_AND_ADD_1: 6288169699Skan case BUILT_IN_FETCH_AND_ADD_2: 6289169699Skan case BUILT_IN_FETCH_AND_ADD_4: 6290169699Skan case BUILT_IN_FETCH_AND_ADD_8: 6291169699Skan case BUILT_IN_FETCH_AND_ADD_16: 6292169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_ADD_1); 6293169699Skan target = expand_builtin_sync_operation (mode, arglist, PLUS, 6294169699Skan false, target, ignore); 6295169699Skan if (target) 6296169699Skan return target; 6297169699Skan break; 6298169699Skan 6299169699Skan case BUILT_IN_FETCH_AND_SUB_1: 6300169699Skan case BUILT_IN_FETCH_AND_SUB_2: 6301169699Skan case BUILT_IN_FETCH_AND_SUB_4: 6302169699Skan case BUILT_IN_FETCH_AND_SUB_8: 6303169699Skan case BUILT_IN_FETCH_AND_SUB_16: 6304169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_SUB_1); 6305169699Skan target = expand_builtin_sync_operation (mode, arglist, MINUS, 6306169699Skan false, target, ignore); 6307169699Skan if (target) 6308169699Skan return target; 6309169699Skan break; 6310169699Skan 6311169699Skan case BUILT_IN_FETCH_AND_OR_1: 6312169699Skan case BUILT_IN_FETCH_AND_OR_2: 6313169699Skan case BUILT_IN_FETCH_AND_OR_4: 6314169699Skan case BUILT_IN_FETCH_AND_OR_8: 6315169699Skan case BUILT_IN_FETCH_AND_OR_16: 6316169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_OR_1); 6317169699Skan target = expand_builtin_sync_operation (mode, arglist, IOR, 6318169699Skan false, target, ignore); 6319169699Skan if (target) 6320169699Skan return target; 6321169699Skan break; 6322169699Skan 6323169699Skan case BUILT_IN_FETCH_AND_AND_1: 6324169699Skan case BUILT_IN_FETCH_AND_AND_2: 6325169699Skan case BUILT_IN_FETCH_AND_AND_4: 6326169699Skan case BUILT_IN_FETCH_AND_AND_8: 6327169699Skan case BUILT_IN_FETCH_AND_AND_16: 6328169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_AND_1); 6329169699Skan target = expand_builtin_sync_operation (mode, arglist, AND, 6330169699Skan false, target, ignore); 6331169699Skan if (target) 6332169699Skan return target; 6333169699Skan break; 6334169699Skan 6335169699Skan case BUILT_IN_FETCH_AND_XOR_1: 6336169699Skan case BUILT_IN_FETCH_AND_XOR_2: 6337169699Skan case BUILT_IN_FETCH_AND_XOR_4: 6338169699Skan case BUILT_IN_FETCH_AND_XOR_8: 6339169699Skan case BUILT_IN_FETCH_AND_XOR_16: 6340169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_XOR_1); 6341169699Skan target = expand_builtin_sync_operation (mode, arglist, XOR, 6342169699Skan false, target, ignore); 6343169699Skan if (target) 6344169699Skan return target; 6345169699Skan break; 6346169699Skan 6347169699Skan case BUILT_IN_FETCH_AND_NAND_1: 6348169699Skan case BUILT_IN_FETCH_AND_NAND_2: 6349169699Skan case BUILT_IN_FETCH_AND_NAND_4: 6350169699Skan case BUILT_IN_FETCH_AND_NAND_8: 6351169699Skan case BUILT_IN_FETCH_AND_NAND_16: 6352169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_NAND_1); 6353169699Skan target = expand_builtin_sync_operation (mode, arglist, NOT, 6354169699Skan false, target, ignore); 6355169699Skan if (target) 6356169699Skan return target; 6357169699Skan break; 6358169699Skan 6359169699Skan case BUILT_IN_ADD_AND_FETCH_1: 6360169699Skan case BUILT_IN_ADD_AND_FETCH_2: 6361169699Skan case BUILT_IN_ADD_AND_FETCH_4: 6362169699Skan case BUILT_IN_ADD_AND_FETCH_8: 6363169699Skan case BUILT_IN_ADD_AND_FETCH_16: 6364169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_ADD_AND_FETCH_1); 6365169699Skan target = expand_builtin_sync_operation (mode, arglist, PLUS, 6366169699Skan true, target, ignore); 6367169699Skan if (target) 6368169699Skan return target; 6369169699Skan break; 6370169699Skan 6371169699Skan case BUILT_IN_SUB_AND_FETCH_1: 6372169699Skan case BUILT_IN_SUB_AND_FETCH_2: 6373169699Skan case BUILT_IN_SUB_AND_FETCH_4: 6374169699Skan case BUILT_IN_SUB_AND_FETCH_8: 6375169699Skan case BUILT_IN_SUB_AND_FETCH_16: 6376169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_SUB_AND_FETCH_1); 6377169699Skan target = expand_builtin_sync_operation (mode, arglist, MINUS, 6378169699Skan true, target, ignore); 6379169699Skan if (target) 6380169699Skan return target; 6381169699Skan break; 6382169699Skan 6383169699Skan case BUILT_IN_OR_AND_FETCH_1: 6384169699Skan case BUILT_IN_OR_AND_FETCH_2: 6385169699Skan case BUILT_IN_OR_AND_FETCH_4: 6386169699Skan case BUILT_IN_OR_AND_FETCH_8: 6387169699Skan case BUILT_IN_OR_AND_FETCH_16: 6388169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_OR_AND_FETCH_1); 6389169699Skan target = expand_builtin_sync_operation (mode, arglist, IOR, 6390169699Skan true, target, ignore); 6391169699Skan if (target) 6392169699Skan return target; 6393169699Skan break; 6394169699Skan 6395169699Skan case BUILT_IN_AND_AND_FETCH_1: 6396169699Skan case BUILT_IN_AND_AND_FETCH_2: 6397169699Skan case BUILT_IN_AND_AND_FETCH_4: 6398169699Skan case BUILT_IN_AND_AND_FETCH_8: 6399169699Skan case BUILT_IN_AND_AND_FETCH_16: 6400169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_AND_AND_FETCH_1); 6401169699Skan target = expand_builtin_sync_operation (mode, arglist, AND, 6402169699Skan true, target, ignore); 6403169699Skan if (target) 6404169699Skan return target; 6405169699Skan break; 6406169699Skan 6407169699Skan case BUILT_IN_XOR_AND_FETCH_1: 6408169699Skan case BUILT_IN_XOR_AND_FETCH_2: 6409169699Skan case BUILT_IN_XOR_AND_FETCH_4: 6410169699Skan case BUILT_IN_XOR_AND_FETCH_8: 6411169699Skan case BUILT_IN_XOR_AND_FETCH_16: 6412169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_XOR_AND_FETCH_1); 6413169699Skan target = expand_builtin_sync_operation (mode, arglist, XOR, 6414169699Skan true, target, ignore); 6415169699Skan if (target) 6416169699Skan return target; 6417169699Skan break; 6418169699Skan 6419169699Skan case BUILT_IN_NAND_AND_FETCH_1: 6420169699Skan case BUILT_IN_NAND_AND_FETCH_2: 6421169699Skan case BUILT_IN_NAND_AND_FETCH_4: 6422169699Skan case BUILT_IN_NAND_AND_FETCH_8: 6423169699Skan case BUILT_IN_NAND_AND_FETCH_16: 6424169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_NAND_AND_FETCH_1); 6425169699Skan target = expand_builtin_sync_operation (mode, arglist, NOT, 6426169699Skan true, target, ignore); 6427169699Skan if (target) 6428169699Skan return target; 6429169699Skan break; 6430169699Skan 6431169699Skan case BUILT_IN_BOOL_COMPARE_AND_SWAP_1: 6432169699Skan case BUILT_IN_BOOL_COMPARE_AND_SWAP_2: 6433169699Skan case BUILT_IN_BOOL_COMPARE_AND_SWAP_4: 6434169699Skan case BUILT_IN_BOOL_COMPARE_AND_SWAP_8: 6435169699Skan case BUILT_IN_BOOL_COMPARE_AND_SWAP_16: 6436169699Skan if (mode == VOIDmode) 6437169699Skan mode = TYPE_MODE (boolean_type_node); 6438169699Skan if (!target || !register_operand (target, mode)) 6439169699Skan target = gen_reg_rtx (mode); 6440169699Skan 6441169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_BOOL_COMPARE_AND_SWAP_1); 6442169699Skan target = expand_builtin_compare_and_swap (mode, arglist, true, target); 6443169699Skan if (target) 6444169699Skan return target; 6445169699Skan break; 6446169699Skan 6447169699Skan case BUILT_IN_VAL_COMPARE_AND_SWAP_1: 6448169699Skan case BUILT_IN_VAL_COMPARE_AND_SWAP_2: 6449169699Skan case BUILT_IN_VAL_COMPARE_AND_SWAP_4: 6450169699Skan case BUILT_IN_VAL_COMPARE_AND_SWAP_8: 6451169699Skan case BUILT_IN_VAL_COMPARE_AND_SWAP_16: 6452169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_VAL_COMPARE_AND_SWAP_1); 6453169699Skan target = expand_builtin_compare_and_swap (mode, arglist, false, target); 6454169699Skan if (target) 6455169699Skan return target; 6456169699Skan break; 6457169699Skan 6458169699Skan case BUILT_IN_LOCK_TEST_AND_SET_1: 6459169699Skan case BUILT_IN_LOCK_TEST_AND_SET_2: 6460169699Skan case BUILT_IN_LOCK_TEST_AND_SET_4: 6461169699Skan case BUILT_IN_LOCK_TEST_AND_SET_8: 6462169699Skan case BUILT_IN_LOCK_TEST_AND_SET_16: 6463169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_TEST_AND_SET_1); 6464169699Skan target = expand_builtin_lock_test_and_set (mode, arglist, target); 6465169699Skan if (target) 6466169699Skan return target; 6467169699Skan break; 6468169699Skan 6469169699Skan case BUILT_IN_LOCK_RELEASE_1: 6470169699Skan case BUILT_IN_LOCK_RELEASE_2: 6471169699Skan case BUILT_IN_LOCK_RELEASE_4: 6472169699Skan case BUILT_IN_LOCK_RELEASE_8: 6473169699Skan case BUILT_IN_LOCK_RELEASE_16: 6474169699Skan mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_RELEASE_1); 6475169699Skan expand_builtin_lock_release (mode, arglist); 6476169699Skan return const0_rtx; 6477169699Skan 6478169699Skan case BUILT_IN_SYNCHRONIZE: 6479169699Skan expand_builtin_synchronize (); 6480169699Skan return const0_rtx; 6481169699Skan 6482169699Skan case BUILT_IN_OBJECT_SIZE: 6483169699Skan return expand_builtin_object_size (exp); 6484169699Skan 6485169699Skan case BUILT_IN_MEMCPY_CHK: 6486169699Skan case BUILT_IN_MEMPCPY_CHK: 6487169699Skan case BUILT_IN_MEMMOVE_CHK: 6488169699Skan case BUILT_IN_MEMSET_CHK: 6489169699Skan target = expand_builtin_memory_chk (exp, target, mode, fcode); 6490169699Skan if (target) 6491169699Skan return target; 6492169699Skan break; 6493169699Skan 6494169699Skan case BUILT_IN_STRCPY_CHK: 6495169699Skan case BUILT_IN_STPCPY_CHK: 6496169699Skan case BUILT_IN_STRNCPY_CHK: 6497169699Skan case BUILT_IN_STRCAT_CHK: 6498169699Skan case BUILT_IN_SNPRINTF_CHK: 6499169699Skan case BUILT_IN_VSNPRINTF_CHK: 6500169699Skan maybe_emit_chk_warning (exp, fcode); 6501169699Skan break; 6502169699Skan 6503169699Skan case BUILT_IN_SPRINTF_CHK: 6504169699Skan case BUILT_IN_VSPRINTF_CHK: 6505169699Skan maybe_emit_sprintf_chk_warning (exp, fcode); 6506169699Skan break; 6507169699Skan 6508117404Skan default: /* just do library call, if unknown builtin */ 6509169699Skan break; 651090075Sobrien } 651190075Sobrien 651290075Sobrien /* The switch statement above can drop through to cause the function 651390075Sobrien to be called normally. */ 651490075Sobrien return expand_call (exp, target, ignore); 651590075Sobrien} 651690075Sobrien 6517132727Skan/* Determine whether a tree node represents a call to a built-in 6518132727Skan function. If the tree T is a call to a built-in function with 6519132727Skan the right number of arguments of the appropriate types, return 6520132727Skan the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT. 6521132727Skan Otherwise the return value is END_BUILTINS. */ 6522132727Skan 6523132727Skanenum built_in_function 6524132727Skanbuiltin_mathfn_code (tree t) 6525132727Skan{ 6526132727Skan tree fndecl, arglist, parmlist; 6527132727Skan tree argtype, parmtype; 6528132727Skan 6529132727Skan if (TREE_CODE (t) != CALL_EXPR 6530132727Skan || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR) 6531132727Skan return END_BUILTINS; 6532132727Skan 6533132727Skan fndecl = get_callee_fndecl (t); 6534132727Skan if (fndecl == NULL_TREE 6535132727Skan || TREE_CODE (fndecl) != FUNCTION_DECL 6536132727Skan || ! DECL_BUILT_IN (fndecl) 6537132727Skan || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) 6538132727Skan return END_BUILTINS; 6539132727Skan 6540132727Skan arglist = TREE_OPERAND (t, 1); 6541132727Skan parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); 6542132727Skan for (; parmlist; parmlist = TREE_CHAIN (parmlist)) 6543132727Skan { 6544132727Skan /* If a function doesn't take a variable number of arguments, 6545132727Skan the last element in the list will have type `void'. */ 6546132727Skan parmtype = TREE_VALUE (parmlist); 6547132727Skan if (VOID_TYPE_P (parmtype)) 6548132727Skan { 6549132727Skan if (arglist) 6550132727Skan return END_BUILTINS; 6551132727Skan return DECL_FUNCTION_CODE (fndecl); 6552132727Skan } 6553132727Skan 6554132727Skan if (! arglist) 6555132727Skan return END_BUILTINS; 6556132727Skan 6557132727Skan argtype = TREE_TYPE (TREE_VALUE (arglist)); 6558132727Skan 6559132727Skan if (SCALAR_FLOAT_TYPE_P (parmtype)) 6560132727Skan { 6561132727Skan if (! SCALAR_FLOAT_TYPE_P (argtype)) 6562132727Skan return END_BUILTINS; 6563132727Skan } 6564132727Skan else if (COMPLEX_FLOAT_TYPE_P (parmtype)) 6565132727Skan { 6566132727Skan if (! COMPLEX_FLOAT_TYPE_P (argtype)) 6567132727Skan return END_BUILTINS; 6568132727Skan } 6569132727Skan else if (POINTER_TYPE_P (parmtype)) 6570132727Skan { 6571132727Skan if (! POINTER_TYPE_P (argtype)) 6572132727Skan return END_BUILTINS; 6573132727Skan } 6574132727Skan else if (INTEGRAL_TYPE_P (parmtype)) 6575132727Skan { 6576132727Skan if (! INTEGRAL_TYPE_P (argtype)) 6577132727Skan return END_BUILTINS; 6578132727Skan } 6579132727Skan else 6580132727Skan return END_BUILTINS; 6581132727Skan 6582132727Skan arglist = TREE_CHAIN (arglist); 6583132727Skan } 6584132727Skan 6585132727Skan /* Variable-length argument list. */ 6586132727Skan return DECL_FUNCTION_CODE (fndecl); 6587132727Skan} 6588132727Skan 658990075Sobrien/* Fold a call to __builtin_constant_p, if we know it will evaluate to a 659090075Sobrien constant. ARGLIST is the argument list of the call. */ 659190075Sobrien 659290075Sobrienstatic tree 6593132727Skanfold_builtin_constant_p (tree arglist) 659490075Sobrien{ 659590075Sobrien if (arglist == 0) 659690075Sobrien return 0; 659790075Sobrien 659890075Sobrien arglist = TREE_VALUE (arglist); 659990075Sobrien 660090075Sobrien /* We return 1 for a numeric type that's known to be a constant 660190075Sobrien value at compile-time or for an aggregate type that's a 660290075Sobrien literal constant. */ 660390075Sobrien STRIP_NOPS (arglist); 660490075Sobrien 660590075Sobrien /* If we know this is a constant, emit the constant of one. */ 6606169699Skan if (CONSTANT_CLASS_P (arglist) 660790075Sobrien || (TREE_CODE (arglist) == CONSTRUCTOR 6608169699Skan && TREE_CONSTANT (arglist))) 660990075Sobrien return integer_one_node; 6610169699Skan if (TREE_CODE (arglist) == ADDR_EXPR) 6611169699Skan { 6612169699Skan tree op = TREE_OPERAND (arglist, 0); 6613169699Skan if (TREE_CODE (op) == STRING_CST 6614169699Skan || (TREE_CODE (op) == ARRAY_REF 6615169699Skan && integer_zerop (TREE_OPERAND (op, 1)) 6616169699Skan && TREE_CODE (TREE_OPERAND (op, 0)) == STRING_CST)) 6617169699Skan return integer_one_node; 6618169699Skan } 661990075Sobrien 6620132727Skan /* If this expression has side effects, show we don't know it to be a 6621132727Skan constant. Likewise if it's a pointer or aggregate type since in 6622132727Skan those case we only want literals, since those are only optimized 662390075Sobrien when generating RTL, not later. 662490075Sobrien And finally, if we are compiling an initializer, not code, we 662590075Sobrien need to return a definite result now; there's not going to be any 662690075Sobrien more optimization done. */ 6627132727Skan if (TREE_SIDE_EFFECTS (arglist) 662890075Sobrien || AGGREGATE_TYPE_P (TREE_TYPE (arglist)) 662990075Sobrien || POINTER_TYPE_P (TREE_TYPE (arglist)) 6630169699Skan || cfun == 0 6631169699Skan || folding_initializer) 663290075Sobrien return integer_zero_node; 663390075Sobrien 663490075Sobrien return 0; 663590075Sobrien} 663690075Sobrien 6637169699Skan/* Fold a call to __builtin_expect, if we expect that a comparison against 6638169699Skan the argument will fold to a constant. In practice, this means a true 6639169699Skan constant or the address of a non-weak symbol. ARGLIST is the argument 6640169699Skan list of the call. */ 6641169699Skan 6642169699Skanstatic tree 6643169699Skanfold_builtin_expect (tree arglist) 6644169699Skan{ 6645169699Skan tree arg, inner; 6646169699Skan 6647169699Skan if (arglist == 0) 6648169699Skan return 0; 6649169699Skan 6650169699Skan arg = TREE_VALUE (arglist); 6651169699Skan 6652169699Skan /* If the argument isn't invariant, then there's nothing we can do. */ 6653169699Skan if (!TREE_INVARIANT (arg)) 6654169699Skan return 0; 6655169699Skan 6656169699Skan /* If we're looking at an address of a weak decl, then do not fold. */ 6657169699Skan inner = arg; 6658169699Skan STRIP_NOPS (inner); 6659169699Skan if (TREE_CODE (inner) == ADDR_EXPR) 6660169699Skan { 6661169699Skan do 6662169699Skan { 6663169699Skan inner = TREE_OPERAND (inner, 0); 6664169699Skan } 6665169699Skan while (TREE_CODE (inner) == COMPONENT_REF 6666169699Skan || TREE_CODE (inner) == ARRAY_REF); 6667169699Skan if (DECL_P (inner) && DECL_WEAK (inner)) 6668169699Skan return 0; 6669169699Skan } 6670169699Skan 6671169699Skan /* Otherwise, ARG already has the proper type for the return value. */ 6672169699Skan return arg; 6673169699Skan} 6674169699Skan 667590075Sobrien/* Fold a call to __builtin_classify_type. */ 667690075Sobrien 667790075Sobrienstatic tree 6678132727Skanfold_builtin_classify_type (tree arglist) 667990075Sobrien{ 668090075Sobrien if (arglist == 0) 6681169699Skan return build_int_cst (NULL_TREE, no_type_class); 668290075Sobrien 6683169699Skan return build_int_cst (NULL_TREE, 6684169699Skan type_to_class (TREE_TYPE (TREE_VALUE (arglist)))); 668590075Sobrien} 668690075Sobrien 6687169699Skan/* Fold a call to __builtin_strlen. */ 6688169699Skan 6689169699Skanstatic tree 6690169699Skanfold_builtin_strlen (tree arglist) 6691169699Skan{ 6692169699Skan if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) 6693169699Skan return NULL_TREE; 6694169699Skan else 6695169699Skan { 6696169699Skan tree len = c_strlen (TREE_VALUE (arglist), 0); 6697169699Skan 6698169699Skan if (len) 6699169699Skan { 6700169699Skan /* Convert from the internal "sizetype" type to "size_t". */ 6701169699Skan if (size_type_node) 6702169699Skan len = fold_convert (size_type_node, len); 6703169699Skan return len; 6704169699Skan } 6705169699Skan 6706169699Skan return NULL_TREE; 6707169699Skan } 6708169699Skan} 6709169699Skan 6710117404Skan/* Fold a call to __builtin_inf or __builtin_huge_val. */ 6711117404Skan 6712117404Skanstatic tree 6713132727Skanfold_builtin_inf (tree type, int warn) 6714117404Skan{ 6715117404Skan REAL_VALUE_TYPE real; 6716117404Skan 6717169699Skan /* __builtin_inff is intended to be usable to define INFINITY on all 6718169699Skan targets. If an infinity is not available, INFINITY expands "to a 6719169699Skan positive constant of type float that overflows at translation 6720169699Skan time", footnote "In this case, using INFINITY will violate the 6721169699Skan constraint in 6.4.4 and thus require a diagnostic." (C99 7.12#4). 6722169699Skan Thus we pedwarn to ensure this constraint violation is 6723169699Skan diagnosed. */ 6724117404Skan if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn) 6725169699Skan pedwarn ("target format does not support infinity"); 6726117404Skan 6727117404Skan real_inf (&real); 6728117404Skan return build_real (type, real); 6729117404Skan} 6730117404Skan 6731117404Skan/* Fold a call to __builtin_nan or __builtin_nans. */ 6732117404Skan 6733117404Skanstatic tree 6734132727Skanfold_builtin_nan (tree arglist, tree type, int quiet) 6735117404Skan{ 6736117404Skan REAL_VALUE_TYPE real; 6737117404Skan const char *str; 6738117404Skan 6739117404Skan if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) 6740117404Skan return 0; 6741117404Skan str = c_getstr (TREE_VALUE (arglist)); 6742117404Skan if (!str) 6743117404Skan return 0; 6744117404Skan 6745117404Skan if (!real_nan (&real, str, quiet, TYPE_MODE (type))) 6746117404Skan return 0; 6747117404Skan 6748117404Skan return build_real (type, real); 6749117404Skan} 6750117404Skan 6751132727Skan/* Return true if the floating point expression T has an integer value. 6752132727Skan We also allow +Inf, -Inf and NaN to be considered integer values. */ 6753132727Skan 6754132727Skanstatic bool 6755132727Skaninteger_valued_real_p (tree t) 6756132727Skan{ 6757132727Skan switch (TREE_CODE (t)) 6758132727Skan { 6759132727Skan case FLOAT_EXPR: 6760132727Skan return true; 6761132727Skan 6762132727Skan case ABS_EXPR: 6763132727Skan case SAVE_EXPR: 6764132727Skan case NON_LVALUE_EXPR: 6765132727Skan return integer_valued_real_p (TREE_OPERAND (t, 0)); 6766132727Skan 6767132727Skan case COMPOUND_EXPR: 6768132727Skan case MODIFY_EXPR: 6769132727Skan case BIND_EXPR: 6770132727Skan return integer_valued_real_p (TREE_OPERAND (t, 1)); 6771132727Skan 6772132727Skan case PLUS_EXPR: 6773132727Skan case MINUS_EXPR: 6774132727Skan case MULT_EXPR: 6775132727Skan case MIN_EXPR: 6776132727Skan case MAX_EXPR: 6777132727Skan return integer_valued_real_p (TREE_OPERAND (t, 0)) 6778132727Skan && integer_valued_real_p (TREE_OPERAND (t, 1)); 6779132727Skan 6780132727Skan case COND_EXPR: 6781132727Skan return integer_valued_real_p (TREE_OPERAND (t, 1)) 6782132727Skan && integer_valued_real_p (TREE_OPERAND (t, 2)); 6783132727Skan 6784132727Skan case REAL_CST: 6785132727Skan if (! TREE_CONSTANT_OVERFLOW (t)) 6786132727Skan { 6787169699Skan REAL_VALUE_TYPE c, cint; 6788132727Skan 6789132727Skan c = TREE_REAL_CST (t); 6790132727Skan real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c); 6791132727Skan return real_identical (&c, &cint); 6792132727Skan } 6793169699Skan break; 6794132727Skan 6795132727Skan case NOP_EXPR: 6796132727Skan { 6797132727Skan tree type = TREE_TYPE (TREE_OPERAND (t, 0)); 6798132727Skan if (TREE_CODE (type) == INTEGER_TYPE) 6799132727Skan return true; 6800132727Skan if (TREE_CODE (type) == REAL_TYPE) 6801132727Skan return integer_valued_real_p (TREE_OPERAND (t, 0)); 6802132727Skan break; 6803132727Skan } 6804132727Skan 6805132727Skan case CALL_EXPR: 6806132727Skan switch (builtin_mathfn_code (t)) 6807132727Skan { 6808169699Skan CASE_FLT_FN (BUILT_IN_CEIL): 6809169699Skan CASE_FLT_FN (BUILT_IN_FLOOR): 6810169699Skan CASE_FLT_FN (BUILT_IN_NEARBYINT): 6811169699Skan CASE_FLT_FN (BUILT_IN_RINT): 6812169699Skan CASE_FLT_FN (BUILT_IN_ROUND): 6813169699Skan CASE_FLT_FN (BUILT_IN_TRUNC): 6814132727Skan return true; 6815132727Skan 6816132727Skan default: 6817132727Skan break; 6818132727Skan } 6819132727Skan break; 6820132727Skan 6821132727Skan default: 6822132727Skan break; 6823132727Skan } 6824132727Skan return false; 6825132727Skan} 6826132727Skan 6827132727Skan/* EXP is assumed to be builtin call where truncation can be propagated 6828132727Skan across (for instance floor((double)f) == (double)floorf (f). 6829132727Skan Do the transformation. */ 6830132727Skan 6831132727Skanstatic tree 6832169699Skanfold_trunc_transparent_mathfn (tree fndecl, tree arglist) 6833132727Skan{ 6834132727Skan enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); 6835132727Skan tree arg; 6836132727Skan 6837132727Skan if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 6838132727Skan return 0; 6839132727Skan 6840132727Skan arg = TREE_VALUE (arglist); 6841132727Skan /* Integer rounding functions are idempotent. */ 6842132727Skan if (fcode == builtin_mathfn_code (arg)) 6843132727Skan return arg; 6844132727Skan 6845132727Skan /* If argument is already integer valued, and we don't need to worry 6846132727Skan about setting errno, there's no need to perform rounding. */ 6847132727Skan if (! flag_errno_math && integer_valued_real_p (arg)) 6848132727Skan return arg; 6849132727Skan 6850132727Skan if (optimize) 6851132727Skan { 6852132727Skan tree arg0 = strip_float_extensions (arg); 6853169699Skan tree ftype = TREE_TYPE (TREE_TYPE (fndecl)); 6854132727Skan tree newtype = TREE_TYPE (arg0); 6855132727Skan tree decl; 6856132727Skan 6857132727Skan if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype) 6858132727Skan && (decl = mathfn_built_in (newtype, fcode))) 6859132727Skan { 6860132727Skan arglist = 6861169699Skan build_tree_list (NULL_TREE, fold_convert (newtype, arg0)); 6862169699Skan return fold_convert (ftype, 6863169699Skan build_function_call_expr (decl, arglist)); 6864132727Skan } 6865132727Skan } 6866132727Skan return 0; 6867132727Skan} 6868132727Skan 6869169699Skan/* EXP is assumed to be builtin call which can narrow the FP type of 6870169699Skan the argument, for instance lround((double)f) -> lroundf (f). */ 6871132727Skan 6872132727Skanstatic tree 6873169699Skanfold_fixed_mathfn (tree fndecl, tree arglist) 6874132727Skan{ 6875169699Skan enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); 6876132727Skan tree arg; 6877132727Skan 6878169699Skan if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 6879169699Skan return 0; 6880169699Skan 6881169699Skan arg = TREE_VALUE (arglist); 6882169699Skan 6883169699Skan /* If argument is already integer valued, and we don't need to worry 6884169699Skan about setting errno, there's no need to perform rounding. */ 6885169699Skan if (! flag_errno_math && integer_valued_real_p (arg)) 6886169699Skan return fold_build1 (FIX_TRUNC_EXPR, TREE_TYPE (TREE_TYPE (fndecl)), arg); 6887169699Skan 6888169699Skan if (optimize) 6889169699Skan { 6890169699Skan tree ftype = TREE_TYPE (arg); 6891169699Skan tree arg0 = strip_float_extensions (arg); 6892169699Skan tree newtype = TREE_TYPE (arg0); 6893169699Skan tree decl; 6894169699Skan 6895169699Skan if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype) 6896169699Skan && (decl = mathfn_built_in (newtype, fcode))) 6897169699Skan { 6898169699Skan arglist = 6899169699Skan build_tree_list (NULL_TREE, fold_convert (newtype, arg0)); 6900169699Skan return build_function_call_expr (decl, arglist); 6901169699Skan } 6902169699Skan } 6903169699Skan 6904169699Skan /* Canonicalize llround (x) to lround (x) on LP64 targets where 6905169699Skan sizeof (long long) == sizeof (long). */ 6906169699Skan if (TYPE_PRECISION (long_long_integer_type_node) 6907169699Skan == TYPE_PRECISION (long_integer_type_node)) 6908169699Skan { 6909169699Skan tree newfn = NULL_TREE; 6910169699Skan switch (fcode) 6911169699Skan { 6912169699Skan CASE_FLT_FN (BUILT_IN_LLCEIL): 6913169699Skan newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LCEIL); 6914169699Skan break; 6915169699Skan 6916169699Skan CASE_FLT_FN (BUILT_IN_LLFLOOR): 6917169699Skan newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LFLOOR); 6918169699Skan break; 6919169699Skan 6920169699Skan CASE_FLT_FN (BUILT_IN_LLROUND): 6921169699Skan newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LROUND); 6922169699Skan break; 6923169699Skan 6924169699Skan CASE_FLT_FN (BUILT_IN_LLRINT): 6925169699Skan newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LRINT); 6926169699Skan break; 6927169699Skan 6928169699Skan default: 6929169699Skan break; 6930169699Skan } 6931169699Skan 6932169699Skan if (newfn) 6933169699Skan { 6934169699Skan tree newcall = build_function_call_expr (newfn, arglist); 6935169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), newcall); 6936169699Skan } 6937169699Skan } 6938169699Skan 6939169699Skan return 0; 6940169699Skan} 6941169699Skan 6942169699Skan/* Fold function call to builtin cabs, cabsf or cabsl. ARGLIST 6943169699Skan is the argument list, TYPE is the return type and FNDECL is the 6944169699Skan original function DECL. Return NULL_TREE if no if no simplification 6945169699Skan can be made. */ 6946169699Skan 6947169699Skanstatic tree 6948169699Skanfold_builtin_cabs (tree arglist, tree type, tree fndecl) 6949169699Skan{ 6950169699Skan tree arg; 6951169699Skan 6952132727Skan if (!arglist || TREE_CHAIN (arglist)) 6953132727Skan return NULL_TREE; 6954132727Skan 6955132727Skan arg = TREE_VALUE (arglist); 6956132727Skan if (TREE_CODE (TREE_TYPE (arg)) != COMPLEX_TYPE 6957132727Skan || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE) 6958132727Skan return NULL_TREE; 6959132727Skan 6960132727Skan /* Evaluate cabs of a constant at compile-time. */ 6961132727Skan if (flag_unsafe_math_optimizations 6962132727Skan && TREE_CODE (arg) == COMPLEX_CST 6963132727Skan && TREE_CODE (TREE_REALPART (arg)) == REAL_CST 6964132727Skan && TREE_CODE (TREE_IMAGPART (arg)) == REAL_CST 6965132727Skan && ! TREE_CONSTANT_OVERFLOW (TREE_REALPART (arg)) 6966132727Skan && ! TREE_CONSTANT_OVERFLOW (TREE_IMAGPART (arg))) 6967132727Skan { 6968132727Skan REAL_VALUE_TYPE r, i; 6969132727Skan 6970132727Skan r = TREE_REAL_CST (TREE_REALPART (arg)); 6971132727Skan i = TREE_REAL_CST (TREE_IMAGPART (arg)); 6972132727Skan 6973132727Skan real_arithmetic (&r, MULT_EXPR, &r, &r); 6974132727Skan real_arithmetic (&i, MULT_EXPR, &i, &i); 6975132727Skan real_arithmetic (&r, PLUS_EXPR, &r, &i); 6976132727Skan if (real_sqrt (&r, TYPE_MODE (type), &r) 6977132727Skan || ! flag_trapping_math) 6978132727Skan return build_real (type, r); 6979132727Skan } 6980132727Skan 6981132727Skan /* If either part is zero, cabs is fabs of the other. */ 6982132727Skan if (TREE_CODE (arg) == COMPLEX_EXPR 6983132727Skan && real_zerop (TREE_OPERAND (arg, 0))) 6984169699Skan return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1)); 6985132727Skan if (TREE_CODE (arg) == COMPLEX_EXPR 6986132727Skan && real_zerop (TREE_OPERAND (arg, 1))) 6987169699Skan return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0)); 6988132727Skan 6989169699Skan /* Optimize cabs(-z) and cabs(conj(z)) as cabs(z). */ 6990169699Skan if (TREE_CODE (arg) == NEGATE_EXPR 6991169699Skan || TREE_CODE (arg) == CONJ_EXPR) 6992132727Skan { 6993169699Skan tree arglist = build_tree_list (NULL_TREE, TREE_OPERAND (arg, 0)); 6994169699Skan return build_function_call_expr (fndecl, arglist); 6995169699Skan } 6996132727Skan 6997169699Skan /* Don't do this when optimizing for size. */ 6998169699Skan if (flag_unsafe_math_optimizations 6999169699Skan && optimize && !optimize_size) 7000169699Skan { 7001169699Skan tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT); 7002132727Skan 7003132727Skan if (sqrtfn != NULL_TREE) 7004132727Skan { 7005132727Skan tree rpart, ipart, result, arglist; 7006132727Skan 7007169699Skan arg = builtin_save_expr (arg); 7008132727Skan 7009169699Skan rpart = fold_build1 (REALPART_EXPR, type, arg); 7010169699Skan ipart = fold_build1 (IMAGPART_EXPR, type, arg); 7011132727Skan 7012169699Skan rpart = builtin_save_expr (rpart); 7013169699Skan ipart = builtin_save_expr (ipart); 7014132727Skan 7015169699Skan result = fold_build2 (PLUS_EXPR, type, 7016169699Skan fold_build2 (MULT_EXPR, type, 7017169699Skan rpart, rpart), 7018169699Skan fold_build2 (MULT_EXPR, type, 7019169699Skan ipart, ipart)); 7020132727Skan 7021132727Skan arglist = build_tree_list (NULL_TREE, result); 7022132727Skan return build_function_call_expr (sqrtfn, arglist); 7023132727Skan } 7024132727Skan } 7025132727Skan 7026132727Skan return NULL_TREE; 7027132727Skan} 7028132727Skan 7029169699Skan/* Fold a builtin function call to sqrt, sqrtf, or sqrtl. Return 7030169699Skan NULL_TREE if no simplification can be made. */ 7031169699Skan 7032169699Skanstatic tree 7033169699Skanfold_builtin_sqrt (tree arglist, tree type) 7034169699Skan{ 7035169699Skan 7036169699Skan enum built_in_function fcode; 7037169699Skan tree arg = TREE_VALUE (arglist); 7038169699Skan 7039169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7040169699Skan return NULL_TREE; 7041169699Skan 7042169699Skan /* Optimize sqrt of constant value. */ 7043169699Skan if (TREE_CODE (arg) == REAL_CST 7044169699Skan && ! TREE_CONSTANT_OVERFLOW (arg)) 7045169699Skan { 7046169699Skan REAL_VALUE_TYPE r, x; 7047169699Skan 7048169699Skan x = TREE_REAL_CST (arg); 7049169699Skan if (real_sqrt (&r, TYPE_MODE (type), &x) 7050169699Skan || (!flag_trapping_math && !flag_errno_math)) 7051169699Skan return build_real (type, r); 7052169699Skan } 7053169699Skan 7054169699Skan /* Optimize sqrt(expN(x)) = expN(x*0.5). */ 7055169699Skan fcode = builtin_mathfn_code (arg); 7056169699Skan if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode)) 7057169699Skan { 7058169699Skan tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); 7059169699Skan arg = fold_build2 (MULT_EXPR, type, 7060169699Skan TREE_VALUE (TREE_OPERAND (arg, 1)), 7061169699Skan build_real (type, dconsthalf)); 7062169699Skan arglist = build_tree_list (NULL_TREE, arg); 7063169699Skan return build_function_call_expr (expfn, arglist); 7064169699Skan } 7065169699Skan 7066169699Skan /* Optimize sqrt(Nroot(x)) -> pow(x,1/(2*N)). */ 7067169699Skan if (flag_unsafe_math_optimizations && BUILTIN_ROOT_P (fcode)) 7068169699Skan { 7069169699Skan tree powfn = mathfn_built_in (type, BUILT_IN_POW); 7070169699Skan 7071169699Skan if (powfn) 7072169699Skan { 7073169699Skan tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); 7074169699Skan tree tree_root; 7075169699Skan /* The inner root was either sqrt or cbrt. */ 7076169699Skan REAL_VALUE_TYPE dconstroot = 7077169699Skan BUILTIN_SQRT_P (fcode) ? dconsthalf : dconstthird; 7078169699Skan 7079169699Skan /* Adjust for the outer root. */ 7080169699Skan SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1); 7081169699Skan dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot); 7082169699Skan tree_root = build_real (type, dconstroot); 7083169699Skan arglist = tree_cons (NULL_TREE, arg0, 7084169699Skan build_tree_list (NULL_TREE, tree_root)); 7085169699Skan return build_function_call_expr (powfn, arglist); 7086169699Skan } 7087169699Skan } 7088169699Skan 7089169699Skan /* Optimize sqrt(pow(x,y)) = pow(|x|,y*0.5). */ 7090169699Skan if (flag_unsafe_math_optimizations 7091169699Skan && (fcode == BUILT_IN_POW 7092169699Skan || fcode == BUILT_IN_POWF 7093169699Skan || fcode == BUILT_IN_POWL)) 7094169699Skan { 7095169699Skan tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); 7096169699Skan tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); 7097169699Skan tree arg1 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); 7098169699Skan tree narg1; 7099169699Skan if (!tree_expr_nonnegative_p (arg0)) 7100169699Skan arg0 = build1 (ABS_EXPR, type, arg0); 7101169699Skan narg1 = fold_build2 (MULT_EXPR, type, arg1, 7102169699Skan build_real (type, dconsthalf)); 7103169699Skan arglist = tree_cons (NULL_TREE, arg0, 7104169699Skan build_tree_list (NULL_TREE, narg1)); 7105169699Skan return build_function_call_expr (powfn, arglist); 7106169699Skan } 7107169699Skan 7108169699Skan return NULL_TREE; 7109169699Skan} 7110169699Skan 7111169699Skan/* Fold a builtin function call to cbrt, cbrtf, or cbrtl. Return 7112169699Skan NULL_TREE if no simplification can be made. */ 7113169699Skanstatic tree 7114169699Skanfold_builtin_cbrt (tree arglist, tree type) 7115169699Skan{ 7116169699Skan tree arg = TREE_VALUE (arglist); 7117169699Skan const enum built_in_function fcode = builtin_mathfn_code (arg); 7118169699Skan 7119169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7120169699Skan return NULL_TREE; 7121169699Skan 7122169699Skan /* Optimize cbrt of constant value. */ 7123169699Skan if (real_zerop (arg) || real_onep (arg) || real_minus_onep (arg)) 7124169699Skan return arg; 7125169699Skan 7126169699Skan if (flag_unsafe_math_optimizations) 7127169699Skan { 7128169699Skan /* Optimize cbrt(expN(x)) -> expN(x/3). */ 7129169699Skan if (BUILTIN_EXPONENT_P (fcode)) 7130169699Skan { 7131169699Skan tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); 7132169699Skan const REAL_VALUE_TYPE third_trunc = 7133169699Skan real_value_truncate (TYPE_MODE (type), dconstthird); 7134169699Skan arg = fold_build2 (MULT_EXPR, type, 7135169699Skan TREE_VALUE (TREE_OPERAND (arg, 1)), 7136169699Skan build_real (type, third_trunc)); 7137169699Skan arglist = build_tree_list (NULL_TREE, arg); 7138169699Skan return build_function_call_expr (expfn, arglist); 7139169699Skan } 7140169699Skan 7141169699Skan /* Optimize cbrt(sqrt(x)) -> pow(x,1/6). */ 7142169699Skan if (BUILTIN_SQRT_P (fcode)) 7143169699Skan { 7144169699Skan tree powfn = mathfn_built_in (type, BUILT_IN_POW); 7145169699Skan 7146169699Skan if (powfn) 7147169699Skan { 7148169699Skan tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); 7149169699Skan tree tree_root; 7150169699Skan REAL_VALUE_TYPE dconstroot = dconstthird; 7151169699Skan 7152169699Skan SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1); 7153169699Skan dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot); 7154169699Skan tree_root = build_real (type, dconstroot); 7155169699Skan arglist = tree_cons (NULL_TREE, arg0, 7156169699Skan build_tree_list (NULL_TREE, tree_root)); 7157169699Skan return build_function_call_expr (powfn, arglist); 7158169699Skan } 7159169699Skan } 7160169699Skan 7161169699Skan /* Optimize cbrt(cbrt(x)) -> pow(x,1/9) iff x is nonnegative. */ 7162169699Skan if (BUILTIN_CBRT_P (fcode)) 7163169699Skan { 7164169699Skan tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); 7165169699Skan if (tree_expr_nonnegative_p (arg0)) 7166169699Skan { 7167169699Skan tree powfn = mathfn_built_in (type, BUILT_IN_POW); 7168169699Skan 7169169699Skan if (powfn) 7170169699Skan { 7171169699Skan tree tree_root; 7172169699Skan REAL_VALUE_TYPE dconstroot; 7173169699Skan 7174169699Skan real_arithmetic (&dconstroot, MULT_EXPR, &dconstthird, &dconstthird); 7175169699Skan dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot); 7176169699Skan tree_root = build_real (type, dconstroot); 7177169699Skan arglist = tree_cons (NULL_TREE, arg0, 7178169699Skan build_tree_list (NULL_TREE, tree_root)); 7179169699Skan return build_function_call_expr (powfn, arglist); 7180169699Skan } 7181169699Skan } 7182169699Skan } 7183169699Skan 7184169699Skan /* Optimize cbrt(pow(x,y)) -> pow(x,y/3) iff x is nonnegative. */ 7185169699Skan if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF 7186169699Skan || fcode == BUILT_IN_POWL) 7187169699Skan { 7188169699Skan tree arg00 = TREE_VALUE (TREE_OPERAND (arg, 1)); 7189169699Skan tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); 7190169699Skan if (tree_expr_nonnegative_p (arg00)) 7191169699Skan { 7192169699Skan tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); 7193169699Skan const REAL_VALUE_TYPE dconstroot 7194169699Skan = real_value_truncate (TYPE_MODE (type), dconstthird); 7195169699Skan tree narg01 = fold_build2 (MULT_EXPR, type, arg01, 7196169699Skan build_real (type, dconstroot)); 7197169699Skan arglist = tree_cons (NULL_TREE, arg00, 7198169699Skan build_tree_list (NULL_TREE, narg01)); 7199169699Skan return build_function_call_expr (powfn, arglist); 7200169699Skan } 7201169699Skan } 7202169699Skan } 7203169699Skan return NULL_TREE; 7204169699Skan} 7205169699Skan 7206169699Skan/* Fold function call to builtin sin, sinf, or sinl. Return 7207169699Skan NULL_TREE if no simplification can be made. */ 7208169699Skanstatic tree 7209169699Skanfold_builtin_sin (tree arglist) 7210169699Skan{ 7211169699Skan tree arg = TREE_VALUE (arglist); 7212169699Skan 7213169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7214169699Skan return NULL_TREE; 7215169699Skan 7216169699Skan /* Optimize sin (0.0) = 0.0. */ 7217169699Skan if (real_zerop (arg)) 7218169699Skan return arg; 7219169699Skan 7220169699Skan return NULL_TREE; 7221169699Skan} 7222169699Skan 7223169699Skan/* Fold function call to builtin cos, cosf, or cosl. Return 7224169699Skan NULL_TREE if no simplification can be made. */ 7225169699Skanstatic tree 7226169699Skanfold_builtin_cos (tree arglist, tree type, tree fndecl) 7227169699Skan{ 7228169699Skan tree arg = TREE_VALUE (arglist); 7229169699Skan 7230169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7231169699Skan return NULL_TREE; 7232169699Skan 7233169699Skan /* Optimize cos (0.0) = 1.0. */ 7234169699Skan if (real_zerop (arg)) 7235169699Skan return build_real (type, dconst1); 7236169699Skan 7237169699Skan /* Optimize cos(-x) into cos (x). */ 7238169699Skan if (TREE_CODE (arg) == NEGATE_EXPR) 7239169699Skan { 7240169699Skan tree args = build_tree_list (NULL_TREE, 7241169699Skan TREE_OPERAND (arg, 0)); 7242169699Skan return build_function_call_expr (fndecl, args); 7243169699Skan } 7244169699Skan 7245169699Skan return NULL_TREE; 7246169699Skan} 7247169699Skan 7248169699Skan/* Fold function call to builtin tan, tanf, or tanl. Return 7249169699Skan NULL_TREE if no simplification can be made. */ 7250169699Skanstatic tree 7251169699Skanfold_builtin_tan (tree arglist) 7252169699Skan{ 7253169699Skan enum built_in_function fcode; 7254169699Skan tree arg = TREE_VALUE (arglist); 7255169699Skan 7256169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7257169699Skan return NULL_TREE; 7258169699Skan 7259169699Skan /* Optimize tan(0.0) = 0.0. */ 7260169699Skan if (real_zerop (arg)) 7261169699Skan return arg; 7262169699Skan 7263169699Skan /* Optimize tan(atan(x)) = x. */ 7264169699Skan fcode = builtin_mathfn_code (arg); 7265169699Skan if (flag_unsafe_math_optimizations 7266169699Skan && (fcode == BUILT_IN_ATAN 7267169699Skan || fcode == BUILT_IN_ATANF 7268169699Skan || fcode == BUILT_IN_ATANL)) 7269169699Skan return TREE_VALUE (TREE_OPERAND (arg, 1)); 7270169699Skan 7271169699Skan return NULL_TREE; 7272169699Skan} 7273169699Skan 7274169699Skan/* Fold function call to builtin atan, atanf, or atanl. Return 7275169699Skan NULL_TREE if no simplification can be made. */ 7276169699Skan 7277169699Skanstatic tree 7278169699Skanfold_builtin_atan (tree arglist, tree type) 7279169699Skan{ 7280169699Skan 7281169699Skan tree arg = TREE_VALUE (arglist); 7282169699Skan 7283169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7284169699Skan return NULL_TREE; 7285169699Skan 7286169699Skan /* Optimize atan(0.0) = 0.0. */ 7287169699Skan if (real_zerop (arg)) 7288169699Skan return arg; 7289169699Skan 7290169699Skan /* Optimize atan(1.0) = pi/4. */ 7291169699Skan if (real_onep (arg)) 7292169699Skan { 7293169699Skan REAL_VALUE_TYPE cst; 7294169699Skan 7295169699Skan real_convert (&cst, TYPE_MODE (type), &dconstpi); 7296169699Skan SET_REAL_EXP (&cst, REAL_EXP (&cst) - 2); 7297169699Skan return build_real (type, cst); 7298169699Skan } 7299169699Skan 7300169699Skan return NULL_TREE; 7301169699Skan} 7302169699Skan 7303132727Skan/* Fold function call to builtin trunc, truncf or truncl. Return 7304132727Skan NULL_TREE if no simplification can be made. */ 7305132727Skan 7306132727Skanstatic tree 7307169699Skanfold_builtin_trunc (tree fndecl, tree arglist) 7308132727Skan{ 7309132727Skan tree arg; 7310132727Skan 7311132727Skan if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7312132727Skan return 0; 7313132727Skan 7314132727Skan /* Optimize trunc of constant value. */ 7315132727Skan arg = TREE_VALUE (arglist); 7316132727Skan if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) 7317132727Skan { 7318132727Skan REAL_VALUE_TYPE r, x; 7319169699Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 7320132727Skan 7321132727Skan x = TREE_REAL_CST (arg); 7322132727Skan real_trunc (&r, TYPE_MODE (type), &x); 7323132727Skan return build_real (type, r); 7324132727Skan } 7325132727Skan 7326169699Skan return fold_trunc_transparent_mathfn (fndecl, arglist); 7327132727Skan} 7328132727Skan 7329132727Skan/* Fold function call to builtin floor, floorf or floorl. Return 7330132727Skan NULL_TREE if no simplification can be made. */ 7331132727Skan 7332132727Skanstatic tree 7333169699Skanfold_builtin_floor (tree fndecl, tree arglist) 7334132727Skan{ 7335132727Skan tree arg; 7336132727Skan 7337132727Skan if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7338132727Skan return 0; 7339132727Skan 7340132727Skan /* Optimize floor of constant value. */ 7341132727Skan arg = TREE_VALUE (arglist); 7342132727Skan if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) 7343132727Skan { 7344132727Skan REAL_VALUE_TYPE x; 7345132727Skan 7346132727Skan x = TREE_REAL_CST (arg); 7347132727Skan if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) 7348132727Skan { 7349169699Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 7350132727Skan REAL_VALUE_TYPE r; 7351132727Skan 7352132727Skan real_floor (&r, TYPE_MODE (type), &x); 7353132727Skan return build_real (type, r); 7354132727Skan } 7355132727Skan } 7356132727Skan 7357169699Skan return fold_trunc_transparent_mathfn (fndecl, arglist); 7358132727Skan} 7359132727Skan 7360132727Skan/* Fold function call to builtin ceil, ceilf or ceill. Return 7361132727Skan NULL_TREE if no simplification can be made. */ 7362132727Skan 7363132727Skanstatic tree 7364169699Skanfold_builtin_ceil (tree fndecl, tree arglist) 7365132727Skan{ 7366132727Skan tree arg; 7367132727Skan 7368132727Skan if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7369132727Skan return 0; 7370132727Skan 7371132727Skan /* Optimize ceil of constant value. */ 7372132727Skan arg = TREE_VALUE (arglist); 7373132727Skan if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) 7374132727Skan { 7375132727Skan REAL_VALUE_TYPE x; 7376132727Skan 7377132727Skan x = TREE_REAL_CST (arg); 7378132727Skan if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) 7379132727Skan { 7380169699Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 7381132727Skan REAL_VALUE_TYPE r; 7382132727Skan 7383132727Skan real_ceil (&r, TYPE_MODE (type), &x); 7384132727Skan return build_real (type, r); 7385132727Skan } 7386132727Skan } 7387132727Skan 7388169699Skan return fold_trunc_transparent_mathfn (fndecl, arglist); 7389132727Skan} 7390132727Skan 7391169699Skan/* Fold function call to builtin round, roundf or roundl. Return 7392169699Skan NULL_TREE if no simplification can be made. */ 7393169699Skan 7394169699Skanstatic tree 7395169699Skanfold_builtin_round (tree fndecl, tree arglist) 7396169699Skan{ 7397169699Skan tree arg; 7398169699Skan 7399169699Skan if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7400169699Skan return 0; 7401169699Skan 7402169699Skan /* Optimize round of constant value. */ 7403169699Skan arg = TREE_VALUE (arglist); 7404169699Skan if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) 7405169699Skan { 7406169699Skan REAL_VALUE_TYPE x; 7407169699Skan 7408169699Skan x = TREE_REAL_CST (arg); 7409169699Skan if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) 7410169699Skan { 7411169699Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 7412169699Skan REAL_VALUE_TYPE r; 7413169699Skan 7414169699Skan real_round (&r, TYPE_MODE (type), &x); 7415169699Skan return build_real (type, r); 7416169699Skan } 7417169699Skan } 7418169699Skan 7419169699Skan return fold_trunc_transparent_mathfn (fndecl, arglist); 7420169699Skan} 7421169699Skan 7422169699Skan/* Fold function call to builtin lround, lroundf or lroundl (or the 7423169699Skan corresponding long long versions) and other rounding functions. 7424169699Skan Return NULL_TREE if no simplification can be made. */ 7425169699Skan 7426169699Skanstatic tree 7427169699Skanfold_builtin_int_roundingfn (tree fndecl, tree arglist) 7428169699Skan{ 7429169699Skan tree arg; 7430169699Skan 7431169699Skan if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7432169699Skan return 0; 7433169699Skan 7434169699Skan /* Optimize lround of constant value. */ 7435169699Skan arg = TREE_VALUE (arglist); 7436169699Skan if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) 7437169699Skan { 7438169699Skan const REAL_VALUE_TYPE x = TREE_REAL_CST (arg); 7439169699Skan 7440169699Skan if (! REAL_VALUE_ISNAN (x) && ! REAL_VALUE_ISINF (x)) 7441169699Skan { 7442169699Skan tree itype = TREE_TYPE (TREE_TYPE (fndecl)); 7443169699Skan tree ftype = TREE_TYPE (arg), result; 7444169699Skan HOST_WIDE_INT hi, lo; 7445169699Skan REAL_VALUE_TYPE r; 7446169699Skan 7447169699Skan switch (DECL_FUNCTION_CODE (fndecl)) 7448169699Skan { 7449169699Skan CASE_FLT_FN (BUILT_IN_LFLOOR): 7450169699Skan CASE_FLT_FN (BUILT_IN_LLFLOOR): 7451169699Skan real_floor (&r, TYPE_MODE (ftype), &x); 7452169699Skan break; 7453169699Skan 7454169699Skan CASE_FLT_FN (BUILT_IN_LCEIL): 7455169699Skan CASE_FLT_FN (BUILT_IN_LLCEIL): 7456169699Skan real_ceil (&r, TYPE_MODE (ftype), &x); 7457169699Skan break; 7458169699Skan 7459169699Skan CASE_FLT_FN (BUILT_IN_LROUND): 7460169699Skan CASE_FLT_FN (BUILT_IN_LLROUND): 7461169699Skan real_round (&r, TYPE_MODE (ftype), &x); 7462169699Skan break; 7463169699Skan 7464169699Skan default: 7465169699Skan gcc_unreachable (); 7466169699Skan } 7467169699Skan 7468169699Skan REAL_VALUE_TO_INT (&lo, &hi, r); 7469169699Skan result = build_int_cst_wide (NULL_TREE, lo, hi); 7470169699Skan if (int_fits_type_p (result, itype)) 7471169699Skan return fold_convert (itype, result); 7472169699Skan } 7473169699Skan } 7474169699Skan 7475169699Skan return fold_fixed_mathfn (fndecl, arglist); 7476169699Skan} 7477169699Skan 7478132727Skan/* Fold function call to builtin ffs, clz, ctz, popcount and parity 7479132727Skan and their long and long long variants (i.e. ffsl and ffsll). 7480132727Skan Return NULL_TREE if no simplification can be made. */ 7481132727Skan 7482132727Skanstatic tree 7483169699Skanfold_builtin_bitop (tree fndecl, tree arglist) 7484132727Skan{ 7485132727Skan tree arg; 7486132727Skan 7487132727Skan if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 7488132727Skan return NULL_TREE; 7489132727Skan 7490132727Skan /* Optimize for constant argument. */ 7491132727Skan arg = TREE_VALUE (arglist); 7492132727Skan if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg)) 7493132727Skan { 7494132727Skan HOST_WIDE_INT hi, width, result; 7495132727Skan unsigned HOST_WIDE_INT lo; 7496169699Skan tree type; 7497132727Skan 7498132727Skan type = TREE_TYPE (arg); 7499132727Skan width = TYPE_PRECISION (type); 7500132727Skan lo = TREE_INT_CST_LOW (arg); 7501132727Skan 7502132727Skan /* Clear all the bits that are beyond the type's precision. */ 7503132727Skan if (width > HOST_BITS_PER_WIDE_INT) 7504132727Skan { 7505132727Skan hi = TREE_INT_CST_HIGH (arg); 7506132727Skan if (width < 2 * HOST_BITS_PER_WIDE_INT) 7507132727Skan hi &= ~((HOST_WIDE_INT) (-1) >> (width - HOST_BITS_PER_WIDE_INT)); 7508132727Skan } 7509132727Skan else 7510132727Skan { 7511132727Skan hi = 0; 7512132727Skan if (width < HOST_BITS_PER_WIDE_INT) 7513132727Skan lo &= ~((unsigned HOST_WIDE_INT) (-1) << width); 7514132727Skan } 7515132727Skan 7516132727Skan switch (DECL_FUNCTION_CODE (fndecl)) 7517132727Skan { 7518169699Skan CASE_INT_FN (BUILT_IN_FFS): 7519132727Skan if (lo != 0) 7520132727Skan result = exact_log2 (lo & -lo) + 1; 7521132727Skan else if (hi != 0) 7522132727Skan result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi) + 1; 7523132727Skan else 7524132727Skan result = 0; 7525132727Skan break; 7526132727Skan 7527169699Skan CASE_INT_FN (BUILT_IN_CLZ): 7528132727Skan if (hi != 0) 7529132727Skan result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT; 7530132727Skan else if (lo != 0) 7531132727Skan result = width - floor_log2 (lo) - 1; 7532132727Skan else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result)) 7533132727Skan result = width; 7534132727Skan break; 7535132727Skan 7536169699Skan CASE_INT_FN (BUILT_IN_CTZ): 7537132727Skan if (lo != 0) 7538132727Skan result = exact_log2 (lo & -lo); 7539132727Skan else if (hi != 0) 7540132727Skan result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi); 7541132727Skan else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result)) 7542132727Skan result = width; 7543132727Skan break; 7544132727Skan 7545169699Skan CASE_INT_FN (BUILT_IN_POPCOUNT): 7546132727Skan result = 0; 7547132727Skan while (lo) 7548132727Skan result++, lo &= lo - 1; 7549132727Skan while (hi) 7550132727Skan result++, hi &= hi - 1; 7551132727Skan break; 7552132727Skan 7553169699Skan CASE_INT_FN (BUILT_IN_PARITY): 7554132727Skan result = 0; 7555132727Skan while (lo) 7556132727Skan result++, lo &= lo - 1; 7557132727Skan while (hi) 7558132727Skan result++, hi &= hi - 1; 7559132727Skan result &= 1; 7560132727Skan break; 7561132727Skan 7562132727Skan default: 7563169699Skan gcc_unreachable (); 7564132727Skan } 7565132727Skan 7566169699Skan return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), result); 7567132727Skan } 7568132727Skan 7569132727Skan return NULL_TREE; 7570132727Skan} 7571132727Skan 7572259563Spfg/* Fold function call to builtin_bswap and the long and long long 7573259563Spfg variants. Return NULL_TREE if no simplification can be made. */ 7574259563Spfgstatic tree 7575259563Spfgfold_builtin_bswap (tree fndecl, tree arglist) 7576259563Spfg{ 7577259563Spfg tree arg; 7578259563Spfg 7579259563Spfg if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 7580259563Spfg return 0; 7581259563Spfg 7582259563Spfg /* Optimize constant value. */ 7583259563Spfg arg = TREE_VALUE (arglist); 7584259563Spfg if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg)) 7585259563Spfg { 7586259563Spfg HOST_WIDE_INT hi, width, r_hi = 0; 7587259563Spfg unsigned HOST_WIDE_INT lo, r_lo = 0; 7588259563Spfg tree type; 7589259563Spfg 7590259563Spfg type = TREE_TYPE (arg); 7591259563Spfg width = TYPE_PRECISION (type); 7592259563Spfg lo = TREE_INT_CST_LOW (arg); 7593259563Spfg hi = TREE_INT_CST_HIGH (arg); 7594259563Spfg 7595259563Spfg switch (DECL_FUNCTION_CODE (fndecl)) 7596259563Spfg { 7597259563Spfg case BUILT_IN_BSWAP32: 7598259563Spfg case BUILT_IN_BSWAP64: 7599259563Spfg { 7600259563Spfg int s; 7601259563Spfg 7602259563Spfg for (s = 0; s < width; s += 8) 7603259563Spfg { 7604259563Spfg int d = width - s - 8; 7605259563Spfg unsigned HOST_WIDE_INT byte; 7606259563Spfg 7607259563Spfg if (s < HOST_BITS_PER_WIDE_INT) 7608259563Spfg byte = (lo >> s) & 0xff; 7609259563Spfg else 7610259563Spfg byte = (hi >> (s - HOST_BITS_PER_WIDE_INT)) & 0xff; 7611259563Spfg 7612259563Spfg if (d < HOST_BITS_PER_WIDE_INT) 7613259563Spfg r_lo |= byte << d; 7614259563Spfg else 7615259563Spfg r_hi |= byte << (d - HOST_BITS_PER_WIDE_INT); 7616259563Spfg } 7617259563Spfg } 7618259563Spfg 7619259563Spfg break; 7620259563Spfg 7621259563Spfg default: 7622259563Spfg gcc_unreachable (); 7623259563Spfg } 7624259563Spfg 7625259563Spfg if (width < HOST_BITS_PER_WIDE_INT) 7626259563Spfg return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), r_lo); 7627259563Spfg else 7628259563Spfg return build_int_cst_wide (TREE_TYPE (TREE_TYPE (fndecl)), r_lo, r_hi); 7629259563Spfg } 7630259563Spfg 7631259563Spfg return NULL_TREE; 7632259563Spfg} 7633132727Skan/* Return true if EXPR is the real constant contained in VALUE. */ 7634132727Skan 7635132727Skanstatic bool 7636132727Skanreal_dconstp (tree expr, const REAL_VALUE_TYPE *value) 7637132727Skan{ 7638132727Skan STRIP_NOPS (expr); 7639132727Skan 7640132727Skan return ((TREE_CODE (expr) == REAL_CST 7641169699Skan && ! TREE_CONSTANT_OVERFLOW (expr) 7642169699Skan && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), *value)) 7643169699Skan || (TREE_CODE (expr) == COMPLEX_CST 7644169699Skan && real_dconstp (TREE_REALPART (expr), value) 7645169699Skan && real_zerop (TREE_IMAGPART (expr)))); 7646132727Skan} 7647132727Skan 7648132727Skan/* A subroutine of fold_builtin to fold the various logarithmic 7649169699Skan functions. EXP is the CALL_EXPR of a call to a builtin logN 7650169699Skan function. VALUE is the base of the logN function. */ 7651132727Skan 7652132727Skanstatic tree 7653169699Skanfold_builtin_logarithm (tree fndecl, tree arglist, 7654169699Skan const REAL_VALUE_TYPE *value) 7655132727Skan{ 7656132727Skan if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7657132727Skan { 7658132727Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 7659132727Skan tree arg = TREE_VALUE (arglist); 7660132727Skan const enum built_in_function fcode = builtin_mathfn_code (arg); 7661169699Skan 7662169699Skan /* Optimize logN(1.0) = 0.0. */ 7663132727Skan if (real_onep (arg)) 7664132727Skan return build_real (type, dconst0); 7665132727Skan 7666132727Skan /* Optimize logN(N) = 1.0. If N can't be truncated to MODE 7667169699Skan exactly, then only do this if flag_unsafe_math_optimizations. */ 7668132727Skan if (exact_real_truncate (TYPE_MODE (type), value) 7669132727Skan || flag_unsafe_math_optimizations) 7670169699Skan { 7671132727Skan const REAL_VALUE_TYPE value_truncate = 7672132727Skan real_value_truncate (TYPE_MODE (type), *value); 7673132727Skan if (real_dconstp (arg, &value_truncate)) 7674132727Skan return build_real (type, dconst1); 7675132727Skan } 7676169699Skan 7677132727Skan /* Special case, optimize logN(expN(x)) = x. */ 7678132727Skan if (flag_unsafe_math_optimizations 7679132727Skan && ((value == &dconste 7680132727Skan && (fcode == BUILT_IN_EXP 7681132727Skan || fcode == BUILT_IN_EXPF 7682132727Skan || fcode == BUILT_IN_EXPL)) 7683132727Skan || (value == &dconst2 7684132727Skan && (fcode == BUILT_IN_EXP2 7685132727Skan || fcode == BUILT_IN_EXP2F 7686132727Skan || fcode == BUILT_IN_EXP2L)) 7687169699Skan || (value == &dconst10 && (BUILTIN_EXP10_P (fcode))))) 7688169699Skan return fold_convert (type, TREE_VALUE (TREE_OPERAND (arg, 1))); 7689132727Skan 7690169699Skan /* Optimize logN(func()) for various exponential functions. We 7691169699Skan want to determine the value "x" and the power "exponent" in 7692169699Skan order to transform logN(x**exponent) into exponent*logN(x). */ 7693132727Skan if (flag_unsafe_math_optimizations) 7694169699Skan { 7695132727Skan tree exponent = 0, x = 0; 7696169699Skan 7697132727Skan switch (fcode) 7698132727Skan { 7699169699Skan CASE_FLT_FN (BUILT_IN_EXP): 7700132727Skan /* Prepare to do logN(exp(exponent) -> exponent*logN(e). */ 7701132727Skan x = build_real (type, 7702132727Skan real_value_truncate (TYPE_MODE (type), dconste)); 7703132727Skan exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); 7704132727Skan break; 7705169699Skan CASE_FLT_FN (BUILT_IN_EXP2): 7706132727Skan /* Prepare to do logN(exp2(exponent) -> exponent*logN(2). */ 7707132727Skan x = build_real (type, dconst2); 7708132727Skan exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); 7709132727Skan break; 7710169699Skan CASE_FLT_FN (BUILT_IN_EXP10): 7711169699Skan CASE_FLT_FN (BUILT_IN_POW10): 7712132727Skan /* Prepare to do logN(exp10(exponent) -> exponent*logN(10). */ 7713132727Skan x = build_real (type, dconst10); 7714132727Skan exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); 7715132727Skan break; 7716169699Skan CASE_FLT_FN (BUILT_IN_SQRT): 7717132727Skan /* Prepare to do logN(sqrt(x) -> 0.5*logN(x). */ 7718132727Skan x = TREE_VALUE (TREE_OPERAND (arg, 1)); 7719132727Skan exponent = build_real (type, dconsthalf); 7720132727Skan break; 7721169699Skan CASE_FLT_FN (BUILT_IN_CBRT): 7722132727Skan /* Prepare to do logN(cbrt(x) -> (1/3)*logN(x). */ 7723132727Skan x = TREE_VALUE (TREE_OPERAND (arg, 1)); 7724132727Skan exponent = build_real (type, real_value_truncate (TYPE_MODE (type), 7725132727Skan dconstthird)); 7726132727Skan break; 7727169699Skan CASE_FLT_FN (BUILT_IN_POW): 7728132727Skan /* Prepare to do logN(pow(x,exponent) -> exponent*logN(x). */ 7729132727Skan x = TREE_VALUE (TREE_OPERAND (arg, 1)); 7730132727Skan exponent = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); 7731132727Skan break; 7732132727Skan default: 7733132727Skan break; 7734132727Skan } 7735132727Skan 7736132727Skan /* Now perform the optimization. */ 7737132727Skan if (x && exponent) 7738132727Skan { 7739132727Skan tree logfn; 7740132727Skan arglist = build_tree_list (NULL_TREE, x); 7741132727Skan logfn = build_function_call_expr (fndecl, arglist); 7742169699Skan return fold_build2 (MULT_EXPR, type, exponent, logfn); 7743132727Skan } 7744132727Skan } 7745132727Skan } 7746132727Skan 7747132727Skan return 0; 7748132727Skan} 7749169699Skan 7750169699Skan/* Fold a builtin function call to pow, powf, or powl. Return 7751169699Skan NULL_TREE if no simplification can be made. */ 7752169699Skanstatic tree 7753169699Skanfold_builtin_pow (tree fndecl, tree arglist, tree type) 7754169699Skan{ 7755169699Skan tree arg0 = TREE_VALUE (arglist); 7756169699Skan tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 7757169699Skan 7758169699Skan if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) 7759169699Skan return NULL_TREE; 7760169699Skan 7761169699Skan /* Optimize pow(1.0,y) = 1.0. */ 7762169699Skan if (real_onep (arg0)) 7763169699Skan return omit_one_operand (type, build_real (type, dconst1), arg1); 7764169699Skan 7765169699Skan if (TREE_CODE (arg1) == REAL_CST 7766169699Skan && ! TREE_CONSTANT_OVERFLOW (arg1)) 7767169699Skan { 7768169699Skan REAL_VALUE_TYPE cint; 7769169699Skan REAL_VALUE_TYPE c; 7770169699Skan HOST_WIDE_INT n; 7771169699Skan 7772169699Skan c = TREE_REAL_CST (arg1); 7773169699Skan 7774169699Skan /* Optimize pow(x,0.0) = 1.0. */ 7775169699Skan if (REAL_VALUES_EQUAL (c, dconst0)) 7776169699Skan return omit_one_operand (type, build_real (type, dconst1), 7777169699Skan arg0); 7778169699Skan 7779169699Skan /* Optimize pow(x,1.0) = x. */ 7780169699Skan if (REAL_VALUES_EQUAL (c, dconst1)) 7781169699Skan return arg0; 7782169699Skan 7783169699Skan /* Optimize pow(x,-1.0) = 1.0/x. */ 7784169699Skan if (REAL_VALUES_EQUAL (c, dconstm1)) 7785169699Skan return fold_build2 (RDIV_EXPR, type, 7786169699Skan build_real (type, dconst1), arg0); 7787169699Skan 7788169699Skan /* Optimize pow(x,0.5) = sqrt(x). */ 7789169699Skan if (flag_unsafe_math_optimizations 7790169699Skan && REAL_VALUES_EQUAL (c, dconsthalf)) 7791169699Skan { 7792169699Skan tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT); 7793169699Skan 7794169699Skan if (sqrtfn != NULL_TREE) 7795169699Skan { 7796169699Skan tree arglist = build_tree_list (NULL_TREE, arg0); 7797169699Skan return build_function_call_expr (sqrtfn, arglist); 7798169699Skan } 7799169699Skan } 7800169699Skan 7801169699Skan /* Check for an integer exponent. */ 7802169699Skan n = real_to_integer (&c); 7803169699Skan real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); 7804169699Skan if (real_identical (&c, &cint)) 7805169699Skan { 7806169699Skan /* Attempt to evaluate pow at compile-time. */ 7807169699Skan if (TREE_CODE (arg0) == REAL_CST 7808169699Skan && ! TREE_CONSTANT_OVERFLOW (arg0)) 7809169699Skan { 7810169699Skan REAL_VALUE_TYPE x; 7811169699Skan bool inexact; 7812169699Skan 7813169699Skan x = TREE_REAL_CST (arg0); 7814169699Skan inexact = real_powi (&x, TYPE_MODE (type), &x, n); 7815169699Skan if (flag_unsafe_math_optimizations || !inexact) 7816169699Skan return build_real (type, x); 7817169699Skan } 7818169699Skan 7819169699Skan /* Strip sign ops from even integer powers. */ 7820169699Skan if ((n & 1) == 0 && flag_unsafe_math_optimizations) 7821169699Skan { 7822169699Skan tree narg0 = fold_strip_sign_ops (arg0); 7823169699Skan if (narg0) 7824169699Skan { 7825169699Skan arglist = build_tree_list (NULL_TREE, arg1); 7826169699Skan arglist = tree_cons (NULL_TREE, narg0, arglist); 7827169699Skan return build_function_call_expr (fndecl, arglist); 7828169699Skan } 7829169699Skan } 7830169699Skan } 7831169699Skan } 7832169699Skan 7833169699Skan if (flag_unsafe_math_optimizations) 7834169699Skan { 7835169699Skan const enum built_in_function fcode = builtin_mathfn_code (arg0); 7836169699Skan 7837169699Skan /* Optimize pow(expN(x),y) = expN(x*y). */ 7838169699Skan if (BUILTIN_EXPONENT_P (fcode)) 7839169699Skan { 7840169699Skan tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0); 7841169699Skan tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1)); 7842169699Skan arg = fold_build2 (MULT_EXPR, type, arg, arg1); 7843169699Skan arglist = build_tree_list (NULL_TREE, arg); 7844169699Skan return build_function_call_expr (expfn, arglist); 7845169699Skan } 7846169699Skan 7847169699Skan /* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */ 7848169699Skan if (BUILTIN_SQRT_P (fcode)) 7849169699Skan { 7850169699Skan tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1)); 7851169699Skan tree narg1 = fold_build2 (MULT_EXPR, type, arg1, 7852169699Skan build_real (type, dconsthalf)); 7853169699Skan 7854169699Skan arglist = tree_cons (NULL_TREE, narg0, 7855169699Skan build_tree_list (NULL_TREE, narg1)); 7856169699Skan return build_function_call_expr (fndecl, arglist); 7857169699Skan } 7858169699Skan 7859169699Skan /* Optimize pow(cbrt(x),y) = pow(x,y/3) iff x is nonnegative. */ 7860169699Skan if (BUILTIN_CBRT_P (fcode)) 7861169699Skan { 7862169699Skan tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1)); 7863169699Skan if (tree_expr_nonnegative_p (arg)) 7864169699Skan { 7865169699Skan const REAL_VALUE_TYPE dconstroot 7866169699Skan = real_value_truncate (TYPE_MODE (type), dconstthird); 7867169699Skan tree narg1 = fold_build2 (MULT_EXPR, type, arg1, 7868169699Skan build_real (type, dconstroot)); 7869169699Skan arglist = tree_cons (NULL_TREE, arg, 7870169699Skan build_tree_list (NULL_TREE, narg1)); 7871169699Skan return build_function_call_expr (fndecl, arglist); 7872169699Skan } 7873169699Skan } 7874169699Skan 7875169699Skan /* Optimize pow(pow(x,y),z) = pow(x,y*z). */ 7876169699Skan if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF 7877169699Skan || fcode == BUILT_IN_POWL) 7878169699Skan { 7879169699Skan tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1)); 7880169699Skan tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1))); 7881169699Skan tree narg1 = fold_build2 (MULT_EXPR, type, arg01, arg1); 7882169699Skan arglist = tree_cons (NULL_TREE, arg00, 7883169699Skan build_tree_list (NULL_TREE, narg1)); 7884169699Skan return build_function_call_expr (fndecl, arglist); 7885169699Skan } 7886169699Skan } 7887169699Skan 7888169699Skan return NULL_TREE; 7889169699Skan} 7890169699Skan 7891169699Skan/* Fold a builtin function call to powi, powif, or powil. Return 7892169699Skan NULL_TREE if no simplification can be made. */ 7893169699Skanstatic tree 7894169699Skanfold_builtin_powi (tree fndecl ATTRIBUTE_UNUSED, tree arglist, tree type) 7895169699Skan{ 7896169699Skan tree arg0 = TREE_VALUE (arglist); 7897169699Skan tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 7898169699Skan 7899169699Skan if (!validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE)) 7900169699Skan return NULL_TREE; 7901169699Skan 7902169699Skan /* Optimize pow(1.0,y) = 1.0. */ 7903169699Skan if (real_onep (arg0)) 7904169699Skan return omit_one_operand (type, build_real (type, dconst1), arg1); 7905169699Skan 7906169699Skan if (host_integerp (arg1, 0)) 7907169699Skan { 7908169699Skan HOST_WIDE_INT c = TREE_INT_CST_LOW (arg1); 7909169699Skan 7910169699Skan /* Evaluate powi at compile-time. */ 7911169699Skan if (TREE_CODE (arg0) == REAL_CST 7912169699Skan && ! TREE_CONSTANT_OVERFLOW (arg0)) 7913169699Skan { 7914169699Skan REAL_VALUE_TYPE x; 7915169699Skan x = TREE_REAL_CST (arg0); 7916169699Skan real_powi (&x, TYPE_MODE (type), &x, c); 7917169699Skan return build_real (type, x); 7918169699Skan } 7919169699Skan 7920169699Skan /* Optimize pow(x,0) = 1.0. */ 7921169699Skan if (c == 0) 7922169699Skan return omit_one_operand (type, build_real (type, dconst1), 7923169699Skan arg0); 7924169699Skan 7925169699Skan /* Optimize pow(x,1) = x. */ 7926169699Skan if (c == 1) 7927169699Skan return arg0; 7928169699Skan 7929169699Skan /* Optimize pow(x,-1) = 1.0/x. */ 7930169699Skan if (c == -1) 7931169699Skan return fold_build2 (RDIV_EXPR, type, 7932169699Skan build_real (type, dconst1), arg0); 7933169699Skan } 7934169699Skan 7935169699Skan return NULL_TREE; 7936169699Skan} 7937169699Skan 7938132727Skan/* A subroutine of fold_builtin to fold the various exponent 7939132727Skan functions. EXP is the CALL_EXPR of a call to a builtin function. 7940132727Skan VALUE is the value which will be raised to a power. */ 7941132727Skan 7942132727Skanstatic tree 7943169699Skanfold_builtin_exponent (tree fndecl, tree arglist, 7944169699Skan const REAL_VALUE_TYPE *value) 7945132727Skan{ 7946132727Skan if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 7947132727Skan { 7948132727Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 7949132727Skan tree arg = TREE_VALUE (arglist); 7950132727Skan 7951132727Skan /* Optimize exp*(0.0) = 1.0. */ 7952132727Skan if (real_zerop (arg)) 7953132727Skan return build_real (type, dconst1); 7954132727Skan 7955132727Skan /* Optimize expN(1.0) = N. */ 7956132727Skan if (real_onep (arg)) 7957169699Skan { 7958132727Skan REAL_VALUE_TYPE cst; 7959132727Skan 7960132727Skan real_convert (&cst, TYPE_MODE (type), value); 7961132727Skan return build_real (type, cst); 7962132727Skan } 7963132727Skan 7964132727Skan /* Attempt to evaluate expN(integer) at compile-time. */ 7965132727Skan if (flag_unsafe_math_optimizations 7966132727Skan && TREE_CODE (arg) == REAL_CST 7967132727Skan && ! TREE_CONSTANT_OVERFLOW (arg)) 7968169699Skan { 7969132727Skan REAL_VALUE_TYPE cint; 7970132727Skan REAL_VALUE_TYPE c; 7971132727Skan HOST_WIDE_INT n; 7972132727Skan 7973132727Skan c = TREE_REAL_CST (arg); 7974132727Skan n = real_to_integer (&c); 7975132727Skan real_from_integer (&cint, VOIDmode, n, 7976132727Skan n < 0 ? -1 : 0, 0); 7977132727Skan if (real_identical (&c, &cint)) 7978132727Skan { 7979132727Skan REAL_VALUE_TYPE x; 7980132727Skan 7981132727Skan real_powi (&x, TYPE_MODE (type), value, n); 7982132727Skan return build_real (type, x); 7983132727Skan } 7984132727Skan } 7985132727Skan 7986132727Skan /* Optimize expN(logN(x)) = x. */ 7987132727Skan if (flag_unsafe_math_optimizations) 7988169699Skan { 7989132727Skan const enum built_in_function fcode = builtin_mathfn_code (arg); 7990132727Skan 7991132727Skan if ((value == &dconste 7992132727Skan && (fcode == BUILT_IN_LOG 7993132727Skan || fcode == BUILT_IN_LOGF 7994132727Skan || fcode == BUILT_IN_LOGL)) 7995132727Skan || (value == &dconst2 7996132727Skan && (fcode == BUILT_IN_LOG2 7997132727Skan || fcode == BUILT_IN_LOG2F 7998132727Skan || fcode == BUILT_IN_LOG2L)) 7999132727Skan || (value == &dconst10 8000132727Skan && (fcode == BUILT_IN_LOG10 8001132727Skan || fcode == BUILT_IN_LOG10F 8002132727Skan || fcode == BUILT_IN_LOG10L))) 8003169699Skan return fold_convert (type, TREE_VALUE (TREE_OPERAND (arg, 1))); 8004132727Skan } 8005132727Skan } 8006132727Skan 8007132727Skan return 0; 8008132727Skan} 8009132727Skan 8010169699Skan/* Return true if VAR is a VAR_DECL or a component thereof. */ 8011169699Skan 8012169699Skanstatic bool 8013169699Skanvar_decl_component_p (tree var) 8014169699Skan{ 8015169699Skan tree inner = var; 8016169699Skan while (handled_component_p (inner)) 8017169699Skan inner = TREE_OPERAND (inner, 0); 8018169699Skan return SSA_VAR_P (inner); 8019169699Skan} 8020169699Skan 8021169699Skan/* Fold function call to builtin memset. Return 8022132727Skan NULL_TREE if no simplification can be made. */ 8023132727Skan 8024132727Skanstatic tree 8025169699Skanfold_builtin_memset (tree arglist, tree type, bool ignore) 8026132727Skan{ 8027169699Skan tree dest, c, len, var, ret; 8028169699Skan unsigned HOST_WIDE_INT length, cval; 8029132727Skan 8030132727Skan if (!validate_arglist (arglist, 8031169699Skan POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) 8032132727Skan return 0; 8033132727Skan 8034132727Skan dest = TREE_VALUE (arglist); 8035169699Skan c = TREE_VALUE (TREE_CHAIN (arglist)); 8036132727Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 8037132727Skan 8038169699Skan if (! host_integerp (len, 1)) 8039169699Skan return 0; 8040169699Skan 8041132727Skan /* If the LEN parameter is zero, return DEST. */ 8042132727Skan if (integer_zerop (len)) 8043169699Skan return omit_one_operand (type, dest, c); 8044132727Skan 8045169699Skan if (! host_integerp (c, 1) || TREE_SIDE_EFFECTS (dest)) 8046169699Skan return 0; 8047132727Skan 8048169699Skan var = dest; 8049169699Skan STRIP_NOPS (var); 8050169699Skan if (TREE_CODE (var) != ADDR_EXPR) 8051169699Skan return 0; 8052169699Skan 8053169699Skan var = TREE_OPERAND (var, 0); 8054169699Skan if (TREE_THIS_VOLATILE (var)) 8055169699Skan return 0; 8056169699Skan 8057169699Skan if (!INTEGRAL_TYPE_P (TREE_TYPE (var)) 8058169699Skan && !POINTER_TYPE_P (TREE_TYPE (var))) 8059169699Skan return 0; 8060169699Skan 8061169699Skan if (! var_decl_component_p (var)) 8062169699Skan return 0; 8063169699Skan 8064169699Skan length = tree_low_cst (len, 1); 8065169699Skan if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (var))) != length 8066169699Skan || get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT 8067169699Skan < (int) length) 8068169699Skan return 0; 8069169699Skan 8070169699Skan if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT) 8071169699Skan return 0; 8072169699Skan 8073169699Skan if (integer_zerop (c)) 8074169699Skan cval = 0; 8075169699Skan else 8076169699Skan { 8077169699Skan if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64) 8078169699Skan return 0; 8079169699Skan 8080169699Skan cval = tree_low_cst (c, 1); 8081169699Skan cval &= 0xff; 8082169699Skan cval |= cval << 8; 8083169699Skan cval |= cval << 16; 8084169699Skan cval |= (cval << 31) << 1; 8085169699Skan } 8086169699Skan 8087169699Skan ret = build_int_cst_type (TREE_TYPE (var), cval); 8088169699Skan ret = build2 (MODIFY_EXPR, TREE_TYPE (var), var, ret); 8089169699Skan if (ignore) 8090169699Skan return ret; 8091169699Skan 8092169699Skan return omit_one_operand (type, dest, ret); 8093132727Skan} 8094132727Skan 8095169699Skan/* Fold function call to builtin memset. Return 8096132727Skan NULL_TREE if no simplification can be made. */ 8097132727Skan 8098132727Skanstatic tree 8099169699Skanfold_builtin_bzero (tree arglist, bool ignore) 8100132727Skan{ 8101169699Skan tree dest, size, newarglist; 8102132727Skan 8103169699Skan if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 8104132727Skan return 0; 8105132727Skan 8106169699Skan if (!ignore) 8107169699Skan return 0; 8108169699Skan 8109132727Skan dest = TREE_VALUE (arglist); 8110169699Skan size = TREE_VALUE (TREE_CHAIN (arglist)); 8111169699Skan 8112169699Skan /* New argument list transforming bzero(ptr x, int y) to 8113169699Skan memset(ptr x, int 0, size_t y). This is done this way 8114169699Skan so that if it isn't expanded inline, we fallback to 8115169699Skan calling bzero instead of memset. */ 8116169699Skan 8117169699Skan newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size)); 8118169699Skan newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist); 8119169699Skan newarglist = tree_cons (NULL_TREE, dest, newarglist); 8120169699Skan return fold_builtin_memset (newarglist, void_type_node, ignore); 8121169699Skan} 8122169699Skan 8123169699Skan/* Fold function call to builtin mem{{,p}cpy,move}. Return 8124169699Skan NULL_TREE if no simplification can be made. 8125169699Skan If ENDP is 0, return DEST (like memcpy). 8126169699Skan If ENDP is 1, return DEST+LEN (like mempcpy). 8127169699Skan If ENDP is 2, return DEST+LEN-1 (like stpcpy). 8128169699Skan If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap 8129169699Skan (memmove). */ 8130169699Skan 8131169699Skanstatic tree 8132169699Skanfold_builtin_memory_op (tree arglist, tree type, bool ignore, int endp) 8133169699Skan{ 8134169699Skan tree dest, src, len, destvar, srcvar, expr; 8135169699Skan unsigned HOST_WIDE_INT length; 8136169699Skan 8137169699Skan if (! validate_arglist (arglist, 8138169699Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 8139169699Skan return 0; 8140169699Skan 8141169699Skan dest = TREE_VALUE (arglist); 8142132727Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 8143132727Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 8144132727Skan 8145132727Skan /* If the LEN parameter is zero, return DEST. */ 8146132727Skan if (integer_zerop (len)) 8147169699Skan return omit_one_operand (type, dest, src); 8148132727Skan 8149169699Skan /* If SRC and DEST are the same (and not volatile), return 8150169699Skan DEST{,+LEN,+LEN-1}. */ 8151132727Skan if (operand_equal_p (src, dest, 0)) 8152169699Skan expr = len; 8153169699Skan else 8154132727Skan { 8155169699Skan if (! host_integerp (len, 1)) 8156169699Skan return 0; 8157169699Skan 8158169699Skan if (TREE_SIDE_EFFECTS (dest) || TREE_SIDE_EFFECTS (src)) 8159169699Skan return 0; 8160169699Skan 8161169699Skan destvar = dest; 8162169699Skan STRIP_NOPS (destvar); 8163169699Skan if (TREE_CODE (destvar) != ADDR_EXPR) 8164169699Skan return 0; 8165169699Skan 8166169699Skan destvar = TREE_OPERAND (destvar, 0); 8167169699Skan if (TREE_THIS_VOLATILE (destvar)) 8168169699Skan return 0; 8169169699Skan 8170169699Skan if (!INTEGRAL_TYPE_P (TREE_TYPE (destvar)) 8171169699Skan && !POINTER_TYPE_P (TREE_TYPE (destvar)) 8172169699Skan && !SCALAR_FLOAT_TYPE_P (TREE_TYPE (destvar))) 8173169699Skan return 0; 8174169699Skan 8175169699Skan if (! var_decl_component_p (destvar)) 8176169699Skan return 0; 8177169699Skan 8178169699Skan srcvar = src; 8179169699Skan STRIP_NOPS (srcvar); 8180169699Skan if (TREE_CODE (srcvar) != ADDR_EXPR) 8181169699Skan return 0; 8182169699Skan 8183169699Skan srcvar = TREE_OPERAND (srcvar, 0); 8184169699Skan if (TREE_THIS_VOLATILE (srcvar)) 8185169699Skan return 0; 8186169699Skan 8187169699Skan if (!INTEGRAL_TYPE_P (TREE_TYPE (srcvar)) 8188169699Skan && !POINTER_TYPE_P (TREE_TYPE (srcvar)) 8189169699Skan && !SCALAR_FLOAT_TYPE_P (TREE_TYPE (srcvar))) 8190169699Skan return 0; 8191169699Skan 8192169699Skan if (! var_decl_component_p (srcvar)) 8193169699Skan return 0; 8194169699Skan 8195169699Skan length = tree_low_cst (len, 1); 8196169699Skan if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (destvar))) != length 8197169699Skan || get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT 8198169699Skan < (int) length 8199169699Skan || GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (srcvar))) != length 8200169699Skan || get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT 8201169699Skan < (int) length) 8202169699Skan return 0; 8203169699Skan 8204169699Skan if ((INTEGRAL_TYPE_P (TREE_TYPE (srcvar)) 8205169699Skan || POINTER_TYPE_P (TREE_TYPE (srcvar))) 8206169699Skan && (INTEGRAL_TYPE_P (TREE_TYPE (destvar)) 8207169699Skan || POINTER_TYPE_P (TREE_TYPE (destvar)))) 8208169699Skan expr = fold_convert (TREE_TYPE (destvar), srcvar); 8209169699Skan else 8210169699Skan expr = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (destvar), srcvar); 8211169699Skan expr = build2 (MODIFY_EXPR, TREE_TYPE (destvar), destvar, expr); 8212132727Skan } 8213132727Skan 8214169699Skan if (ignore) 8215169699Skan return expr; 8216169699Skan 8217169699Skan if (endp == 0 || endp == 3) 8218169699Skan return omit_one_operand (type, dest, expr); 8219169699Skan 8220169699Skan if (expr == len) 8221169699Skan expr = 0; 8222169699Skan 8223169699Skan if (endp == 2) 8224169699Skan len = fold_build2 (MINUS_EXPR, TREE_TYPE (len), len, 8225169699Skan ssize_int (1)); 8226169699Skan 8227169699Skan len = fold_convert (TREE_TYPE (dest), len); 8228169699Skan dest = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len); 8229169699Skan dest = fold_convert (type, dest); 8230169699Skan if (expr) 8231169699Skan dest = omit_one_operand (type, dest, expr); 8232169699Skan return dest; 8233132727Skan} 8234132727Skan 8235169699Skan/* Fold function call to builtin bcopy. Return NULL_TREE if no 8236169699Skan simplification can be made. */ 8237132727Skan 8238132727Skanstatic tree 8239169699Skanfold_builtin_bcopy (tree arglist, bool ignore) 8240132727Skan{ 8241169699Skan tree src, dest, size, newarglist; 8242132727Skan 8243132727Skan if (!validate_arglist (arglist, 8244132727Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 8245132727Skan return 0; 8246132727Skan 8247169699Skan if (! ignore) 8248169699Skan return 0; 8249132727Skan 8250169699Skan src = TREE_VALUE (arglist); 8251169699Skan dest = TREE_VALUE (TREE_CHAIN (arglist)); 8252169699Skan size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 8253132727Skan 8254169699Skan /* New argument list transforming bcopy(ptr x, ptr y, int z) to 8255169699Skan memmove(ptr y, ptr x, size_t z). This is done this way 8256169699Skan so that if it isn't expanded inline, we fallback to 8257169699Skan calling bcopy instead of memmove. */ 8258132727Skan 8259169699Skan newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size)); 8260169699Skan newarglist = tree_cons (NULL_TREE, src, newarglist); 8261169699Skan newarglist = tree_cons (NULL_TREE, dest, newarglist); 8262169699Skan 8263169699Skan return fold_builtin_memory_op (newarglist, void_type_node, true, /*endp=*/3); 8264132727Skan} 8265132727Skan 8266169699Skan/* Fold function call to builtin strcpy. If LEN is not NULL, it represents 8267169699Skan the length of the string to be copied. Return NULL_TREE if no 8268169699Skan simplification can be made. */ 8269132727Skan 8270169699Skantree 8271169699Skanfold_builtin_strcpy (tree fndecl, tree arglist, tree len) 8272132727Skan{ 8273169699Skan tree dest, src, fn; 8274132727Skan 8275132727Skan if (!validate_arglist (arglist, 8276132727Skan POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 8277132727Skan return 0; 8278132727Skan 8279132727Skan dest = TREE_VALUE (arglist); 8280132727Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 8281132727Skan 8282132727Skan /* If SRC and DEST are the same (and not volatile), return DEST. */ 8283132727Skan if (operand_equal_p (src, dest, 0)) 8284169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest); 8285132727Skan 8286169699Skan if (optimize_size) 8287169699Skan return 0; 8288169699Skan 8289169699Skan fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; 8290169699Skan if (!fn) 8291169699Skan return 0; 8292169699Skan 8293169699Skan if (!len) 8294169699Skan { 8295169699Skan len = c_strlen (src, 1); 8296169699Skan if (! len || TREE_SIDE_EFFECTS (len)) 8297169699Skan return 0; 8298169699Skan } 8299169699Skan 8300169699Skan len = size_binop (PLUS_EXPR, len, ssize_int (1)); 8301169699Skan arglist = build_tree_list (NULL_TREE, len); 8302169699Skan arglist = tree_cons (NULL_TREE, src, arglist); 8303169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 8304169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), 8305169699Skan build_function_call_expr (fn, arglist)); 8306132727Skan} 8307132727Skan 8308169699Skan/* Fold function call to builtin strncpy. If SLEN is not NULL, it represents 8309169699Skan the length of the source string. Return NULL_TREE if no simplification 8310169699Skan can be made. */ 8311132727Skan 8312169699Skantree 8313169699Skanfold_builtin_strncpy (tree fndecl, tree arglist, tree slen) 8314132727Skan{ 8315169699Skan tree dest, src, len, fn; 8316132727Skan 8317132727Skan if (!validate_arglist (arglist, 8318132727Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 8319132727Skan return 0; 8320132727Skan 8321132727Skan dest = TREE_VALUE (arglist); 8322132727Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 8323132727Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 8324132727Skan 8325132727Skan /* If the LEN parameter is zero, return DEST. */ 8326132727Skan if (integer_zerop (len)) 8327169699Skan return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); 8328132727Skan 8329169699Skan /* We can't compare slen with len as constants below if len is not a 8330169699Skan constant. */ 8331169699Skan if (len == 0 || TREE_CODE (len) != INTEGER_CST) 8332169699Skan return 0; 8333169699Skan 8334169699Skan if (!slen) 8335169699Skan slen = c_strlen (src, 1); 8336169699Skan 8337169699Skan /* Now, we must be passed a constant src ptr parameter. */ 8338169699Skan if (slen == 0 || TREE_CODE (slen) != INTEGER_CST) 8339169699Skan return 0; 8340169699Skan 8341169699Skan slen = size_binop (PLUS_EXPR, slen, ssize_int (1)); 8342169699Skan 8343169699Skan /* We do not support simplification of this case, though we do 8344169699Skan support it when expanding trees into RTL. */ 8345169699Skan /* FIXME: generate a call to __builtin_memset. */ 8346169699Skan if (tree_int_cst_lt (slen, len)) 8347169699Skan return 0; 8348169699Skan 8349169699Skan /* OK transform into builtin memcpy. */ 8350169699Skan fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; 8351169699Skan if (!fn) 8352169699Skan return 0; 8353169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), 8354169699Skan build_function_call_expr (fn, arglist)); 8355132727Skan} 8356132727Skan 8357132727Skan/* Fold function call to builtin memcmp. Return 8358132727Skan NULL_TREE if no simplification can be made. */ 8359132727Skan 8360132727Skanstatic tree 8361169699Skanfold_builtin_memcmp (tree arglist) 8362132727Skan{ 8363132727Skan tree arg1, arg2, len; 8364169699Skan const char *p1, *p2; 8365132727Skan 8366132727Skan if (!validate_arglist (arglist, 8367132727Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 8368132727Skan return 0; 8369132727Skan 8370132727Skan arg1 = TREE_VALUE (arglist); 8371132727Skan arg2 = TREE_VALUE (TREE_CHAIN (arglist)); 8372132727Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 8373132727Skan 8374132727Skan /* If the LEN parameter is zero, return zero. */ 8375132727Skan if (integer_zerop (len)) 8376169699Skan return omit_two_operands (integer_type_node, integer_zero_node, 8377169699Skan arg1, arg2); 8378132727Skan 8379132727Skan /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ 8380132727Skan if (operand_equal_p (arg1, arg2, 0)) 8381169699Skan return omit_one_operand (integer_type_node, integer_zero_node, len); 8382132727Skan 8383169699Skan p1 = c_getstr (arg1); 8384169699Skan p2 = c_getstr (arg2); 8385169699Skan 8386169699Skan /* If all arguments are constant, and the value of len is not greater 8387169699Skan than the lengths of arg1 and arg2, evaluate at compile-time. */ 8388169699Skan if (host_integerp (len, 1) && p1 && p2 8389169699Skan && compare_tree_int (len, strlen (p1) + 1) <= 0 8390169699Skan && compare_tree_int (len, strlen (p2) + 1) <= 0) 8391169699Skan { 8392169699Skan const int r = memcmp (p1, p2, tree_low_cst (len, 1)); 8393169699Skan 8394169699Skan if (r > 0) 8395169699Skan return integer_one_node; 8396169699Skan else if (r < 0) 8397169699Skan return integer_minus_one_node; 8398169699Skan else 8399169699Skan return integer_zero_node; 8400169699Skan } 8401169699Skan 8402169699Skan /* If len parameter is one, return an expression corresponding to 8403169699Skan (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */ 8404169699Skan if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1) 8405169699Skan { 8406169699Skan tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); 8407169699Skan tree cst_uchar_ptr_node 8408169699Skan = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); 8409169699Skan 8410169699Skan tree ind1 = fold_convert (integer_type_node, 8411169699Skan build1 (INDIRECT_REF, cst_uchar_node, 8412169699Skan fold_convert (cst_uchar_ptr_node, 8413169699Skan arg1))); 8414169699Skan tree ind2 = fold_convert (integer_type_node, 8415169699Skan build1 (INDIRECT_REF, cst_uchar_node, 8416169699Skan fold_convert (cst_uchar_ptr_node, 8417169699Skan arg2))); 8418169699Skan return fold_build2 (MINUS_EXPR, integer_type_node, ind1, ind2); 8419169699Skan } 8420169699Skan 8421132727Skan return 0; 8422132727Skan} 8423132727Skan 8424132727Skan/* Fold function call to builtin strcmp. Return 8425132727Skan NULL_TREE if no simplification can be made. */ 8426132727Skan 8427132727Skanstatic tree 8428169699Skanfold_builtin_strcmp (tree arglist) 8429132727Skan{ 8430132727Skan tree arg1, arg2; 8431132727Skan const char *p1, *p2; 8432132727Skan 8433169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 8434132727Skan return 0; 8435132727Skan 8436132727Skan arg1 = TREE_VALUE (arglist); 8437132727Skan arg2 = TREE_VALUE (TREE_CHAIN (arglist)); 8438132727Skan 8439132727Skan /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ 8440132727Skan if (operand_equal_p (arg1, arg2, 0)) 8441169699Skan return integer_zero_node; 8442132727Skan 8443132727Skan p1 = c_getstr (arg1); 8444132727Skan p2 = c_getstr (arg2); 8445132727Skan 8446132727Skan if (p1 && p2) 8447132727Skan { 8448132727Skan const int i = strcmp (p1, p2); 8449132727Skan if (i < 0) 8450169699Skan return integer_minus_one_node; 8451132727Skan else if (i > 0) 8452169699Skan return integer_one_node; 8453132727Skan else 8454169699Skan return integer_zero_node; 8455132727Skan } 8456132727Skan 8457169699Skan /* If the second arg is "", return *(const unsigned char*)arg1. */ 8458169699Skan if (p2 && *p2 == '\0') 8459169699Skan { 8460169699Skan tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); 8461169699Skan tree cst_uchar_ptr_node 8462169699Skan = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); 8463169699Skan 8464169699Skan return fold_convert (integer_type_node, 8465169699Skan build1 (INDIRECT_REF, cst_uchar_node, 8466169699Skan fold_convert (cst_uchar_ptr_node, 8467169699Skan arg1))); 8468169699Skan } 8469169699Skan 8470169699Skan /* If the first arg is "", return -*(const unsigned char*)arg2. */ 8471169699Skan if (p1 && *p1 == '\0') 8472169699Skan { 8473169699Skan tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); 8474169699Skan tree cst_uchar_ptr_node 8475169699Skan = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); 8476169699Skan 8477169699Skan tree temp = fold_convert (integer_type_node, 8478169699Skan build1 (INDIRECT_REF, cst_uchar_node, 8479169699Skan fold_convert (cst_uchar_ptr_node, 8480169699Skan arg2))); 8481169699Skan return fold_build1 (NEGATE_EXPR, integer_type_node, temp); 8482169699Skan } 8483169699Skan 8484132727Skan return 0; 8485132727Skan} 8486132727Skan 8487132727Skan/* Fold function call to builtin strncmp. Return 8488132727Skan NULL_TREE if no simplification can be made. */ 8489132727Skan 8490132727Skanstatic tree 8491169699Skanfold_builtin_strncmp (tree arglist) 8492132727Skan{ 8493132727Skan tree arg1, arg2, len; 8494132727Skan const char *p1, *p2; 8495132727Skan 8496132727Skan if (!validate_arglist (arglist, 8497132727Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 8498132727Skan return 0; 8499132727Skan 8500132727Skan arg1 = TREE_VALUE (arglist); 8501132727Skan arg2 = TREE_VALUE (TREE_CHAIN (arglist)); 8502132727Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 8503132727Skan 8504132727Skan /* If the LEN parameter is zero, return zero. */ 8505132727Skan if (integer_zerop (len)) 8506169699Skan return omit_two_operands (integer_type_node, integer_zero_node, 8507169699Skan arg1, arg2); 8508132727Skan 8509132727Skan /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ 8510132727Skan if (operand_equal_p (arg1, arg2, 0)) 8511169699Skan return omit_one_operand (integer_type_node, integer_zero_node, len); 8512132727Skan 8513132727Skan p1 = c_getstr (arg1); 8514132727Skan p2 = c_getstr (arg2); 8515132727Skan 8516132727Skan if (host_integerp (len, 1) && p1 && p2) 8517132727Skan { 8518132727Skan const int i = strncmp (p1, p2, tree_low_cst (len, 1)); 8519169699Skan if (i > 0) 8520169699Skan return integer_one_node; 8521169699Skan else if (i < 0) 8522169699Skan return integer_minus_one_node; 8523132727Skan else 8524169699Skan return integer_zero_node; 8525132727Skan } 8526132727Skan 8527169699Skan /* If the second arg is "", and the length is greater than zero, 8528169699Skan return *(const unsigned char*)arg1. */ 8529169699Skan if (p2 && *p2 == '\0' 8530169699Skan && TREE_CODE (len) == INTEGER_CST 8531169699Skan && tree_int_cst_sgn (len) == 1) 8532169699Skan { 8533169699Skan tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); 8534169699Skan tree cst_uchar_ptr_node 8535169699Skan = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); 8536169699Skan 8537169699Skan return fold_convert (integer_type_node, 8538169699Skan build1 (INDIRECT_REF, cst_uchar_node, 8539169699Skan fold_convert (cst_uchar_ptr_node, 8540169699Skan arg1))); 8541169699Skan } 8542169699Skan 8543169699Skan /* If the first arg is "", and the length is greater than zero, 8544169699Skan return -*(const unsigned char*)arg2. */ 8545169699Skan if (p1 && *p1 == '\0' 8546169699Skan && TREE_CODE (len) == INTEGER_CST 8547169699Skan && tree_int_cst_sgn (len) == 1) 8548169699Skan { 8549169699Skan tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); 8550169699Skan tree cst_uchar_ptr_node 8551169699Skan = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); 8552169699Skan 8553169699Skan tree temp = fold_convert (integer_type_node, 8554169699Skan build1 (INDIRECT_REF, cst_uchar_node, 8555169699Skan fold_convert (cst_uchar_ptr_node, 8556169699Skan arg2))); 8557169699Skan return fold_build1 (NEGATE_EXPR, integer_type_node, temp); 8558169699Skan } 8559169699Skan 8560169699Skan /* If len parameter is one, return an expression corresponding to 8561169699Skan (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */ 8562169699Skan if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1) 8563169699Skan { 8564169699Skan tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); 8565169699Skan tree cst_uchar_ptr_node 8566169699Skan = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); 8567169699Skan 8568169699Skan tree ind1 = fold_convert (integer_type_node, 8569169699Skan build1 (INDIRECT_REF, cst_uchar_node, 8570169699Skan fold_convert (cst_uchar_ptr_node, 8571169699Skan arg1))); 8572169699Skan tree ind2 = fold_convert (integer_type_node, 8573169699Skan build1 (INDIRECT_REF, cst_uchar_node, 8574169699Skan fold_convert (cst_uchar_ptr_node, 8575169699Skan arg2))); 8576169699Skan return fold_build2 (MINUS_EXPR, integer_type_node, ind1, ind2); 8577169699Skan } 8578169699Skan 8579132727Skan return 0; 8580132727Skan} 8581132727Skan 8582169699Skan/* Fold function call to builtin signbit, signbitf or signbitl. Return 8583169699Skan NULL_TREE if no simplification can be made. */ 858490075Sobrien 8585169699Skanstatic tree 8586169699Skanfold_builtin_signbit (tree fndecl, tree arglist) 858790075Sobrien{ 8588132727Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 8589169699Skan tree arg, temp; 859090075Sobrien 8591169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 8592169699Skan return NULL_TREE; 8593169699Skan 8594169699Skan arg = TREE_VALUE (arglist); 8595169699Skan 8596169699Skan /* If ARG is a compile-time constant, determine the result. */ 8597169699Skan if (TREE_CODE (arg) == REAL_CST 8598169699Skan && !TREE_CONSTANT_OVERFLOW (arg)) 8599169699Skan { 8600169699Skan REAL_VALUE_TYPE c; 8601169699Skan 8602169699Skan c = TREE_REAL_CST (arg); 8603169699Skan temp = REAL_VALUE_NEGATIVE (c) ? integer_one_node : integer_zero_node; 8604169699Skan return fold_convert (type, temp); 8605169699Skan } 8606169699Skan 8607169699Skan /* If ARG is non-negative, the result is always zero. */ 8608169699Skan if (tree_expr_nonnegative_p (arg)) 8609169699Skan return omit_one_operand (type, integer_zero_node, arg); 8610169699Skan 8611169699Skan /* If ARG's format doesn't have signed zeros, return "arg < 0.0". */ 8612169699Skan if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg)))) 8613169699Skan return fold_build2 (LT_EXPR, type, arg, 8614169699Skan build_real (TREE_TYPE (arg), dconst0)); 8615169699Skan 8616169699Skan return NULL_TREE; 8617169699Skan} 8618169699Skan 8619169699Skan/* Fold function call to builtin copysign, copysignf or copysignl. 8620169699Skan Return NULL_TREE if no simplification can be made. */ 8621169699Skan 8622169699Skanstatic tree 8623169699Skanfold_builtin_copysign (tree fndecl, tree arglist, tree type) 8624169699Skan{ 8625169699Skan tree arg1, arg2, tem; 8626169699Skan 8627169699Skan if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) 8628169699Skan return NULL_TREE; 8629169699Skan 8630169699Skan arg1 = TREE_VALUE (arglist); 8631169699Skan arg2 = TREE_VALUE (TREE_CHAIN (arglist)); 8632169699Skan 8633169699Skan /* copysign(X,X) is X. */ 8634169699Skan if (operand_equal_p (arg1, arg2, 0)) 8635169699Skan return fold_convert (type, arg1); 8636169699Skan 8637169699Skan /* If ARG1 and ARG2 are compile-time constants, determine the result. */ 8638169699Skan if (TREE_CODE (arg1) == REAL_CST 8639169699Skan && TREE_CODE (arg2) == REAL_CST 8640169699Skan && !TREE_CONSTANT_OVERFLOW (arg1) 8641169699Skan && !TREE_CONSTANT_OVERFLOW (arg2)) 8642169699Skan { 8643169699Skan REAL_VALUE_TYPE c1, c2; 8644169699Skan 8645169699Skan c1 = TREE_REAL_CST (arg1); 8646169699Skan c2 = TREE_REAL_CST (arg2); 8647169699Skan /* c1.sign := c2.sign. */ 8648169699Skan real_copysign (&c1, &c2); 8649169699Skan return build_real (type, c1); 8650169699Skan } 8651169699Skan 8652169699Skan /* copysign(X, Y) is fabs(X) when Y is always non-negative. 8653169699Skan Remember to evaluate Y for side-effects. */ 8654169699Skan if (tree_expr_nonnegative_p (arg2)) 8655169699Skan return omit_one_operand (type, 8656169699Skan fold_build1 (ABS_EXPR, type, arg1), 8657169699Skan arg2); 8658169699Skan 8659169699Skan /* Strip sign changing operations for the first argument. */ 8660169699Skan tem = fold_strip_sign_ops (arg1); 8661169699Skan if (tem) 8662169699Skan { 8663169699Skan arglist = tree_cons (NULL_TREE, tem, TREE_CHAIN (arglist)); 8664169699Skan return build_function_call_expr (fndecl, arglist); 8665169699Skan } 8666169699Skan 8667169699Skan return NULL_TREE; 8668169699Skan} 8669169699Skan 8670169699Skan/* Fold a call to builtin isascii. */ 8671169699Skan 8672169699Skanstatic tree 8673169699Skanfold_builtin_isascii (tree arglist) 8674169699Skan{ 8675169699Skan if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 867690075Sobrien return 0; 8677169699Skan else 8678169699Skan { 8679169699Skan /* Transform isascii(c) -> ((c & ~0x7f) == 0). */ 8680169699Skan tree arg = TREE_VALUE (arglist); 868190075Sobrien 8682169699Skan arg = build2 (BIT_AND_EXPR, integer_type_node, arg, 8683169699Skan build_int_cst (NULL_TREE, 8684169699Skan ~ (unsigned HOST_WIDE_INT) 0x7f)); 8685169699Skan arg = fold_build2 (EQ_EXPR, integer_type_node, 8686169699Skan arg, integer_zero_node); 8687169699Skan 8688169699Skan if (in_gimple_form && !TREE_CONSTANT (arg)) 8689169699Skan return NULL_TREE; 8690169699Skan else 8691169699Skan return arg; 8692169699Skan } 8693169699Skan} 8694169699Skan 8695169699Skan/* Fold a call to builtin toascii. */ 8696169699Skan 8697169699Skanstatic tree 8698169699Skanfold_builtin_toascii (tree arglist) 8699169699Skan{ 8700169699Skan if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 8701169699Skan return 0; 8702169699Skan else 870390075Sobrien { 8704169699Skan /* Transform toascii(c) -> (c & 0x7f). */ 8705169699Skan tree arg = TREE_VALUE (arglist); 870690075Sobrien 8707169699Skan return fold_build2 (BIT_AND_EXPR, integer_type_node, arg, 8708169699Skan build_int_cst (NULL_TREE, 0x7f)); 8709169699Skan } 8710169699Skan} 871190075Sobrien 8712169699Skan/* Fold a call to builtin isdigit. */ 871390075Sobrien 8714169699Skanstatic tree 8715169699Skanfold_builtin_isdigit (tree arglist) 8716169699Skan{ 8717169699Skan if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 8718169699Skan return 0; 8719169699Skan else 8720169699Skan { 8721169699Skan /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9. */ 8722169699Skan /* According to the C standard, isdigit is unaffected by locale. 8723169699Skan However, it definitely is affected by the target character set. */ 8724169699Skan tree arg; 8725169699Skan unsigned HOST_WIDE_INT target_digit0 8726169699Skan = lang_hooks.to_target_charset ('0'); 8727132727Skan 8728169699Skan if (target_digit0 == 0) 8729169699Skan return NULL_TREE; 8730132727Skan 8731169699Skan arg = fold_convert (unsigned_type_node, TREE_VALUE (arglist)); 8732169699Skan arg = build2 (MINUS_EXPR, unsigned_type_node, arg, 8733169699Skan build_int_cst (unsigned_type_node, target_digit0)); 8734169699Skan arg = fold_build2 (LE_EXPR, integer_type_node, arg, 8735169699Skan build_int_cst (unsigned_type_node, 9)); 8736169699Skan if (in_gimple_form && !TREE_CONSTANT (arg)) 8737169699Skan return NULL_TREE; 8738169699Skan else 8739169699Skan return arg; 8740169699Skan } 8741169699Skan} 8742132727Skan 8743169699Skan/* Fold a call to fabs, fabsf or fabsl. */ 8744132727Skan 8745169699Skanstatic tree 8746169699Skanfold_builtin_fabs (tree arglist, tree type) 8747169699Skan{ 8748169699Skan tree arg; 8749132727Skan 8750169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 8751169699Skan return 0; 8752132727Skan 8753169699Skan arg = TREE_VALUE (arglist); 8754169699Skan arg = fold_convert (type, arg); 8755169699Skan if (TREE_CODE (arg) == REAL_CST) 8756169699Skan return fold_abs_const (arg, type); 8757169699Skan return fold_build1 (ABS_EXPR, type, arg); 8758169699Skan} 8759132727Skan 8760169699Skan/* Fold a call to abs, labs, llabs or imaxabs. */ 8761132727Skan 8762169699Skanstatic tree 8763169699Skanfold_builtin_abs (tree arglist, tree type) 8764169699Skan{ 8765169699Skan tree arg; 8766132727Skan 8767169699Skan if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) 8768169699Skan return 0; 8769132727Skan 8770169699Skan arg = TREE_VALUE (arglist); 8771169699Skan arg = fold_convert (type, arg); 8772169699Skan if (TREE_CODE (arg) == INTEGER_CST) 8773169699Skan return fold_abs_const (arg, type); 8774169699Skan return fold_build1 (ABS_EXPR, type, arg); 8775169699Skan} 8776132727Skan 8777169699Skan/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite. 8778169699Skan EXP is the CALL_EXPR for the call. */ 8779132727Skan 8780169699Skanstatic tree 8781169699Skanfold_builtin_classify (tree fndecl, tree arglist, int builtin_index) 8782169699Skan{ 8783169699Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 8784169699Skan tree arg; 8785169699Skan REAL_VALUE_TYPE r; 8786132727Skan 8787169699Skan if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) 8788169699Skan { 8789169699Skan /* Check that we have exactly one argument. */ 8790169699Skan if (arglist == 0) 8791132727Skan { 8792169699Skan error ("too few arguments to function %qs", 8793169699Skan IDENTIFIER_POINTER (DECL_NAME (fndecl))); 8794169699Skan return error_mark_node; 8795169699Skan } 8796169699Skan else if (TREE_CHAIN (arglist) != 0) 8797169699Skan { 8798169699Skan error ("too many arguments to function %qs", 8799169699Skan IDENTIFIER_POINTER (DECL_NAME (fndecl))); 8800169699Skan return error_mark_node; 8801169699Skan } 8802169699Skan else 8803169699Skan { 8804169699Skan error ("non-floating-point argument to function %qs", 8805169699Skan IDENTIFIER_POINTER (DECL_NAME (fndecl))); 8806169699Skan return error_mark_node; 8807169699Skan } 8808169699Skan } 8809132727Skan 8810169699Skan arg = TREE_VALUE (arglist); 8811169699Skan switch (builtin_index) 8812169699Skan { 8813169699Skan case BUILT_IN_ISINF: 8814228756Spfg if (!HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (arg)))) 8815169699Skan return omit_one_operand (type, integer_zero_node, arg); 8816132727Skan 8817169699Skan if (TREE_CODE (arg) == REAL_CST) 8818169699Skan { 8819169699Skan r = TREE_REAL_CST (arg); 8820169699Skan if (real_isinf (&r)) 8821169699Skan return real_compare (GT_EXPR, &r, &dconst0) 8822169699Skan ? integer_one_node : integer_minus_one_node; 8823169699Skan else 8824169699Skan return integer_zero_node; 8825132727Skan } 8826132727Skan 8827169699Skan return NULL_TREE; 8828169699Skan 8829169699Skan case BUILT_IN_FINITE: 8830228756Spfg if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg))) 8831228756Spfg && !HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (arg)))) 8832233923Spfg return omit_one_operand (type, integer_one_node, arg); 8833169699Skan 8834169699Skan if (TREE_CODE (arg) == REAL_CST) 8835132727Skan { 8836169699Skan r = TREE_REAL_CST (arg); 8837169699Skan return real_isinf (&r) || real_isnan (&r) 8838169699Skan ? integer_zero_node : integer_one_node; 8839169699Skan } 8840132727Skan 8841169699Skan return NULL_TREE; 8842132727Skan 8843169699Skan case BUILT_IN_ISNAN: 8844228756Spfg if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg)))) 8845169699Skan return omit_one_operand (type, integer_zero_node, arg); 8846132727Skan 8847169699Skan if (TREE_CODE (arg) == REAL_CST) 8848169699Skan { 8849169699Skan r = TREE_REAL_CST (arg); 8850169699Skan return real_isnan (&r) ? integer_one_node : integer_zero_node; 8851132727Skan } 8852132727Skan 8853169699Skan arg = builtin_save_expr (arg); 8854169699Skan return fold_build2 (UNORDERED_EXPR, type, arg, arg); 8855169699Skan 8856169699Skan default: 8857169699Skan gcc_unreachable (); 8858169699Skan } 8859169699Skan} 8860169699Skan 8861169699Skan/* Fold a call to an unordered comparison function such as 8862169699Skan __builtin_isgreater(). FNDECL is the FUNCTION_DECL for the function 8863169699Skan being called and ARGLIST is the argument list for the call. 8864169699Skan UNORDERED_CODE and ORDERED_CODE are comparison codes that give 8865169699Skan the opposite of the desired result. UNORDERED_CODE is used 8866169699Skan for modes that can hold NaNs and ORDERED_CODE is used for 8867169699Skan the rest. */ 8868169699Skan 8869169699Skanstatic tree 8870169699Skanfold_builtin_unordered_cmp (tree fndecl, tree arglist, 8871169699Skan enum tree_code unordered_code, 8872169699Skan enum tree_code ordered_code) 8873169699Skan{ 8874169699Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 8875169699Skan enum tree_code code; 8876169699Skan tree arg0, arg1; 8877169699Skan tree type0, type1; 8878169699Skan enum tree_code code0, code1; 8879169699Skan tree cmp_type = NULL_TREE; 8880169699Skan 8881169699Skan if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) 8882169699Skan { 8883169699Skan /* Check that we have exactly two arguments. */ 8884169699Skan if (arglist == 0 || TREE_CHAIN (arglist) == 0) 8885132727Skan { 8886169699Skan error ("too few arguments to function %qs", 8887169699Skan IDENTIFIER_POINTER (DECL_NAME (fndecl))); 8888169699Skan return error_mark_node; 8889169699Skan } 8890169699Skan else if (TREE_CHAIN (TREE_CHAIN (arglist)) != 0) 8891169699Skan { 8892169699Skan error ("too many arguments to function %qs", 8893169699Skan IDENTIFIER_POINTER (DECL_NAME (fndecl))); 8894169699Skan return error_mark_node; 8895169699Skan } 8896169699Skan } 8897132727Skan 8898169699Skan arg0 = TREE_VALUE (arglist); 8899169699Skan arg1 = TREE_VALUE (TREE_CHAIN (arglist)); 8900132727Skan 8901169699Skan type0 = TREE_TYPE (arg0); 8902169699Skan type1 = TREE_TYPE (arg1); 8903132727Skan 8904169699Skan code0 = TREE_CODE (type0); 8905169699Skan code1 = TREE_CODE (type1); 8906132727Skan 8907169699Skan if (code0 == REAL_TYPE && code1 == REAL_TYPE) 8908169699Skan /* Choose the wider of two real types. */ 8909169699Skan cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1) 8910169699Skan ? type0 : type1; 8911169699Skan else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE) 8912169699Skan cmp_type = type0; 8913169699Skan else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE) 8914169699Skan cmp_type = type1; 8915169699Skan else 8916169699Skan { 8917169699Skan error ("non-floating-point argument to function %qs", 8918169699Skan IDENTIFIER_POINTER (DECL_NAME (fndecl))); 8919169699Skan return error_mark_node; 8920169699Skan } 8921132727Skan 8922169699Skan arg0 = fold_convert (cmp_type, arg0); 8923169699Skan arg1 = fold_convert (cmp_type, arg1); 8924132727Skan 8925169699Skan if (unordered_code == UNORDERED_EXPR) 8926169699Skan { 8927228756Spfg if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg0)))) 8928169699Skan return omit_two_operands (type, integer_zero_node, arg0, arg1); 8929169699Skan return fold_build2 (UNORDERED_EXPR, type, arg0, arg1); 8930169699Skan } 8931132727Skan 8932228756Spfg code = HONOR_NANS (TYPE_MODE (TREE_TYPE (arg0))) ? unordered_code 8933228756Spfg : ordered_code; 8934169699Skan return fold_build1 (TRUTH_NOT_EXPR, type, 8935169699Skan fold_build2 (code, type, arg0, arg1)); 8936169699Skan} 8937132727Skan 8938169699Skan/* Used by constant folding to simplify calls to builtin functions. EXP is 8939169699Skan the CALL_EXPR of a call to a builtin function. IGNORE is true if the 8940169699Skan result of the function call is ignored. This function returns NULL_TREE 8941169699Skan if no simplification was possible. */ 8942132727Skan 8943169699Skanstatic tree 8944169699Skanfold_builtin_1 (tree fndecl, tree arglist, bool ignore) 8945169699Skan{ 8946169699Skan tree type = TREE_TYPE (TREE_TYPE (fndecl)); 8947169699Skan enum built_in_function fcode; 8948132727Skan 8949169699Skan if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) 8950169699Skan return targetm.fold_builtin (fndecl, arglist, ignore); 8951132727Skan 8952169699Skan fcode = DECL_FUNCTION_CODE (fndecl); 8953169699Skan switch (fcode) 8954169699Skan { 8955169699Skan case BUILT_IN_FPUTS: 8956169699Skan return fold_builtin_fputs (arglist, ignore, false, NULL_TREE); 8957132727Skan 8958169699Skan case BUILT_IN_FPUTS_UNLOCKED: 8959169699Skan return fold_builtin_fputs (arglist, ignore, true, NULL_TREE); 8960132727Skan 8961169699Skan case BUILT_IN_STRSTR: 8962169699Skan return fold_builtin_strstr (arglist, type); 8963132727Skan 8964169699Skan case BUILT_IN_STRCAT: 8965169699Skan return fold_builtin_strcat (arglist); 8966132727Skan 8967169699Skan case BUILT_IN_STRNCAT: 8968169699Skan return fold_builtin_strncat (arglist); 8969169699Skan 8970169699Skan case BUILT_IN_STRSPN: 8971169699Skan return fold_builtin_strspn (arglist); 8972169699Skan 8973169699Skan case BUILT_IN_STRCSPN: 8974169699Skan return fold_builtin_strcspn (arglist); 8975169699Skan 8976169699Skan case BUILT_IN_STRCHR: 8977169699Skan case BUILT_IN_INDEX: 8978169699Skan return fold_builtin_strchr (arglist, type); 8979169699Skan 8980169699Skan case BUILT_IN_STRRCHR: 8981169699Skan case BUILT_IN_RINDEX: 8982169699Skan return fold_builtin_strrchr (arglist, type); 8983169699Skan 8984169699Skan case BUILT_IN_STRCPY: 8985169699Skan return fold_builtin_strcpy (fndecl, arglist, NULL_TREE); 8986169699Skan 8987169699Skan case BUILT_IN_STRNCPY: 8988169699Skan return fold_builtin_strncpy (fndecl, arglist, NULL_TREE); 8989169699Skan 8990169699Skan case BUILT_IN_STRCMP: 8991169699Skan return fold_builtin_strcmp (arglist); 8992169699Skan 8993169699Skan case BUILT_IN_STRNCMP: 8994169699Skan return fold_builtin_strncmp (arglist); 8995169699Skan 8996169699Skan case BUILT_IN_STRPBRK: 8997169699Skan return fold_builtin_strpbrk (arglist, type); 8998169699Skan 8999169699Skan case BUILT_IN_BCMP: 9000169699Skan case BUILT_IN_MEMCMP: 9001169699Skan return fold_builtin_memcmp (arglist); 9002169699Skan 9003169699Skan case BUILT_IN_SPRINTF: 9004169699Skan return fold_builtin_sprintf (arglist, ignore); 9005169699Skan 9006169699Skan case BUILT_IN_CONSTANT_P: 9007169699Skan { 9008169699Skan tree val; 9009169699Skan 9010169699Skan val = fold_builtin_constant_p (arglist); 9011169699Skan /* Gimplification will pull the CALL_EXPR for the builtin out of 9012169699Skan an if condition. When not optimizing, we'll not CSE it back. 9013169699Skan To avoid link error types of regressions, return false now. */ 9014169699Skan if (!val && !optimize) 9015169699Skan val = integer_zero_node; 9016169699Skan 9017169699Skan return val; 9018169699Skan } 9019169699Skan 9020169699Skan case BUILT_IN_EXPECT: 9021169699Skan return fold_builtin_expect (arglist); 9022169699Skan 9023169699Skan case BUILT_IN_CLASSIFY_TYPE: 9024169699Skan return fold_builtin_classify_type (arglist); 9025169699Skan 9026169699Skan case BUILT_IN_STRLEN: 9027169699Skan return fold_builtin_strlen (arglist); 9028169699Skan 9029169699Skan CASE_FLT_FN (BUILT_IN_FABS): 9030169699Skan return fold_builtin_fabs (arglist, type); 9031169699Skan 9032169699Skan case BUILT_IN_ABS: 9033169699Skan case BUILT_IN_LABS: 9034169699Skan case BUILT_IN_LLABS: 9035169699Skan case BUILT_IN_IMAXABS: 9036169699Skan return fold_builtin_abs (arglist, type); 9037169699Skan 9038169699Skan CASE_FLT_FN (BUILT_IN_CONJ): 9039169699Skan if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) 9040169699Skan return fold_build1 (CONJ_EXPR, type, TREE_VALUE (arglist)); 9041132727Skan break; 9042132727Skan 9043169699Skan CASE_FLT_FN (BUILT_IN_CREAL): 9044169699Skan if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) 9045169699Skan return non_lvalue (fold_build1 (REALPART_EXPR, type, 9046169699Skan TREE_VALUE (arglist))); 9047169699Skan break; 9048169699Skan 9049169699Skan CASE_FLT_FN (BUILT_IN_CIMAG): 9050169699Skan if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) 9051169699Skan return non_lvalue (fold_build1 (IMAGPART_EXPR, type, 9052169699Skan TREE_VALUE (arglist))); 9053169699Skan break; 9054169699Skan 9055169699Skan CASE_FLT_FN (BUILT_IN_CABS): 9056169699Skan return fold_builtin_cabs (arglist, type, fndecl); 9057169699Skan 9058169699Skan CASE_FLT_FN (BUILT_IN_SQRT): 9059169699Skan return fold_builtin_sqrt (arglist, type); 9060169699Skan 9061169699Skan CASE_FLT_FN (BUILT_IN_CBRT): 9062169699Skan return fold_builtin_cbrt (arglist, type); 9063169699Skan 9064169699Skan CASE_FLT_FN (BUILT_IN_SIN): 9065169699Skan return fold_builtin_sin (arglist); 9066169699Skan 9067169699Skan CASE_FLT_FN (BUILT_IN_COS): 9068169699Skan return fold_builtin_cos (arglist, type, fndecl); 9069169699Skan 9070169699Skan CASE_FLT_FN (BUILT_IN_EXP): 9071169699Skan return fold_builtin_exponent (fndecl, arglist, &dconste); 9072169699Skan 9073169699Skan CASE_FLT_FN (BUILT_IN_EXP2): 9074169699Skan return fold_builtin_exponent (fndecl, arglist, &dconst2); 9075169699Skan 9076169699Skan CASE_FLT_FN (BUILT_IN_EXP10): 9077169699Skan CASE_FLT_FN (BUILT_IN_POW10): 9078169699Skan return fold_builtin_exponent (fndecl, arglist, &dconst10); 9079169699Skan 9080169699Skan CASE_FLT_FN (BUILT_IN_LOG): 9081169699Skan return fold_builtin_logarithm (fndecl, arglist, &dconste); 9082169699Skan 9083169699Skan CASE_FLT_FN (BUILT_IN_LOG2): 9084169699Skan return fold_builtin_logarithm (fndecl, arglist, &dconst2); 9085169699Skan 9086169699Skan CASE_FLT_FN (BUILT_IN_LOG10): 9087169699Skan return fold_builtin_logarithm (fndecl, arglist, &dconst10); 9088169699Skan 9089169699Skan CASE_FLT_FN (BUILT_IN_TAN): 9090169699Skan return fold_builtin_tan (arglist); 9091169699Skan 9092169699Skan CASE_FLT_FN (BUILT_IN_ATAN): 9093169699Skan return fold_builtin_atan (arglist, type); 9094169699Skan 9095169699Skan CASE_FLT_FN (BUILT_IN_POW): 9096169699Skan return fold_builtin_pow (fndecl, arglist, type); 9097169699Skan 9098169699Skan CASE_FLT_FN (BUILT_IN_POWI): 9099169699Skan return fold_builtin_powi (fndecl, arglist, type); 9100169699Skan 9101169699Skan CASE_FLT_FN (BUILT_IN_INF): 9102169699Skan case BUILT_IN_INFD32: 9103169699Skan case BUILT_IN_INFD64: 9104169699Skan case BUILT_IN_INFD128: 9105132727Skan return fold_builtin_inf (type, true); 9106117404Skan 9107169699Skan CASE_FLT_FN (BUILT_IN_HUGE_VAL): 9108132727Skan return fold_builtin_inf (type, false); 9109117404Skan 9110169699Skan CASE_FLT_FN (BUILT_IN_NAN): 9111169699Skan case BUILT_IN_NAND32: 9112169699Skan case BUILT_IN_NAND64: 9113169699Skan case BUILT_IN_NAND128: 9114132727Skan return fold_builtin_nan (arglist, type, true); 9115117404Skan 9116169699Skan CASE_FLT_FN (BUILT_IN_NANS): 9117132727Skan return fold_builtin_nan (arglist, type, false); 9118117404Skan 9119169699Skan CASE_FLT_FN (BUILT_IN_FLOOR): 9120169699Skan return fold_builtin_floor (fndecl, arglist); 9121132727Skan 9122169699Skan CASE_FLT_FN (BUILT_IN_CEIL): 9123169699Skan return fold_builtin_ceil (fndecl, arglist); 9124132727Skan 9125169699Skan CASE_FLT_FN (BUILT_IN_TRUNC): 9126169699Skan return fold_builtin_trunc (fndecl, arglist); 9127132727Skan 9128169699Skan CASE_FLT_FN (BUILT_IN_ROUND): 9129169699Skan return fold_builtin_round (fndecl, arglist); 9130132727Skan 9131169699Skan CASE_FLT_FN (BUILT_IN_NEARBYINT): 9132169699Skan CASE_FLT_FN (BUILT_IN_RINT): 9133169699Skan return fold_trunc_transparent_mathfn (fndecl, arglist); 9134132727Skan 9135169699Skan CASE_FLT_FN (BUILT_IN_LCEIL): 9136169699Skan CASE_FLT_FN (BUILT_IN_LLCEIL): 9137169699Skan CASE_FLT_FN (BUILT_IN_LFLOOR): 9138169699Skan CASE_FLT_FN (BUILT_IN_LLFLOOR): 9139169699Skan CASE_FLT_FN (BUILT_IN_LROUND): 9140169699Skan CASE_FLT_FN (BUILT_IN_LLROUND): 9141169699Skan return fold_builtin_int_roundingfn (fndecl, arglist); 9142169699Skan 9143169699Skan CASE_FLT_FN (BUILT_IN_LRINT): 9144169699Skan CASE_FLT_FN (BUILT_IN_LLRINT): 9145169699Skan return fold_fixed_mathfn (fndecl, arglist); 9146169699Skan 9147259563Spfg case BUILT_IN_BSWAP32: 9148259563Spfg case BUILT_IN_BSWAP64: 9149259563Spfg return fold_builtin_bswap (fndecl, arglist); 9150259563Spfg 9151169699Skan CASE_INT_FN (BUILT_IN_FFS): 9152169699Skan CASE_INT_FN (BUILT_IN_CLZ): 9153169699Skan CASE_INT_FN (BUILT_IN_CTZ): 9154169699Skan CASE_INT_FN (BUILT_IN_POPCOUNT): 9155169699Skan CASE_INT_FN (BUILT_IN_PARITY): 9156169699Skan return fold_builtin_bitop (fndecl, arglist); 9157169699Skan 9158169699Skan case BUILT_IN_MEMSET: 9159169699Skan return fold_builtin_memset (arglist, type, ignore); 9160169699Skan 9161132727Skan case BUILT_IN_MEMCPY: 9162169699Skan return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/0); 9163132727Skan 9164132727Skan case BUILT_IN_MEMPCPY: 9165169699Skan return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/1); 9166132727Skan 9167132727Skan case BUILT_IN_MEMMOVE: 9168169699Skan return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/3); 9169132727Skan 9170169699Skan case BUILT_IN_BZERO: 9171169699Skan return fold_builtin_bzero (arglist, ignore); 9172132727Skan 9173169699Skan case BUILT_IN_BCOPY: 9174169699Skan return fold_builtin_bcopy (arglist, ignore); 9175132727Skan 9176169699Skan CASE_FLT_FN (BUILT_IN_SIGNBIT): 9177169699Skan return fold_builtin_signbit (fndecl, arglist); 9178132727Skan 9179169699Skan case BUILT_IN_ISASCII: 9180169699Skan return fold_builtin_isascii (arglist); 9181132727Skan 9182169699Skan case BUILT_IN_TOASCII: 9183169699Skan return fold_builtin_toascii (arglist); 9184132727Skan 9185169699Skan case BUILT_IN_ISDIGIT: 9186169699Skan return fold_builtin_isdigit (arglist); 9187169699Skan 9188169699Skan CASE_FLT_FN (BUILT_IN_COPYSIGN): 9189169699Skan return fold_builtin_copysign (fndecl, arglist, type); 9190169699Skan 9191169699Skan CASE_FLT_FN (BUILT_IN_FINITE): 9192169699Skan case BUILT_IN_FINITED32: 9193169699Skan case BUILT_IN_FINITED64: 9194169699Skan case BUILT_IN_FINITED128: 9195169699Skan return fold_builtin_classify (fndecl, arglist, BUILT_IN_FINITE); 9196169699Skan 9197169699Skan CASE_FLT_FN (BUILT_IN_ISINF): 9198169699Skan case BUILT_IN_ISINFD32: 9199169699Skan case BUILT_IN_ISINFD64: 9200169699Skan case BUILT_IN_ISINFD128: 9201169699Skan return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISINF); 9202169699Skan 9203169699Skan CASE_FLT_FN (BUILT_IN_ISNAN): 9204169699Skan case BUILT_IN_ISNAND32: 9205169699Skan case BUILT_IN_ISNAND64: 9206169699Skan case BUILT_IN_ISNAND128: 9207169699Skan return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISNAN); 9208169699Skan 9209169699Skan case BUILT_IN_ISGREATER: 9210169699Skan return fold_builtin_unordered_cmp (fndecl, arglist, UNLE_EXPR, LE_EXPR); 9211169699Skan case BUILT_IN_ISGREATEREQUAL: 9212169699Skan return fold_builtin_unordered_cmp (fndecl, arglist, UNLT_EXPR, LT_EXPR); 9213169699Skan case BUILT_IN_ISLESS: 9214169699Skan return fold_builtin_unordered_cmp (fndecl, arglist, UNGE_EXPR, GE_EXPR); 9215169699Skan case BUILT_IN_ISLESSEQUAL: 9216169699Skan return fold_builtin_unordered_cmp (fndecl, arglist, UNGT_EXPR, GT_EXPR); 9217169699Skan case BUILT_IN_ISLESSGREATER: 9218169699Skan return fold_builtin_unordered_cmp (fndecl, arglist, UNEQ_EXPR, EQ_EXPR); 9219169699Skan case BUILT_IN_ISUNORDERED: 9220169699Skan return fold_builtin_unordered_cmp (fndecl, arglist, UNORDERED_EXPR, 9221169699Skan NOP_EXPR); 9222169699Skan 9223169699Skan /* We do the folding for va_start in the expander. */ 9224169699Skan case BUILT_IN_VA_START: 9225169699Skan break; 9226169699Skan 9227169699Skan case BUILT_IN_OBJECT_SIZE: 9228169699Skan return fold_builtin_object_size (arglist); 9229169699Skan case BUILT_IN_MEMCPY_CHK: 9230169699Skan case BUILT_IN_MEMPCPY_CHK: 9231169699Skan case BUILT_IN_MEMMOVE_CHK: 9232169699Skan case BUILT_IN_MEMSET_CHK: 9233169699Skan return fold_builtin_memory_chk (fndecl, arglist, NULL_TREE, ignore, 9234169699Skan DECL_FUNCTION_CODE (fndecl)); 9235169699Skan case BUILT_IN_STRCPY_CHK: 9236169699Skan case BUILT_IN_STPCPY_CHK: 9237169699Skan return fold_builtin_stxcpy_chk (fndecl, arglist, NULL_TREE, ignore, 9238169699Skan DECL_FUNCTION_CODE (fndecl)); 9239169699Skan case BUILT_IN_STRNCPY_CHK: 9240169699Skan return fold_builtin_strncpy_chk (arglist, NULL_TREE); 9241169699Skan case BUILT_IN_STRCAT_CHK: 9242169699Skan return fold_builtin_strcat_chk (fndecl, arglist); 9243169699Skan case BUILT_IN_STRNCAT_CHK: 9244169699Skan return fold_builtin_strncat_chk (fndecl, arglist); 9245169699Skan case BUILT_IN_SPRINTF_CHK: 9246169699Skan case BUILT_IN_VSPRINTF_CHK: 9247169699Skan return fold_builtin_sprintf_chk (arglist, DECL_FUNCTION_CODE (fndecl)); 9248169699Skan case BUILT_IN_SNPRINTF_CHK: 9249169699Skan case BUILT_IN_VSNPRINTF_CHK: 9250169699Skan return fold_builtin_snprintf_chk (arglist, NULL_TREE, 9251169699Skan DECL_FUNCTION_CODE (fndecl)); 9252169699Skan 9253169699Skan case BUILT_IN_PRINTF: 9254169699Skan case BUILT_IN_PRINTF_UNLOCKED: 9255169699Skan case BUILT_IN_VPRINTF: 9256169699Skan case BUILT_IN_PRINTF_CHK: 9257169699Skan case BUILT_IN_VPRINTF_CHK: 9258169699Skan return fold_builtin_printf (fndecl, arglist, ignore, 9259169699Skan DECL_FUNCTION_CODE (fndecl)); 9260169699Skan 9261169699Skan case BUILT_IN_FPRINTF: 9262169699Skan case BUILT_IN_FPRINTF_UNLOCKED: 9263169699Skan case BUILT_IN_VFPRINTF: 9264169699Skan case BUILT_IN_FPRINTF_CHK: 9265169699Skan case BUILT_IN_VFPRINTF_CHK: 9266169699Skan return fold_builtin_fprintf (fndecl, arglist, ignore, 9267169699Skan DECL_FUNCTION_CODE (fndecl)); 9268169699Skan 926990075Sobrien default: 927090075Sobrien break; 927190075Sobrien } 927290075Sobrien 927390075Sobrien return 0; 927490075Sobrien} 927590075Sobrien 9276169699Skan/* A wrapper function for builtin folding that prevents warnings for 9277169699Skan "statement without effect" and the like, caused by removing the 9278169699Skan call node earlier than the warning is generated. */ 9279169699Skan 9280169699Skantree 9281169699Skanfold_builtin (tree fndecl, tree arglist, bool ignore) 9282169699Skan{ 9283169699Skan tree exp = fold_builtin_1 (fndecl, arglist, ignore); 9284169699Skan if (exp) 9285169699Skan { 9286169699Skan exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp); 9287169699Skan TREE_NO_WARNING (exp) = 1; 9288169699Skan } 9289169699Skan 9290169699Skan return exp; 9291169699Skan} 9292169699Skan 9293132727Skan/* Conveniently construct a function call expression. */ 9294132727Skan 9295132727Skantree 9296132727Skanbuild_function_call_expr (tree fn, tree arglist) 929790075Sobrien{ 929890075Sobrien tree call_expr; 929990075Sobrien 930090075Sobrien call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); 9301169699Skan return fold_build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), 9302169699Skan call_expr, arglist, NULL_TREE); 930390075Sobrien} 930490075Sobrien 930590075Sobrien/* This function validates the types of a function call argument list 930690075Sobrien represented as a tree chain of parameters against a specified list 930790075Sobrien of tree_codes. If the last specifier is a 0, that represents an 930890075Sobrien ellipses, otherwise the last specifier must be a VOID_TYPE. */ 930990075Sobrien 931090075Sobrienstatic int 9311132727Skanvalidate_arglist (tree arglist, ...) 931290075Sobrien{ 931390075Sobrien enum tree_code code; 931490075Sobrien int res = 0; 9315132727Skan va_list ap; 931690075Sobrien 9317132727Skan va_start (ap, arglist); 931890075Sobrien 9319117404Skan do 932090075Sobrien { 9321117404Skan code = va_arg (ap, enum tree_code); 9322117404Skan switch (code) 9323117404Skan { 9324117404Skan case 0: 9325117404Skan /* This signifies an ellipses, any further arguments are all ok. */ 9326117404Skan res = 1; 9327117404Skan goto end; 9328117404Skan case VOID_TYPE: 9329117404Skan /* This signifies an endlink, if no arguments remain, return 9330117404Skan true, otherwise return false. */ 9331117404Skan res = arglist == 0; 9332117404Skan goto end; 9333117404Skan default: 9334117404Skan /* If no parameters remain or the parameter's code does not 9335117404Skan match the specified code, return false. Otherwise continue 9336117404Skan checking any remaining arguments. */ 9337169699Skan if (arglist == 0) 9338117404Skan goto end; 9339169699Skan if (code == POINTER_TYPE) 9340169699Skan { 9341169699Skan if (! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))) 9342169699Skan goto end; 9343169699Skan } 9344169699Skan else if (code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist)))) 9345169699Skan goto end; 9346117404Skan break; 9347117404Skan } 9348117404Skan arglist = TREE_CHAIN (arglist); 934990075Sobrien } 9350117404Skan while (1); 935190075Sobrien 935290075Sobrien /* We need gotos here since we can only have one VA_CLOSE in a 935390075Sobrien function. */ 935490075Sobrien end: ; 9355132727Skan va_end (ap); 935690075Sobrien 935790075Sobrien return res; 935890075Sobrien} 935990075Sobrien 9360132727Skan/* Default target-specific builtin expander that does nothing. */ 936190075Sobrien 9362132727Skanrtx 9363132727Skandefault_expand_builtin (tree exp ATTRIBUTE_UNUSED, 9364132727Skan rtx target ATTRIBUTE_UNUSED, 9365132727Skan rtx subtarget ATTRIBUTE_UNUSED, 9366132727Skan enum machine_mode mode ATTRIBUTE_UNUSED, 9367132727Skan int ignore ATTRIBUTE_UNUSED) 9368132727Skan{ 9369132727Skan return NULL_RTX; 9370132727Skan} 9371132727Skan 9372169699Skan/* Returns true is EXP represents data that would potentially reside 9373169699Skan in a readonly section. */ 9374132727Skan 9375169699Skanstatic bool 9376169699Skanreadonly_data_expr (tree exp) 937790075Sobrien{ 9378169699Skan STRIP_NOPS (exp); 9379132727Skan 9380169699Skan if (TREE_CODE (exp) != ADDR_EXPR) 9381169699Skan return false; 9382169699Skan 9383169699Skan exp = get_base_address (TREE_OPERAND (exp, 0)); 9384169699Skan if (!exp) 9385169699Skan return false; 9386169699Skan 9387169699Skan /* Make sure we call decl_readonly_section only for trees it 9388169699Skan can handle (since it returns true for everything it doesn't 9389169699Skan understand). */ 9390169699Skan if (TREE_CODE (exp) == STRING_CST 9391169699Skan || TREE_CODE (exp) == CONSTRUCTOR 9392169699Skan || (TREE_CODE (exp) == VAR_DECL && TREE_STATIC (exp))) 9393169699Skan return decl_readonly_section (exp, 0); 9394169699Skan else 9395169699Skan return false; 9396169699Skan} 9397169699Skan 9398169699Skan/* Simplify a call to the strstr builtin. 9399169699Skan 9400169699Skan Return 0 if no simplification was possible, otherwise return the 9401169699Skan simplified form of the call as a tree. 9402169699Skan 9403169699Skan The simplified form may be a constant or other expression which 9404169699Skan computes the same value, but in a more efficient manner (including 9405169699Skan calls to other builtin functions). 9406169699Skan 9407169699Skan The call may contain arguments which need to be evaluated, but 9408169699Skan which are not useful to determine the result of the call. In 9409169699Skan this case we return a chain of COMPOUND_EXPRs. The LHS of each 9410169699Skan COMPOUND_EXPR will be an argument which must be evaluated. 9411169699Skan COMPOUND_EXPRs are chained through their RHS. The RHS of the last 9412169699Skan COMPOUND_EXPR in the chain will contain the tree for the simplified 9413169699Skan form of the builtin function call. */ 9414169699Skan 9415169699Skanstatic tree 9416169699Skanfold_builtin_strstr (tree arglist, tree type) 9417169699Skan{ 9418169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 9419169699Skan return 0; 9420169699Skan else 9421169699Skan { 9422169699Skan tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); 9423169699Skan tree fn; 9424169699Skan const char *p1, *p2; 9425169699Skan 9426169699Skan p2 = c_getstr (s2); 9427169699Skan if (p2 == NULL) 9428169699Skan return 0; 9429169699Skan 9430169699Skan p1 = c_getstr (s1); 9431169699Skan if (p1 != NULL) 9432169699Skan { 9433169699Skan const char *r = strstr (p1, p2); 9434169699Skan tree tem; 9435169699Skan 9436169699Skan if (r == NULL) 9437169699Skan return build_int_cst (TREE_TYPE (s1), 0); 9438169699Skan 9439169699Skan /* Return an offset into the constant string argument. */ 9440169699Skan tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1), 9441169699Skan s1, build_int_cst (TREE_TYPE (s1), r - p1)); 9442169699Skan return fold_convert (type, tem); 9443169699Skan } 9444169699Skan 9445169699Skan /* The argument is const char *, and the result is char *, so we need 9446169699Skan a type conversion here to avoid a warning. */ 9447169699Skan if (p2[0] == '\0') 9448169699Skan return fold_convert (type, s1); 9449169699Skan 9450169699Skan if (p2[1] != '\0') 9451169699Skan return 0; 9452169699Skan 9453169699Skan fn = implicit_built_in_decls[BUILT_IN_STRCHR]; 9454169699Skan if (!fn) 9455169699Skan return 0; 9456169699Skan 9457169699Skan /* New argument list transforming strstr(s1, s2) to 9458169699Skan strchr(s1, s2[0]). */ 9459169699Skan arglist = build_tree_list (NULL_TREE, 9460169699Skan build_int_cst (NULL_TREE, p2[0])); 9461169699Skan arglist = tree_cons (NULL_TREE, s1, arglist); 9462169699Skan return build_function_call_expr (fn, arglist); 9463169699Skan } 9464169699Skan} 9465169699Skan 9466169699Skan/* Simplify a call to the strchr builtin. 9467169699Skan 9468169699Skan Return 0 if no simplification was possible, otherwise return the 9469169699Skan simplified form of the call as a tree. 9470169699Skan 9471169699Skan The simplified form may be a constant or other expression which 9472169699Skan computes the same value, but in a more efficient manner (including 9473169699Skan calls to other builtin functions). 9474169699Skan 9475169699Skan The call may contain arguments which need to be evaluated, but 9476169699Skan which are not useful to determine the result of the call. In 9477169699Skan this case we return a chain of COMPOUND_EXPRs. The LHS of each 9478169699Skan COMPOUND_EXPR will be an argument which must be evaluated. 9479169699Skan COMPOUND_EXPRs are chained through their RHS. The RHS of the last 9480169699Skan COMPOUND_EXPR in the chain will contain the tree for the simplified 9481169699Skan form of the builtin function call. */ 9482169699Skan 9483169699Skanstatic tree 9484169699Skanfold_builtin_strchr (tree arglist, tree type) 9485169699Skan{ 9486169699Skan if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 9487169699Skan return 0; 9488169699Skan else 9489169699Skan { 9490169699Skan tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); 9491169699Skan const char *p1; 9492169699Skan 9493169699Skan if (TREE_CODE (s2) != INTEGER_CST) 9494169699Skan return 0; 9495169699Skan 9496169699Skan p1 = c_getstr (s1); 9497169699Skan if (p1 != NULL) 9498169699Skan { 9499169699Skan char c; 9500169699Skan const char *r; 9501169699Skan tree tem; 9502169699Skan 9503169699Skan if (target_char_cast (s2, &c)) 9504169699Skan return 0; 9505169699Skan 9506169699Skan r = strchr (p1, c); 9507169699Skan 9508169699Skan if (r == NULL) 9509169699Skan return build_int_cst (TREE_TYPE (s1), 0); 9510169699Skan 9511169699Skan /* Return an offset into the constant string argument. */ 9512169699Skan tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1), 9513169699Skan s1, build_int_cst (TREE_TYPE (s1), r - p1)); 9514169699Skan return fold_convert (type, tem); 9515169699Skan } 9516169699Skan return 0; 9517169699Skan } 9518169699Skan} 9519169699Skan 9520169699Skan/* Simplify a call to the strrchr builtin. 9521169699Skan 9522169699Skan Return 0 if no simplification was possible, otherwise return the 9523169699Skan simplified form of the call as a tree. 9524169699Skan 9525169699Skan The simplified form may be a constant or other expression which 9526169699Skan computes the same value, but in a more efficient manner (including 9527169699Skan calls to other builtin functions). 9528169699Skan 9529169699Skan The call may contain arguments which need to be evaluated, but 9530169699Skan which are not useful to determine the result of the call. In 9531169699Skan this case we return a chain of COMPOUND_EXPRs. The LHS of each 9532169699Skan COMPOUND_EXPR will be an argument which must be evaluated. 9533169699Skan COMPOUND_EXPRs are chained through their RHS. The RHS of the last 9534169699Skan COMPOUND_EXPR in the chain will contain the tree for the simplified 9535169699Skan form of the builtin function call. */ 9536169699Skan 9537169699Skanstatic tree 9538169699Skanfold_builtin_strrchr (tree arglist, tree type) 9539169699Skan{ 9540169699Skan if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 9541169699Skan return 0; 9542169699Skan else 9543169699Skan { 9544169699Skan tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); 9545169699Skan tree fn; 9546169699Skan const char *p1; 9547169699Skan 9548169699Skan if (TREE_CODE (s2) != INTEGER_CST) 9549169699Skan return 0; 9550169699Skan 9551169699Skan p1 = c_getstr (s1); 9552169699Skan if (p1 != NULL) 9553169699Skan { 9554169699Skan char c; 9555169699Skan const char *r; 9556169699Skan tree tem; 9557169699Skan 9558169699Skan if (target_char_cast (s2, &c)) 9559169699Skan return 0; 9560169699Skan 9561169699Skan r = strrchr (p1, c); 9562169699Skan 9563169699Skan if (r == NULL) 9564169699Skan return build_int_cst (TREE_TYPE (s1), 0); 9565169699Skan 9566169699Skan /* Return an offset into the constant string argument. */ 9567169699Skan tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1), 9568169699Skan s1, build_int_cst (TREE_TYPE (s1), r - p1)); 9569169699Skan return fold_convert (type, tem); 9570169699Skan } 9571169699Skan 9572169699Skan if (! integer_zerop (s2)) 9573169699Skan return 0; 9574169699Skan 9575169699Skan fn = implicit_built_in_decls[BUILT_IN_STRCHR]; 9576169699Skan if (!fn) 9577169699Skan return 0; 9578169699Skan 9579169699Skan /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */ 9580169699Skan return build_function_call_expr (fn, arglist); 9581169699Skan } 9582169699Skan} 9583169699Skan 9584169699Skan/* Simplify a call to the strpbrk builtin. 9585169699Skan 9586169699Skan Return 0 if no simplification was possible, otherwise return the 9587169699Skan simplified form of the call as a tree. 9588169699Skan 9589169699Skan The simplified form may be a constant or other expression which 9590169699Skan computes the same value, but in a more efficient manner (including 9591169699Skan calls to other builtin functions). 9592169699Skan 9593169699Skan The call may contain arguments which need to be evaluated, but 9594169699Skan which are not useful to determine the result of the call. In 9595169699Skan this case we return a chain of COMPOUND_EXPRs. The LHS of each 9596169699Skan COMPOUND_EXPR will be an argument which must be evaluated. 9597169699Skan COMPOUND_EXPRs are chained through their RHS. The RHS of the last 9598169699Skan COMPOUND_EXPR in the chain will contain the tree for the simplified 9599169699Skan form of the builtin function call. */ 9600169699Skan 9601169699Skanstatic tree 9602169699Skanfold_builtin_strpbrk (tree arglist, tree type) 9603169699Skan{ 9604169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 9605169699Skan return 0; 9606169699Skan else 9607169699Skan { 9608169699Skan tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); 9609169699Skan tree fn; 9610169699Skan const char *p1, *p2; 9611169699Skan 9612169699Skan p2 = c_getstr (s2); 9613169699Skan if (p2 == NULL) 9614169699Skan return 0; 9615169699Skan 9616169699Skan p1 = c_getstr (s1); 9617169699Skan if (p1 != NULL) 9618169699Skan { 9619169699Skan const char *r = strpbrk (p1, p2); 9620169699Skan tree tem; 9621169699Skan 9622169699Skan if (r == NULL) 9623169699Skan return build_int_cst (TREE_TYPE (s1), 0); 9624169699Skan 9625169699Skan /* Return an offset into the constant string argument. */ 9626169699Skan tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1), 9627169699Skan s1, build_int_cst (TREE_TYPE (s1), r - p1)); 9628169699Skan return fold_convert (type, tem); 9629169699Skan } 9630169699Skan 9631169699Skan if (p2[0] == '\0') 9632169699Skan /* strpbrk(x, "") == NULL. 9633169699Skan Evaluate and ignore s1 in case it had side-effects. */ 9634169699Skan return omit_one_operand (TREE_TYPE (s1), integer_zero_node, s1); 9635169699Skan 9636169699Skan if (p2[1] != '\0') 9637169699Skan return 0; /* Really call strpbrk. */ 9638169699Skan 9639169699Skan fn = implicit_built_in_decls[BUILT_IN_STRCHR]; 9640169699Skan if (!fn) 9641169699Skan return 0; 9642169699Skan 9643169699Skan /* New argument list transforming strpbrk(s1, s2) to 9644169699Skan strchr(s1, s2[0]). */ 9645169699Skan arglist = build_tree_list (NULL_TREE, 9646169699Skan build_int_cst (NULL_TREE, p2[0])); 9647169699Skan arglist = tree_cons (NULL_TREE, s1, arglist); 9648169699Skan return build_function_call_expr (fn, arglist); 9649169699Skan } 9650169699Skan} 9651169699Skan 9652169699Skan/* Simplify a call to the strcat builtin. 9653169699Skan 9654169699Skan Return 0 if no simplification was possible, otherwise return the 9655169699Skan simplified form of the call as a tree. 9656169699Skan 9657169699Skan The simplified form may be a constant or other expression which 9658169699Skan computes the same value, but in a more efficient manner (including 9659169699Skan calls to other builtin functions). 9660169699Skan 9661169699Skan The call may contain arguments which need to be evaluated, but 9662169699Skan which are not useful to determine the result of the call. In 9663169699Skan this case we return a chain of COMPOUND_EXPRs. The LHS of each 9664169699Skan COMPOUND_EXPR will be an argument which must be evaluated. 9665169699Skan COMPOUND_EXPRs are chained through their RHS. The RHS of the last 9666169699Skan COMPOUND_EXPR in the chain will contain the tree for the simplified 9667169699Skan form of the builtin function call. */ 9668169699Skan 9669169699Skanstatic tree 9670169699Skanfold_builtin_strcat (tree arglist) 9671169699Skan{ 9672169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 9673169699Skan return 0; 9674169699Skan else 9675169699Skan { 9676169699Skan tree dst = TREE_VALUE (arglist), 9677169699Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 9678169699Skan const char *p = c_getstr (src); 9679169699Skan 9680169699Skan /* If the string length is zero, return the dst parameter. */ 9681169699Skan if (p && *p == '\0') 9682169699Skan return dst; 9683169699Skan 9684169699Skan return 0; 9685169699Skan } 9686169699Skan} 9687169699Skan 9688169699Skan/* Simplify a call to the strncat builtin. 9689169699Skan 9690169699Skan Return 0 if no simplification was possible, otherwise return the 9691169699Skan simplified form of the call as a tree. 9692169699Skan 9693169699Skan The simplified form may be a constant or other expression which 9694169699Skan computes the same value, but in a more efficient manner (including 9695169699Skan calls to other builtin functions). 9696169699Skan 9697169699Skan The call may contain arguments which need to be evaluated, but 9698169699Skan which are not useful to determine the result of the call. In 9699169699Skan this case we return a chain of COMPOUND_EXPRs. The LHS of each 9700169699Skan COMPOUND_EXPR will be an argument which must be evaluated. 9701169699Skan COMPOUND_EXPRs are chained through their RHS. The RHS of the last 9702169699Skan COMPOUND_EXPR in the chain will contain the tree for the simplified 9703169699Skan form of the builtin function call. */ 9704169699Skan 9705169699Skanstatic tree 9706169699Skanfold_builtin_strncat (tree arglist) 9707169699Skan{ 9708169699Skan if (!validate_arglist (arglist, 9709169699Skan POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 9710169699Skan return 0; 9711169699Skan else 9712169699Skan { 9713169699Skan tree dst = TREE_VALUE (arglist); 9714169699Skan tree src = TREE_VALUE (TREE_CHAIN (arglist)); 9715169699Skan tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 9716169699Skan const char *p = c_getstr (src); 9717169699Skan 9718169699Skan /* If the requested length is zero, or the src parameter string 9719169699Skan length is zero, return the dst parameter. */ 9720169699Skan if (integer_zerop (len) || (p && *p == '\0')) 9721169699Skan return omit_two_operands (TREE_TYPE (dst), dst, src, len); 9722169699Skan 9723169699Skan /* If the requested len is greater than or equal to the string 9724169699Skan length, call strcat. */ 9725169699Skan if (TREE_CODE (len) == INTEGER_CST && p 9726169699Skan && compare_tree_int (len, strlen (p)) >= 0) 9727169699Skan { 9728169699Skan tree newarglist 9729169699Skan = tree_cons (NULL_TREE, dst, build_tree_list (NULL_TREE, src)); 9730169699Skan tree fn = implicit_built_in_decls[BUILT_IN_STRCAT]; 9731169699Skan 9732169699Skan /* If the replacement _DECL isn't initialized, don't do the 9733169699Skan transformation. */ 9734169699Skan if (!fn) 9735169699Skan return 0; 9736169699Skan 9737169699Skan return build_function_call_expr (fn, newarglist); 9738169699Skan } 9739169699Skan return 0; 9740169699Skan } 9741169699Skan} 9742169699Skan 9743169699Skan/* Simplify a call to the strspn builtin. 9744169699Skan 9745169699Skan Return 0 if no simplification was possible, otherwise return the 9746169699Skan simplified form of the call as a tree. 9747169699Skan 9748169699Skan The simplified form may be a constant or other expression which 9749169699Skan computes the same value, but in a more efficient manner (including 9750169699Skan calls to other builtin functions). 9751169699Skan 9752169699Skan The call may contain arguments which need to be evaluated, but 9753169699Skan which are not useful to determine the result of the call. In 9754169699Skan this case we return a chain of COMPOUND_EXPRs. The LHS of each 9755169699Skan COMPOUND_EXPR will be an argument which must be evaluated. 9756169699Skan COMPOUND_EXPRs are chained through their RHS. The RHS of the last 9757169699Skan COMPOUND_EXPR in the chain will contain the tree for the simplified 9758169699Skan form of the builtin function call. */ 9759169699Skan 9760169699Skanstatic tree 9761169699Skanfold_builtin_strspn (tree arglist) 9762169699Skan{ 9763169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 9764169699Skan return 0; 9765169699Skan else 9766169699Skan { 9767169699Skan tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); 9768169699Skan const char *p1 = c_getstr (s1), *p2 = c_getstr (s2); 9769169699Skan 9770169699Skan /* If both arguments are constants, evaluate at compile-time. */ 9771169699Skan if (p1 && p2) 9772169699Skan { 9773169699Skan const size_t r = strspn (p1, p2); 9774169699Skan return size_int (r); 9775169699Skan } 9776169699Skan 9777169699Skan /* If either argument is "", return 0. */ 9778169699Skan if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0')) 9779169699Skan /* Evaluate and ignore both arguments in case either one has 9780169699Skan side-effects. */ 9781169699Skan return omit_two_operands (integer_type_node, integer_zero_node, 9782169699Skan s1, s2); 9783169699Skan return 0; 9784169699Skan } 9785169699Skan} 9786169699Skan 9787169699Skan/* Simplify a call to the strcspn builtin. 9788169699Skan 9789169699Skan Return 0 if no simplification was possible, otherwise return the 9790169699Skan simplified form of the call as a tree. 9791169699Skan 9792169699Skan The simplified form may be a constant or other expression which 9793169699Skan computes the same value, but in a more efficient manner (including 9794169699Skan calls to other builtin functions). 9795169699Skan 9796169699Skan The call may contain arguments which need to be evaluated, but 9797169699Skan which are not useful to determine the result of the call. In 9798169699Skan this case we return a chain of COMPOUND_EXPRs. The LHS of each 9799169699Skan COMPOUND_EXPR will be an argument which must be evaluated. 9800169699Skan COMPOUND_EXPRs are chained through their RHS. The RHS of the last 9801169699Skan COMPOUND_EXPR in the chain will contain the tree for the simplified 9802169699Skan form of the builtin function call. */ 9803169699Skan 9804169699Skanstatic tree 9805169699Skanfold_builtin_strcspn (tree arglist) 9806169699Skan{ 9807169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 9808169699Skan return 0; 9809169699Skan else 9810169699Skan { 9811169699Skan tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); 9812169699Skan const char *p1 = c_getstr (s1), *p2 = c_getstr (s2); 9813169699Skan 9814169699Skan /* If both arguments are constants, evaluate at compile-time. */ 9815169699Skan if (p1 && p2) 9816169699Skan { 9817169699Skan const size_t r = strcspn (p1, p2); 9818169699Skan return size_int (r); 9819169699Skan } 9820169699Skan 9821169699Skan /* If the first argument is "", return 0. */ 9822169699Skan if (p1 && *p1 == '\0') 9823169699Skan { 9824169699Skan /* Evaluate and ignore argument s2 in case it has 9825169699Skan side-effects. */ 9826169699Skan return omit_one_operand (integer_type_node, 9827169699Skan integer_zero_node, s2); 9828169699Skan } 9829169699Skan 9830169699Skan /* If the second argument is "", return __builtin_strlen(s1). */ 9831169699Skan if (p2 && *p2 == '\0') 9832169699Skan { 9833169699Skan tree newarglist = build_tree_list (NULL_TREE, s1), 9834169699Skan fn = implicit_built_in_decls[BUILT_IN_STRLEN]; 9835169699Skan 9836169699Skan /* If the replacement _DECL isn't initialized, don't do the 9837169699Skan transformation. */ 9838169699Skan if (!fn) 9839169699Skan return 0; 9840169699Skan 9841169699Skan return build_function_call_expr (fn, newarglist); 9842169699Skan } 9843169699Skan return 0; 9844169699Skan } 9845169699Skan} 9846169699Skan 9847169699Skan/* Fold a call to the fputs builtin. IGNORE is true if the value returned 9848169699Skan by the builtin will be ignored. UNLOCKED is true is true if this 9849169699Skan actually a call to fputs_unlocked. If LEN in non-NULL, it represents 9850169699Skan the known length of the string. Return NULL_TREE if no simplification 9851169699Skan was possible. */ 9852169699Skan 9853169699Skantree 9854169699Skanfold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len) 9855169699Skan{ 9856169699Skan tree fn; 9857169699Skan /* If we're using an unlocked function, assume the other unlocked 9858169699Skan functions exist explicitly. */ 9859169699Skan tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED] 9860169699Skan : implicit_built_in_decls[BUILT_IN_FPUTC]; 9861169699Skan tree const fn_fwrite = unlocked ? built_in_decls[BUILT_IN_FWRITE_UNLOCKED] 9862169699Skan : implicit_built_in_decls[BUILT_IN_FWRITE]; 9863169699Skan 9864169699Skan /* If the return value is used, don't do the transformation. */ 9865169699Skan if (!ignore) 9866169699Skan return 0; 9867169699Skan 9868169699Skan /* Verify the arguments in the original call. */ 9869169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) 9870169699Skan return 0; 9871169699Skan 9872169699Skan if (! len) 9873169699Skan len = c_strlen (TREE_VALUE (arglist), 0); 9874169699Skan 9875169699Skan /* Get the length of the string passed to fputs. If the length 9876169699Skan can't be determined, punt. */ 9877169699Skan if (!len 9878169699Skan || TREE_CODE (len) != INTEGER_CST) 9879169699Skan return 0; 9880169699Skan 9881169699Skan switch (compare_tree_int (len, 1)) 9882169699Skan { 9883169699Skan case -1: /* length is 0, delete the call entirely . */ 9884169699Skan return omit_one_operand (integer_type_node, integer_zero_node, 9885169699Skan TREE_VALUE (TREE_CHAIN (arglist))); 9886169699Skan 9887169699Skan case 0: /* length is 1, call fputc. */ 9888132727Skan { 9889169699Skan const char *p = c_getstr (TREE_VALUE (arglist)); 9890132727Skan 9891169699Skan if (p != NULL) 9892169699Skan { 9893169699Skan /* New argument list transforming fputs(string, stream) to 9894169699Skan fputc(string[0], stream). */ 9895169699Skan arglist = build_tree_list (NULL_TREE, 9896169699Skan TREE_VALUE (TREE_CHAIN (arglist))); 9897169699Skan arglist = tree_cons (NULL_TREE, 9898169699Skan build_int_cst (NULL_TREE, p[0]), 9899169699Skan arglist); 9900169699Skan fn = fn_fputc; 9901169699Skan break; 9902169699Skan } 9903132727Skan } 9904169699Skan /* FALLTHROUGH */ 9905169699Skan case 1: /* length is greater than 1, call fwrite. */ 9906169699Skan { 9907169699Skan tree string_arg; 9908169699Skan 9909169699Skan /* If optimizing for size keep fputs. */ 9910169699Skan if (optimize_size) 9911169699Skan return 0; 9912169699Skan string_arg = TREE_VALUE (arglist); 9913169699Skan /* New argument list transforming fputs(string, stream) to 9914169699Skan fwrite(string, 1, len, stream). */ 9915169699Skan arglist = build_tree_list (NULL_TREE, 9916169699Skan TREE_VALUE (TREE_CHAIN (arglist))); 9917169699Skan arglist = tree_cons (NULL_TREE, len, arglist); 9918169699Skan arglist = tree_cons (NULL_TREE, size_one_node, arglist); 9919169699Skan arglist = tree_cons (NULL_TREE, string_arg, arglist); 9920169699Skan fn = fn_fwrite; 9921169699Skan break; 9922169699Skan } 9923169699Skan default: 9924169699Skan gcc_unreachable (); 9925169699Skan } 9926169699Skan 9927169699Skan /* If the replacement _DECL isn't initialized, don't do the 9928169699Skan transformation. */ 9929169699Skan if (!fn) 9930169699Skan return 0; 9931169699Skan 9932169699Skan /* These optimizations are only performed when the result is ignored, 9933169699Skan hence there's no need to cast the result to integer_type_node. */ 9934169699Skan return build_function_call_expr (fn, arglist); 993590075Sobrien} 993690075Sobrien 9937169699Skan/* Fold the new_arg's arguments (ARGLIST). Returns true if there was an error 9938169699Skan produced. False otherwise. This is done so that we don't output the error 9939169699Skan or warning twice or three times. */ 9940169699Skanbool 9941169699Skanfold_builtin_next_arg (tree arglist) 9942169699Skan{ 9943169699Skan tree fntype = TREE_TYPE (current_function_decl); 994490075Sobrien 9945169699Skan if (TYPE_ARG_TYPES (fntype) == 0 9946169699Skan || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) 9947169699Skan == void_type_node)) 9948169699Skan { 9949169699Skan error ("%<va_start%> used in function with fixed args"); 9950169699Skan return true; 9951169699Skan } 9952169699Skan else if (!arglist) 9953169699Skan { 9954169699Skan /* Evidently an out of date version of <stdarg.h>; can't validate 9955169699Skan va_start's second argument, but can still work as intended. */ 9956169699Skan warning (0, "%<__builtin_next_arg%> called without an argument"); 9957169699Skan return true; 9958169699Skan } 9959169699Skan /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0) 9960169699Skan when we checked the arguments and if needed issued a warning. */ 9961169699Skan else if (!TREE_CHAIN (arglist) 9962169699Skan || !integer_zerop (TREE_VALUE (arglist)) 9963169699Skan || !integer_zerop (TREE_VALUE (TREE_CHAIN (arglist))) 9964169699Skan || TREE_CHAIN (TREE_CHAIN (arglist))) 9965169699Skan { 9966169699Skan tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl)); 9967169699Skan tree arg = TREE_VALUE (arglist); 9968169699Skan 9969169699Skan if (TREE_CHAIN (arglist)) 9970169699Skan { 9971169699Skan error ("%<va_start%> used with too many arguments"); 9972169699Skan return true; 9973169699Skan } 9974169699Skan 9975169699Skan /* Strip off all nops for the sake of the comparison. This 9976169699Skan is not quite the same as STRIP_NOPS. It does more. 9977169699Skan We must also strip off INDIRECT_EXPR for C++ reference 9978169699Skan parameters. */ 9979169699Skan while (TREE_CODE (arg) == NOP_EXPR 9980169699Skan || TREE_CODE (arg) == CONVERT_EXPR 9981169699Skan || TREE_CODE (arg) == NON_LVALUE_EXPR 9982169699Skan || TREE_CODE (arg) == INDIRECT_REF) 9983169699Skan arg = TREE_OPERAND (arg, 0); 9984169699Skan if (arg != last_parm) 9985169699Skan { 9986169699Skan /* FIXME: Sometimes with the tree optimizers we can get the 9987169699Skan not the last argument even though the user used the last 9988169699Skan argument. We just warn and set the arg to be the last 9989169699Skan argument so that we will get wrong-code because of 9990169699Skan it. */ 9991169699Skan warning (0, "second parameter of %<va_start%> not last named argument"); 9992169699Skan } 9993169699Skan /* We want to verify the second parameter just once before the tree 9994169699Skan optimizers are run and then avoid keeping it in the tree, 9995169699Skan as otherwise we could warn even for correct code like: 9996169699Skan void foo (int i, ...) 9997169699Skan { va_list ap; i++; va_start (ap, i); va_end (ap); } */ 9998169699Skan TREE_VALUE (arglist) = integer_zero_node; 9999169699Skan TREE_CHAIN (arglist) = build_tree_list (NULL, integer_zero_node); 10000169699Skan } 10001169699Skan return false; 10002169699Skan} 10003169699Skan 10004169699Skan 10005169699Skan/* Simplify a call to the sprintf builtin. 10006169699Skan 10007169699Skan Return 0 if no simplification was possible, otherwise return the 10008169699Skan simplified form of the call as a tree. If IGNORED is true, it means that 10009169699Skan the caller does not use the returned value of the function. */ 10010169699Skan 10011169699Skanstatic tree 10012169699Skanfold_builtin_sprintf (tree arglist, int ignored) 1001390075Sobrien{ 10014169699Skan tree call, retval, dest, fmt; 10015169699Skan const char *fmt_str = NULL; 10016132727Skan 10017169699Skan /* Verify the required arguments in the original call. We deal with two 10018169699Skan types of sprintf() calls: 'sprintf (str, fmt)' and 10019169699Skan 'sprintf (dest, "%s", orig)'. */ 10020169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE) 10021169699Skan && !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE, 10022169699Skan VOID_TYPE)) 10023169699Skan return NULL_TREE; 10024169699Skan 10025169699Skan /* Get the destination string and the format specifier. */ 10026169699Skan dest = TREE_VALUE (arglist); 10027169699Skan fmt = TREE_VALUE (TREE_CHAIN (arglist)); 10028169699Skan arglist = TREE_CHAIN (TREE_CHAIN (arglist)); 10029169699Skan 10030169699Skan /* Check whether the format is a literal string constant. */ 10031169699Skan fmt_str = c_getstr (fmt); 10032169699Skan if (fmt_str == NULL) 10033169699Skan return NULL_TREE; 10034169699Skan 10035169699Skan call = NULL_TREE; 10036169699Skan retval = NULL_TREE; 10037169699Skan 10038169699Skan if (!init_target_chars()) 10039169699Skan return 0; 10040169699Skan 10041169699Skan /* If the format doesn't contain % args or %%, use strcpy. */ 10042169699Skan if (strchr (fmt_str, target_percent) == NULL) 10043169699Skan { 10044169699Skan tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; 10045169699Skan 10046169699Skan if (!fn) 10047169699Skan return NULL_TREE; 10048169699Skan 10049169699Skan /* Don't optimize sprintf (buf, "abc", ptr++). */ 10050169699Skan if (arglist) 10051169699Skan return NULL_TREE; 10052169699Skan 10053169699Skan /* Convert sprintf (str, fmt) into strcpy (str, fmt) when 10054169699Skan 'format' is known to contain no % formats. */ 10055169699Skan arglist = build_tree_list (NULL_TREE, fmt); 10056169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10057169699Skan call = build_function_call_expr (fn, arglist); 10058169699Skan if (!ignored) 10059169699Skan retval = build_int_cst (NULL_TREE, strlen (fmt_str)); 10060169699Skan } 10061169699Skan 10062169699Skan /* If the format is "%s", use strcpy if the result isn't used. */ 10063169699Skan else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0) 10064169699Skan { 10065169699Skan tree fn, orig; 10066169699Skan fn = implicit_built_in_decls[BUILT_IN_STRCPY]; 10067169699Skan 10068169699Skan if (!fn) 10069169699Skan return NULL_TREE; 10070169699Skan 10071169699Skan /* Don't crash on sprintf (str1, "%s"). */ 10072169699Skan if (!arglist) 10073169699Skan return NULL_TREE; 10074169699Skan 10075169699Skan /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2). */ 10076169699Skan orig = TREE_VALUE (arglist); 10077169699Skan arglist = build_tree_list (NULL_TREE, orig); 10078169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10079169699Skan if (!ignored) 10080169699Skan { 10081169699Skan retval = c_strlen (orig, 1); 10082169699Skan if (!retval || TREE_CODE (retval) != INTEGER_CST) 10083169699Skan return NULL_TREE; 10084169699Skan } 10085169699Skan call = build_function_call_expr (fn, arglist); 10086169699Skan } 10087169699Skan 10088169699Skan if (call && retval) 10089169699Skan { 10090169699Skan retval = fold_convert 10091169699Skan (TREE_TYPE (TREE_TYPE (implicit_built_in_decls[BUILT_IN_SPRINTF])), 10092169699Skan retval); 10093169699Skan return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval); 10094169699Skan } 10095132727Skan else 10096169699Skan return call; 1009790075Sobrien} 10098169699Skan 10099169699Skan/* Expand a call to __builtin_object_size. */ 10100169699Skan 10101169699Skanrtx 10102169699Skanexpand_builtin_object_size (tree exp) 10103169699Skan{ 10104169699Skan tree ost; 10105169699Skan int object_size_type; 10106169699Skan tree fndecl = get_callee_fndecl (exp); 10107169699Skan tree arglist = TREE_OPERAND (exp, 1); 10108169699Skan location_t locus = EXPR_LOCATION (exp); 10109169699Skan 10110169699Skan if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 10111169699Skan { 10112169699Skan error ("%Hfirst argument of %D must be a pointer, second integer constant", 10113169699Skan &locus, fndecl); 10114169699Skan expand_builtin_trap (); 10115169699Skan return const0_rtx; 10116169699Skan } 10117169699Skan 10118169699Skan ost = TREE_VALUE (TREE_CHAIN (arglist)); 10119169699Skan STRIP_NOPS (ost); 10120169699Skan 10121169699Skan if (TREE_CODE (ost) != INTEGER_CST 10122169699Skan || tree_int_cst_sgn (ost) < 0 10123169699Skan || compare_tree_int (ost, 3) > 0) 10124169699Skan { 10125169699Skan error ("%Hlast argument of %D is not integer constant between 0 and 3", 10126169699Skan &locus, fndecl); 10127169699Skan expand_builtin_trap (); 10128169699Skan return const0_rtx; 10129169699Skan } 10130169699Skan 10131169699Skan object_size_type = tree_low_cst (ost, 0); 10132169699Skan 10133169699Skan return object_size_type < 2 ? constm1_rtx : const0_rtx; 10134169699Skan} 10135169699Skan 10136169699Skan/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin. 10137169699Skan FCODE is the BUILT_IN_* to use. 10138169699Skan Return 0 if we failed; the caller should emit a normal call, 10139169699Skan otherwise try to get the result in TARGET, if convenient (and in 10140169699Skan mode MODE if that's convenient). */ 10141169699Skan 10142169699Skanstatic rtx 10143169699Skanexpand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, 10144169699Skan enum built_in_function fcode) 10145169699Skan{ 10146169699Skan tree arglist = TREE_OPERAND (exp, 1); 10147169699Skan tree dest, src, len, size; 10148169699Skan 10149169699Skan if (!validate_arglist (arglist, 10150169699Skan POINTER_TYPE, 10151169699Skan fcode == BUILT_IN_MEMSET_CHK 10152169699Skan ? INTEGER_TYPE : POINTER_TYPE, 10153169699Skan INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) 10154169699Skan return 0; 10155169699Skan 10156169699Skan dest = TREE_VALUE (arglist); 10157169699Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 10158169699Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 10159169699Skan size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); 10160169699Skan 10161169699Skan if (! host_integerp (size, 1)) 10162169699Skan return 0; 10163169699Skan 10164169699Skan if (host_integerp (len, 1) || integer_all_onesp (size)) 10165169699Skan { 10166169699Skan tree fn; 10167169699Skan 10168169699Skan if (! integer_all_onesp (size) && tree_int_cst_lt (size, len)) 10169169699Skan { 10170169699Skan location_t locus = EXPR_LOCATION (exp); 10171169699Skan warning (0, "%Hcall to %D will always overflow destination buffer", 10172169699Skan &locus, get_callee_fndecl (exp)); 10173169699Skan return 0; 10174169699Skan } 10175169699Skan 10176169699Skan arglist = build_tree_list (NULL_TREE, len); 10177169699Skan arglist = tree_cons (NULL_TREE, src, arglist); 10178169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10179169699Skan 10180169699Skan fn = NULL_TREE; 10181169699Skan /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume 10182169699Skan mem{cpy,pcpy,move,set} is available. */ 10183169699Skan switch (fcode) 10184169699Skan { 10185169699Skan case BUILT_IN_MEMCPY_CHK: 10186169699Skan fn = built_in_decls[BUILT_IN_MEMCPY]; 10187169699Skan break; 10188169699Skan case BUILT_IN_MEMPCPY_CHK: 10189169699Skan fn = built_in_decls[BUILT_IN_MEMPCPY]; 10190169699Skan break; 10191169699Skan case BUILT_IN_MEMMOVE_CHK: 10192169699Skan fn = built_in_decls[BUILT_IN_MEMMOVE]; 10193169699Skan break; 10194169699Skan case BUILT_IN_MEMSET_CHK: 10195169699Skan fn = built_in_decls[BUILT_IN_MEMSET]; 10196169699Skan break; 10197169699Skan default: 10198169699Skan break; 10199169699Skan } 10200169699Skan 10201169699Skan if (! fn) 10202169699Skan return 0; 10203169699Skan 10204169699Skan fn = build_function_call_expr (fn, arglist); 10205169699Skan if (TREE_CODE (fn) == CALL_EXPR) 10206169699Skan CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); 10207169699Skan return expand_expr (fn, target, mode, EXPAND_NORMAL); 10208169699Skan } 10209169699Skan else if (fcode == BUILT_IN_MEMSET_CHK) 10210169699Skan return 0; 10211169699Skan else 10212169699Skan { 10213169699Skan unsigned int dest_align 10214169699Skan = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); 10215169699Skan 10216169699Skan /* If DEST is not a pointer type, call the normal function. */ 10217169699Skan if (dest_align == 0) 10218169699Skan return 0; 10219169699Skan 10220169699Skan /* If SRC and DEST are the same (and not volatile), do nothing. */ 10221169699Skan if (operand_equal_p (src, dest, 0)) 10222169699Skan { 10223169699Skan tree expr; 10224169699Skan 10225169699Skan if (fcode != BUILT_IN_MEMPCPY_CHK) 10226169699Skan { 10227169699Skan /* Evaluate and ignore LEN in case it has side-effects. */ 10228169699Skan expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); 10229169699Skan return expand_expr (dest, target, mode, EXPAND_NORMAL); 10230169699Skan } 10231169699Skan 10232169699Skan len = fold_convert (TREE_TYPE (dest), len); 10233169699Skan expr = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len); 10234169699Skan return expand_expr (expr, target, mode, EXPAND_NORMAL); 10235169699Skan } 10236169699Skan 10237169699Skan /* __memmove_chk special case. */ 10238169699Skan if (fcode == BUILT_IN_MEMMOVE_CHK) 10239169699Skan { 10240169699Skan unsigned int src_align 10241169699Skan = get_pointer_alignment (src, BIGGEST_ALIGNMENT); 10242169699Skan 10243169699Skan if (src_align == 0) 10244169699Skan return 0; 10245169699Skan 10246169699Skan /* If src is categorized for a readonly section we can use 10247169699Skan normal __memcpy_chk. */ 10248169699Skan if (readonly_data_expr (src)) 10249169699Skan { 10250169699Skan tree fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; 10251169699Skan if (!fn) 10252169699Skan return 0; 10253169699Skan fn = build_function_call_expr (fn, arglist); 10254169699Skan if (TREE_CODE (fn) == CALL_EXPR) 10255169699Skan CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); 10256169699Skan return expand_expr (fn, target, mode, EXPAND_NORMAL); 10257169699Skan } 10258169699Skan } 10259169699Skan return 0; 10260169699Skan } 10261169699Skan} 10262169699Skan 10263169699Skan/* Emit warning if a buffer overflow is detected at compile time. */ 10264169699Skan 10265169699Skanstatic void 10266169699Skanmaybe_emit_chk_warning (tree exp, enum built_in_function fcode) 10267169699Skan{ 10268169699Skan int arg_mask, is_strlen = 0; 10269169699Skan tree arglist = TREE_OPERAND (exp, 1), a; 10270169699Skan tree len, size; 10271169699Skan location_t locus; 10272169699Skan 10273169699Skan switch (fcode) 10274169699Skan { 10275169699Skan case BUILT_IN_STRCPY_CHK: 10276169699Skan case BUILT_IN_STPCPY_CHK: 10277169699Skan /* For __strcat_chk the warning will be emitted only if overflowing 10278169699Skan by at least strlen (dest) + 1 bytes. */ 10279169699Skan case BUILT_IN_STRCAT_CHK: 10280169699Skan arg_mask = 6; 10281169699Skan is_strlen = 1; 10282169699Skan break; 10283169699Skan case BUILT_IN_STRNCPY_CHK: 10284169699Skan arg_mask = 12; 10285169699Skan break; 10286169699Skan case BUILT_IN_SNPRINTF_CHK: 10287169699Skan case BUILT_IN_VSNPRINTF_CHK: 10288169699Skan arg_mask = 10; 10289169699Skan break; 10290169699Skan default: 10291169699Skan gcc_unreachable (); 10292169699Skan } 10293169699Skan 10294169699Skan len = NULL_TREE; 10295169699Skan size = NULL_TREE; 10296169699Skan for (a = arglist; a && arg_mask; a = TREE_CHAIN (a), arg_mask >>= 1) 10297169699Skan if (arg_mask & 1) 10298169699Skan { 10299169699Skan if (len) 10300169699Skan size = a; 10301169699Skan else 10302169699Skan len = a; 10303169699Skan } 10304169699Skan 10305169699Skan if (!len || !size) 10306169699Skan return; 10307169699Skan 10308169699Skan len = TREE_VALUE (len); 10309169699Skan size = TREE_VALUE (size); 10310169699Skan 10311169699Skan if (! host_integerp (size, 1) || integer_all_onesp (size)) 10312169699Skan return; 10313169699Skan 10314169699Skan if (is_strlen) 10315169699Skan { 10316169699Skan len = c_strlen (len, 1); 10317169699Skan if (! len || ! host_integerp (len, 1) || tree_int_cst_lt (len, size)) 10318169699Skan return; 10319169699Skan } 10320169699Skan else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len)) 10321169699Skan return; 10322169699Skan 10323169699Skan locus = EXPR_LOCATION (exp); 10324169699Skan warning (0, "%Hcall to %D will always overflow destination buffer", 10325169699Skan &locus, get_callee_fndecl (exp)); 10326169699Skan} 10327169699Skan 10328169699Skan/* Emit warning if a buffer overflow is detected at compile time 10329169699Skan in __sprintf_chk/__vsprintf_chk calls. */ 10330169699Skan 10331169699Skanstatic void 10332169699Skanmaybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode) 10333169699Skan{ 10334169699Skan tree arglist = TREE_OPERAND (exp, 1); 10335169699Skan tree dest, size, len, fmt, flag; 10336169699Skan const char *fmt_str; 10337169699Skan 10338169699Skan /* Verify the required arguments in the original call. */ 10339169699Skan if (! arglist) 10340169699Skan return; 10341169699Skan dest = TREE_VALUE (arglist); 10342169699Skan arglist = TREE_CHAIN (arglist); 10343169699Skan if (! arglist) 10344169699Skan return; 10345169699Skan flag = TREE_VALUE (arglist); 10346169699Skan arglist = TREE_CHAIN (arglist); 10347169699Skan if (! arglist) 10348169699Skan return; 10349169699Skan size = TREE_VALUE (arglist); 10350169699Skan arglist = TREE_CHAIN (arglist); 10351169699Skan if (! arglist) 10352169699Skan return; 10353169699Skan fmt = TREE_VALUE (arglist); 10354169699Skan arglist = TREE_CHAIN (arglist); 10355169699Skan 10356169699Skan if (! host_integerp (size, 1) || integer_all_onesp (size)) 10357169699Skan return; 10358169699Skan 10359169699Skan /* Check whether the format is a literal string constant. */ 10360169699Skan fmt_str = c_getstr (fmt); 10361169699Skan if (fmt_str == NULL) 10362169699Skan return; 10363169699Skan 10364169699Skan if (!init_target_chars()) 10365169699Skan return; 10366169699Skan 10367169699Skan /* If the format doesn't contain % args or %%, we know its size. */ 10368169699Skan if (strchr (fmt_str, target_percent) == 0) 10369169699Skan len = build_int_cstu (size_type_node, strlen (fmt_str)); 10370169699Skan /* If the format is "%s" and first ... argument is a string literal, 10371169699Skan we know it too. */ 10372169699Skan else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, target_percent_s) == 0) 10373169699Skan { 10374169699Skan tree arg; 10375169699Skan 10376169699Skan if (! arglist) 10377169699Skan return; 10378169699Skan arg = TREE_VALUE (arglist); 10379169699Skan if (! POINTER_TYPE_P (TREE_TYPE (arg))) 10380169699Skan return; 10381169699Skan 10382169699Skan len = c_strlen (arg, 1); 10383169699Skan if (!len || ! host_integerp (len, 1)) 10384169699Skan return; 10385169699Skan } 10386169699Skan else 10387169699Skan return; 10388169699Skan 10389169699Skan if (! tree_int_cst_lt (len, size)) 10390169699Skan { 10391169699Skan location_t locus = EXPR_LOCATION (exp); 10392169699Skan warning (0, "%Hcall to %D will always overflow destination buffer", 10393169699Skan &locus, get_callee_fndecl (exp)); 10394169699Skan } 10395169699Skan} 10396169699Skan 10397169699Skan/* Fold a call to __builtin_object_size, if possible. */ 10398169699Skan 10399169699Skantree 10400169699Skanfold_builtin_object_size (tree arglist) 10401169699Skan{ 10402169699Skan tree ptr, ost, ret = 0; 10403169699Skan int object_size_type; 10404169699Skan 10405169699Skan if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) 10406169699Skan return 0; 10407169699Skan 10408169699Skan ptr = TREE_VALUE (arglist); 10409169699Skan ost = TREE_VALUE (TREE_CHAIN (arglist)); 10410169699Skan STRIP_NOPS (ost); 10411169699Skan 10412169699Skan if (TREE_CODE (ost) != INTEGER_CST 10413169699Skan || tree_int_cst_sgn (ost) < 0 10414169699Skan || compare_tree_int (ost, 3) > 0) 10415169699Skan return 0; 10416169699Skan 10417169699Skan object_size_type = tree_low_cst (ost, 0); 10418169699Skan 10419169699Skan /* __builtin_object_size doesn't evaluate side-effects in its arguments; 10420169699Skan if there are any side-effects, it returns (size_t) -1 for types 0 and 1 10421169699Skan and (size_t) 0 for types 2 and 3. */ 10422169699Skan if (TREE_SIDE_EFFECTS (ptr)) 10423169699Skan return fold_convert (size_type_node, 10424169699Skan object_size_type < 2 10425169699Skan ? integer_minus_one_node : integer_zero_node); 10426169699Skan 10427169699Skan if (TREE_CODE (ptr) == ADDR_EXPR) 10428169699Skan ret = build_int_cstu (size_type_node, 10429169699Skan compute_builtin_object_size (ptr, object_size_type)); 10430169699Skan 10431169699Skan else if (TREE_CODE (ptr) == SSA_NAME) 10432169699Skan { 10433169699Skan unsigned HOST_WIDE_INT bytes; 10434169699Skan 10435169699Skan /* If object size is not known yet, delay folding until 10436169699Skan later. Maybe subsequent passes will help determining 10437169699Skan it. */ 10438169699Skan bytes = compute_builtin_object_size (ptr, object_size_type); 10439169699Skan if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 10440169699Skan ? -1 : 0)) 10441169699Skan ret = build_int_cstu (size_type_node, bytes); 10442169699Skan } 10443169699Skan 10444169699Skan if (ret) 10445169699Skan { 10446169699Skan ret = force_fit_type (ret, -1, false, false); 10447169699Skan if (TREE_CONSTANT_OVERFLOW (ret)) 10448169699Skan ret = 0; 10449169699Skan } 10450169699Skan 10451169699Skan return ret; 10452169699Skan} 10453169699Skan 10454169699Skan/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin. 10455169699Skan IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* 10456169699Skan code of the builtin. If MAXLEN is not NULL, it is maximum length 10457169699Skan passed as third argument. */ 10458169699Skan 10459169699Skantree 10460169699Skanfold_builtin_memory_chk (tree fndecl, tree arglist, tree maxlen, bool ignore, 10461169699Skan enum built_in_function fcode) 10462169699Skan{ 10463169699Skan tree dest, src, len, size, fn; 10464169699Skan 10465169699Skan if (!validate_arglist (arglist, 10466169699Skan POINTER_TYPE, 10467169699Skan fcode == BUILT_IN_MEMSET_CHK 10468169699Skan ? INTEGER_TYPE : POINTER_TYPE, 10469169699Skan INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) 10470169699Skan return 0; 10471169699Skan 10472169699Skan dest = TREE_VALUE (arglist); 10473169699Skan /* Actually val for __memset_chk, but it doesn't matter. */ 10474169699Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 10475169699Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 10476169699Skan size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); 10477169699Skan 10478169699Skan /* If SRC and DEST are the same (and not volatile), return DEST 10479169699Skan (resp. DEST+LEN for __mempcpy_chk). */ 10480169699Skan if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0)) 10481169699Skan { 10482169699Skan if (fcode != BUILT_IN_MEMPCPY_CHK) 10483169699Skan return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); 10484169699Skan else 10485169699Skan { 10486169699Skan tree temp = fold_convert (TREE_TYPE (dest), len); 10487169699Skan temp = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp); 10488169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), temp); 10489169699Skan } 10490169699Skan } 10491169699Skan 10492169699Skan if (! host_integerp (size, 1)) 10493169699Skan return 0; 10494169699Skan 10495169699Skan if (! integer_all_onesp (size)) 10496169699Skan { 10497169699Skan if (! host_integerp (len, 1)) 10498169699Skan { 10499169699Skan /* If LEN is not constant, try MAXLEN too. 10500169699Skan For MAXLEN only allow optimizing into non-_ocs function 10501169699Skan if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ 10502169699Skan if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) 10503169699Skan { 10504169699Skan if (fcode == BUILT_IN_MEMPCPY_CHK && ignore) 10505169699Skan { 10506169699Skan /* (void) __mempcpy_chk () can be optimized into 10507169699Skan (void) __memcpy_chk (). */ 10508169699Skan fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; 10509169699Skan if (!fn) 10510169699Skan return 0; 10511169699Skan 10512169699Skan return build_function_call_expr (fn, arglist); 10513169699Skan } 10514169699Skan return 0; 10515169699Skan } 10516169699Skan } 10517169699Skan else 10518169699Skan maxlen = len; 10519169699Skan 10520169699Skan if (tree_int_cst_lt (size, maxlen)) 10521169699Skan return 0; 10522169699Skan } 10523169699Skan 10524169699Skan arglist = build_tree_list (NULL_TREE, len); 10525169699Skan arglist = tree_cons (NULL_TREE, src, arglist); 10526169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10527169699Skan 10528169699Skan fn = NULL_TREE; 10529169699Skan /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume 10530169699Skan mem{cpy,pcpy,move,set} is available. */ 10531169699Skan switch (fcode) 10532169699Skan { 10533169699Skan case BUILT_IN_MEMCPY_CHK: 10534169699Skan fn = built_in_decls[BUILT_IN_MEMCPY]; 10535169699Skan break; 10536169699Skan case BUILT_IN_MEMPCPY_CHK: 10537169699Skan fn = built_in_decls[BUILT_IN_MEMPCPY]; 10538169699Skan break; 10539169699Skan case BUILT_IN_MEMMOVE_CHK: 10540169699Skan fn = built_in_decls[BUILT_IN_MEMMOVE]; 10541169699Skan break; 10542169699Skan case BUILT_IN_MEMSET_CHK: 10543169699Skan fn = built_in_decls[BUILT_IN_MEMSET]; 10544169699Skan break; 10545169699Skan default: 10546169699Skan break; 10547169699Skan } 10548169699Skan 10549169699Skan if (!fn) 10550169699Skan return 0; 10551169699Skan 10552169699Skan return build_function_call_expr (fn, arglist); 10553169699Skan} 10554169699Skan 10555169699Skan/* Fold a call to the __st[rp]cpy_chk builtin. 10556169699Skan IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* 10557169699Skan code of the builtin. If MAXLEN is not NULL, it is maximum length of 10558169699Skan strings passed as second argument. */ 10559169699Skan 10560169699Skantree 10561169699Skanfold_builtin_stxcpy_chk (tree fndecl, tree arglist, tree maxlen, bool ignore, 10562169699Skan enum built_in_function fcode) 10563169699Skan{ 10564169699Skan tree dest, src, size, len, fn; 10565169699Skan 10566169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, 10567169699Skan VOID_TYPE)) 10568169699Skan return 0; 10569169699Skan 10570169699Skan dest = TREE_VALUE (arglist); 10571169699Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 10572169699Skan size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 10573169699Skan 10574169699Skan /* If SRC and DEST are the same (and not volatile), return DEST. */ 10575169699Skan if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0)) 10576169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest); 10577169699Skan 10578169699Skan if (! host_integerp (size, 1)) 10579169699Skan return 0; 10580169699Skan 10581169699Skan if (! integer_all_onesp (size)) 10582169699Skan { 10583169699Skan len = c_strlen (src, 1); 10584169699Skan if (! len || ! host_integerp (len, 1)) 10585169699Skan { 10586169699Skan /* If LEN is not constant, try MAXLEN too. 10587169699Skan For MAXLEN only allow optimizing into non-_ocs function 10588169699Skan if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ 10589169699Skan if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) 10590169699Skan { 10591169699Skan if (fcode == BUILT_IN_STPCPY_CHK) 10592169699Skan { 10593169699Skan if (! ignore) 10594169699Skan return 0; 10595169699Skan 10596169699Skan /* If return value of __stpcpy_chk is ignored, 10597169699Skan optimize into __strcpy_chk. */ 10598169699Skan fn = built_in_decls[BUILT_IN_STRCPY_CHK]; 10599169699Skan if (!fn) 10600169699Skan return 0; 10601169699Skan 10602169699Skan return build_function_call_expr (fn, arglist); 10603169699Skan } 10604169699Skan 10605169699Skan if (! len || TREE_SIDE_EFFECTS (len)) 10606169699Skan return 0; 10607169699Skan 10608169699Skan /* If c_strlen returned something, but not a constant, 10609169699Skan transform __strcpy_chk into __memcpy_chk. */ 10610169699Skan fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; 10611169699Skan if (!fn) 10612169699Skan return 0; 10613169699Skan 10614169699Skan len = size_binop (PLUS_EXPR, len, ssize_int (1)); 10615169699Skan arglist = build_tree_list (NULL_TREE, size); 10616169699Skan arglist = tree_cons (NULL_TREE, len, arglist); 10617169699Skan arglist = tree_cons (NULL_TREE, src, arglist); 10618169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10619169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), 10620169699Skan build_function_call_expr (fn, arglist)); 10621169699Skan } 10622169699Skan } 10623169699Skan else 10624169699Skan maxlen = len; 10625169699Skan 10626169699Skan if (! tree_int_cst_lt (maxlen, size)) 10627169699Skan return 0; 10628169699Skan } 10629169699Skan 10630169699Skan arglist = build_tree_list (NULL_TREE, src); 10631169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10632169699Skan 10633169699Skan /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */ 10634169699Skan fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK 10635169699Skan ? BUILT_IN_STPCPY : BUILT_IN_STRCPY]; 10636169699Skan if (!fn) 10637169699Skan return 0; 10638169699Skan 10639169699Skan return build_function_call_expr (fn, arglist); 10640169699Skan} 10641169699Skan 10642169699Skan/* Fold a call to the __strncpy_chk builtin. 10643169699Skan If MAXLEN is not NULL, it is maximum length passed as third argument. */ 10644169699Skan 10645169699Skantree 10646169699Skanfold_builtin_strncpy_chk (tree arglist, tree maxlen) 10647169699Skan{ 10648169699Skan tree dest, src, size, len, fn; 10649169699Skan 10650169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, 10651169699Skan INTEGER_TYPE, VOID_TYPE)) 10652169699Skan return 0; 10653169699Skan 10654169699Skan dest = TREE_VALUE (arglist); 10655169699Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 10656169699Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 10657169699Skan size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); 10658169699Skan 10659169699Skan if (! host_integerp (size, 1)) 10660169699Skan return 0; 10661169699Skan 10662169699Skan if (! integer_all_onesp (size)) 10663169699Skan { 10664169699Skan if (! host_integerp (len, 1)) 10665169699Skan { 10666169699Skan /* If LEN is not constant, try MAXLEN too. 10667169699Skan For MAXLEN only allow optimizing into non-_ocs function 10668169699Skan if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ 10669169699Skan if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) 10670169699Skan return 0; 10671169699Skan } 10672169699Skan else 10673169699Skan maxlen = len; 10674169699Skan 10675169699Skan if (tree_int_cst_lt (size, maxlen)) 10676169699Skan return 0; 10677169699Skan } 10678169699Skan 10679169699Skan arglist = build_tree_list (NULL_TREE, len); 10680169699Skan arglist = tree_cons (NULL_TREE, src, arglist); 10681169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10682169699Skan 10683169699Skan /* If __builtin_strncpy_chk is used, assume strncpy is available. */ 10684169699Skan fn = built_in_decls[BUILT_IN_STRNCPY]; 10685169699Skan if (!fn) 10686169699Skan return 0; 10687169699Skan 10688169699Skan return build_function_call_expr (fn, arglist); 10689169699Skan} 10690169699Skan 10691169699Skan/* Fold a call to the __strcat_chk builtin FNDECL with ARGLIST. */ 10692169699Skan 10693169699Skanstatic tree 10694169699Skanfold_builtin_strcat_chk (tree fndecl, tree arglist) 10695169699Skan{ 10696169699Skan tree dest, src, size, fn; 10697169699Skan const char *p; 10698169699Skan 10699169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, 10700169699Skan VOID_TYPE)) 10701169699Skan return 0; 10702169699Skan 10703169699Skan dest = TREE_VALUE (arglist); 10704169699Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 10705169699Skan size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 10706169699Skan 10707169699Skan p = c_getstr (src); 10708169699Skan /* If the SRC parameter is "", return DEST. */ 10709169699Skan if (p && *p == '\0') 10710169699Skan return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); 10711169699Skan 10712169699Skan if (! host_integerp (size, 1) || ! integer_all_onesp (size)) 10713169699Skan return 0; 10714169699Skan 10715169699Skan arglist = build_tree_list (NULL_TREE, src); 10716169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10717169699Skan 10718169699Skan /* If __builtin_strcat_chk is used, assume strcat is available. */ 10719169699Skan fn = built_in_decls[BUILT_IN_STRCAT]; 10720169699Skan if (!fn) 10721169699Skan return 0; 10722169699Skan 10723169699Skan return build_function_call_expr (fn, arglist); 10724169699Skan} 10725169699Skan 10726169699Skan/* Fold a call to the __strncat_chk builtin EXP. */ 10727169699Skan 10728169699Skanstatic tree 10729169699Skanfold_builtin_strncat_chk (tree fndecl, tree arglist) 10730169699Skan{ 10731169699Skan tree dest, src, size, len, fn; 10732169699Skan const char *p; 10733169699Skan 10734169699Skan if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, 10735169699Skan INTEGER_TYPE, VOID_TYPE)) 10736169699Skan return 0; 10737169699Skan 10738169699Skan dest = TREE_VALUE (arglist); 10739169699Skan src = TREE_VALUE (TREE_CHAIN (arglist)); 10740169699Skan len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); 10741169699Skan size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); 10742169699Skan 10743169699Skan p = c_getstr (src); 10744169699Skan /* If the SRC parameter is "" or if LEN is 0, return DEST. */ 10745169699Skan if (p && *p == '\0') 10746169699Skan return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); 10747169699Skan else if (integer_zerop (len)) 10748169699Skan return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); 10749169699Skan 10750169699Skan if (! host_integerp (size, 1)) 10751169699Skan return 0; 10752169699Skan 10753169699Skan if (! integer_all_onesp (size)) 10754169699Skan { 10755169699Skan tree src_len = c_strlen (src, 1); 10756169699Skan if (src_len 10757169699Skan && host_integerp (src_len, 1) 10758169699Skan && host_integerp (len, 1) 10759169699Skan && ! tree_int_cst_lt (len, src_len)) 10760169699Skan { 10761169699Skan /* If LEN >= strlen (SRC), optimize into __strcat_chk. */ 10762169699Skan fn = built_in_decls[BUILT_IN_STRCAT_CHK]; 10763169699Skan if (!fn) 10764169699Skan return 0; 10765169699Skan 10766169699Skan arglist = build_tree_list (NULL_TREE, size); 10767169699Skan arglist = tree_cons (NULL_TREE, src, arglist); 10768169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10769169699Skan return build_function_call_expr (fn, arglist); 10770169699Skan } 10771169699Skan return 0; 10772169699Skan } 10773169699Skan 10774169699Skan arglist = build_tree_list (NULL_TREE, len); 10775169699Skan arglist = tree_cons (NULL_TREE, src, arglist); 10776169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10777169699Skan 10778169699Skan /* If __builtin_strncat_chk is used, assume strncat is available. */ 10779169699Skan fn = built_in_decls[BUILT_IN_STRNCAT]; 10780169699Skan if (!fn) 10781169699Skan return 0; 10782169699Skan 10783169699Skan return build_function_call_expr (fn, arglist); 10784169699Skan} 10785169699Skan 10786169699Skan/* Fold a call to __{,v}sprintf_chk with argument list ARGLIST. Return 0 if 10787169699Skan a normal call should be emitted rather than expanding the function 10788169699Skan inline. FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK. */ 10789169699Skan 10790169699Skanstatic tree 10791169699Skanfold_builtin_sprintf_chk (tree arglist, enum built_in_function fcode) 10792169699Skan{ 10793169699Skan tree dest, size, len, fn, fmt, flag; 10794169699Skan const char *fmt_str; 10795169699Skan 10796169699Skan /* Verify the required arguments in the original call. */ 10797169699Skan if (! arglist) 10798169699Skan return 0; 10799169699Skan dest = TREE_VALUE (arglist); 10800169699Skan if (! POINTER_TYPE_P (TREE_TYPE (dest))) 10801169699Skan return 0; 10802169699Skan arglist = TREE_CHAIN (arglist); 10803169699Skan if (! arglist) 10804169699Skan return 0; 10805169699Skan flag = TREE_VALUE (arglist); 10806169699Skan if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE) 10807169699Skan return 0; 10808169699Skan arglist = TREE_CHAIN (arglist); 10809169699Skan if (! arglist) 10810169699Skan return 0; 10811169699Skan size = TREE_VALUE (arglist); 10812169699Skan if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE) 10813169699Skan return 0; 10814169699Skan arglist = TREE_CHAIN (arglist); 10815169699Skan if (! arglist) 10816169699Skan return 0; 10817169699Skan fmt = TREE_VALUE (arglist); 10818169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fmt))) 10819169699Skan return 0; 10820169699Skan arglist = TREE_CHAIN (arglist); 10821169699Skan 10822169699Skan if (! host_integerp (size, 1)) 10823169699Skan return 0; 10824169699Skan 10825169699Skan len = NULL_TREE; 10826169699Skan 10827169699Skan if (!init_target_chars()) 10828169699Skan return 0; 10829169699Skan 10830169699Skan /* Check whether the format is a literal string constant. */ 10831169699Skan fmt_str = c_getstr (fmt); 10832169699Skan if (fmt_str != NULL) 10833169699Skan { 10834169699Skan /* If the format doesn't contain % args or %%, we know the size. */ 10835169699Skan if (strchr (fmt_str, target_percent) == 0) 10836169699Skan { 10837169699Skan if (fcode != BUILT_IN_SPRINTF_CHK || arglist == NULL_TREE) 10838169699Skan len = build_int_cstu (size_type_node, strlen (fmt_str)); 10839169699Skan } 10840169699Skan /* If the format is "%s" and first ... argument is a string literal, 10841169699Skan we know the size too. */ 10842169699Skan else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, target_percent_s) == 0) 10843169699Skan { 10844169699Skan tree arg; 10845169699Skan 10846169699Skan if (arglist && !TREE_CHAIN (arglist)) 10847169699Skan { 10848169699Skan arg = TREE_VALUE (arglist); 10849169699Skan if (POINTER_TYPE_P (TREE_TYPE (arg))) 10850169699Skan { 10851169699Skan len = c_strlen (arg, 1); 10852169699Skan if (! len || ! host_integerp (len, 1)) 10853169699Skan len = NULL_TREE; 10854169699Skan } 10855169699Skan } 10856169699Skan } 10857169699Skan } 10858169699Skan 10859169699Skan if (! integer_all_onesp (size)) 10860169699Skan { 10861169699Skan if (! len || ! tree_int_cst_lt (len, size)) 10862169699Skan return 0; 10863169699Skan } 10864169699Skan 10865169699Skan /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0 10866169699Skan or if format doesn't contain % chars or is "%s". */ 10867169699Skan if (! integer_zerop (flag)) 10868169699Skan { 10869169699Skan if (fmt_str == NULL) 10870169699Skan return 0; 10871169699Skan if (strchr (fmt_str, target_percent) != NULL && strcmp (fmt_str, target_percent_s)) 10872169699Skan return 0; 10873169699Skan } 10874169699Skan 10875169699Skan arglist = tree_cons (NULL_TREE, fmt, arglist); 10876169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10877169699Skan 10878169699Skan /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */ 10879169699Skan fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK 10880169699Skan ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF]; 10881169699Skan if (!fn) 10882169699Skan return 0; 10883169699Skan 10884169699Skan return build_function_call_expr (fn, arglist); 10885169699Skan} 10886169699Skan 10887169699Skan/* Fold a call to {,v}snprintf with argument list ARGLIST. Return 0 if 10888169699Skan a normal call should be emitted rather than expanding the function 10889169699Skan inline. FCODE is either BUILT_IN_SNPRINTF_CHK or 10890169699Skan BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length 10891169699Skan passed as second argument. */ 10892169699Skan 10893169699Skantree 10894169699Skanfold_builtin_snprintf_chk (tree arglist, tree maxlen, 10895169699Skan enum built_in_function fcode) 10896169699Skan{ 10897169699Skan tree dest, size, len, fn, fmt, flag; 10898169699Skan const char *fmt_str; 10899169699Skan 10900169699Skan /* Verify the required arguments in the original call. */ 10901169699Skan if (! arglist) 10902169699Skan return 0; 10903169699Skan dest = TREE_VALUE (arglist); 10904169699Skan if (! POINTER_TYPE_P (TREE_TYPE (dest))) 10905169699Skan return 0; 10906169699Skan arglist = TREE_CHAIN (arglist); 10907169699Skan if (! arglist) 10908169699Skan return 0; 10909169699Skan len = TREE_VALUE (arglist); 10910169699Skan if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE) 10911169699Skan return 0; 10912169699Skan arglist = TREE_CHAIN (arglist); 10913169699Skan if (! arglist) 10914169699Skan return 0; 10915169699Skan flag = TREE_VALUE (arglist); 10916169699Skan if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE) 10917169699Skan return 0; 10918169699Skan arglist = TREE_CHAIN (arglist); 10919169699Skan if (! arglist) 10920169699Skan return 0; 10921169699Skan size = TREE_VALUE (arglist); 10922169699Skan if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE) 10923169699Skan return 0; 10924169699Skan arglist = TREE_CHAIN (arglist); 10925169699Skan if (! arglist) 10926169699Skan return 0; 10927169699Skan fmt = TREE_VALUE (arglist); 10928169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fmt))) 10929169699Skan return 0; 10930169699Skan arglist = TREE_CHAIN (arglist); 10931169699Skan 10932169699Skan if (! host_integerp (size, 1)) 10933169699Skan return 0; 10934169699Skan 10935169699Skan if (! integer_all_onesp (size)) 10936169699Skan { 10937169699Skan if (! host_integerp (len, 1)) 10938169699Skan { 10939169699Skan /* If LEN is not constant, try MAXLEN too. 10940169699Skan For MAXLEN only allow optimizing into non-_ocs function 10941169699Skan if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ 10942169699Skan if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) 10943169699Skan return 0; 10944169699Skan } 10945169699Skan else 10946169699Skan maxlen = len; 10947169699Skan 10948169699Skan if (tree_int_cst_lt (size, maxlen)) 10949169699Skan return 0; 10950169699Skan } 10951169699Skan 10952169699Skan if (!init_target_chars()) 10953169699Skan return 0; 10954169699Skan 10955169699Skan /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0 10956169699Skan or if format doesn't contain % chars or is "%s". */ 10957169699Skan if (! integer_zerop (flag)) 10958169699Skan { 10959169699Skan fmt_str = c_getstr (fmt); 10960169699Skan if (fmt_str == NULL) 10961169699Skan return 0; 10962169699Skan if (strchr (fmt_str, target_percent) != NULL && strcmp (fmt_str, target_percent_s)) 10963169699Skan return 0; 10964169699Skan } 10965169699Skan 10966169699Skan arglist = tree_cons (NULL_TREE, fmt, arglist); 10967169699Skan arglist = tree_cons (NULL_TREE, len, arglist); 10968169699Skan arglist = tree_cons (NULL_TREE, dest, arglist); 10969169699Skan 10970169699Skan /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is 10971169699Skan available. */ 10972169699Skan fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK 10973169699Skan ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF]; 10974169699Skan if (!fn) 10975169699Skan return 0; 10976169699Skan 10977169699Skan return build_function_call_expr (fn, arglist); 10978169699Skan} 10979169699Skan 10980169699Skan/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins. 10981169699Skan 10982169699Skan Return 0 if no simplification was possible, otherwise return the 10983169699Skan simplified form of the call as a tree. FCODE is the BUILT_IN_* 10984169699Skan code of the function to be simplified. */ 10985169699Skan 10986169699Skanstatic tree 10987169699Skanfold_builtin_printf (tree fndecl, tree arglist, bool ignore, 10988169699Skan enum built_in_function fcode) 10989169699Skan{ 10990169699Skan tree fmt, fn = NULL_TREE, fn_putchar, fn_puts, arg, call; 10991169699Skan const char *fmt_str = NULL; 10992169699Skan 10993169699Skan /* If the return value is used, don't do the transformation. */ 10994169699Skan if (! ignore) 10995169699Skan return 0; 10996169699Skan 10997169699Skan /* Verify the required arguments in the original call. */ 10998169699Skan if (fcode == BUILT_IN_PRINTF_CHK || fcode == BUILT_IN_VPRINTF_CHK) 10999169699Skan { 11000169699Skan tree flag; 11001169699Skan 11002169699Skan if (! arglist) 11003169699Skan return 0; 11004169699Skan flag = TREE_VALUE (arglist); 11005169699Skan if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE 11006169699Skan || TREE_SIDE_EFFECTS (flag)) 11007169699Skan return 0; 11008169699Skan arglist = TREE_CHAIN (arglist); 11009169699Skan } 11010169699Skan 11011169699Skan if (! arglist) 11012169699Skan return 0; 11013169699Skan fmt = TREE_VALUE (arglist); 11014169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fmt))) 11015169699Skan return 0; 11016169699Skan arglist = TREE_CHAIN (arglist); 11017169699Skan 11018169699Skan /* Check whether the format is a literal string constant. */ 11019169699Skan fmt_str = c_getstr (fmt); 11020169699Skan if (fmt_str == NULL) 11021169699Skan return NULL_TREE; 11022169699Skan 11023169699Skan if (fcode == BUILT_IN_PRINTF_UNLOCKED) 11024169699Skan { 11025169699Skan /* If we're using an unlocked function, assume the other 11026169699Skan unlocked functions exist explicitly. */ 11027169699Skan fn_putchar = built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]; 11028169699Skan fn_puts = built_in_decls[BUILT_IN_PUTS_UNLOCKED]; 11029169699Skan } 11030169699Skan else 11031169699Skan { 11032169699Skan fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR]; 11033169699Skan fn_puts = implicit_built_in_decls[BUILT_IN_PUTS]; 11034169699Skan } 11035169699Skan 11036169699Skan if (!init_target_chars()) 11037169699Skan return 0; 11038169699Skan 11039169699Skan if (strcmp (fmt_str, target_percent_s) == 0 || strchr (fmt_str, target_percent) == NULL) 11040169699Skan { 11041169699Skan const char *str; 11042169699Skan 11043169699Skan if (strcmp (fmt_str, target_percent_s) == 0) 11044169699Skan { 11045169699Skan if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) 11046169699Skan return 0; 11047169699Skan 11048169699Skan if (! arglist 11049169699Skan || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) 11050169699Skan || TREE_CHAIN (arglist)) 11051169699Skan return 0; 11052169699Skan 11053169699Skan str = c_getstr (TREE_VALUE (arglist)); 11054169699Skan if (str == NULL) 11055169699Skan return 0; 11056169699Skan } 11057169699Skan else 11058169699Skan { 11059169699Skan /* The format specifier doesn't contain any '%' characters. */ 11060169699Skan if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK 11061169699Skan && arglist) 11062169699Skan return 0; 11063169699Skan str = fmt_str; 11064169699Skan } 11065169699Skan 11066169699Skan /* If the string was "", printf does nothing. */ 11067169699Skan if (str[0] == '\0') 11068169699Skan return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); 11069169699Skan 11070169699Skan /* If the string has length of 1, call putchar. */ 11071169699Skan if (str[1] == '\0') 11072169699Skan { 11073169699Skan /* Given printf("c"), (where c is any one character,) 11074169699Skan convert "c"[0] to an int and pass that to the replacement 11075169699Skan function. */ 11076169699Skan arg = build_int_cst (NULL_TREE, str[0]); 11077169699Skan arglist = build_tree_list (NULL_TREE, arg); 11078169699Skan fn = fn_putchar; 11079169699Skan } 11080169699Skan else 11081169699Skan { 11082169699Skan /* If the string was "string\n", call puts("string"). */ 11083169699Skan size_t len = strlen (str); 11084169699Skan if ((unsigned char)str[len - 1] == target_newline) 11085169699Skan { 11086169699Skan /* Create a NUL-terminated string that's one char shorter 11087169699Skan than the original, stripping off the trailing '\n'. */ 11088169699Skan char *newstr = alloca (len); 11089169699Skan memcpy (newstr, str, len - 1); 11090169699Skan newstr[len - 1] = 0; 11091169699Skan 11092169699Skan arg = build_string_literal (len, newstr); 11093169699Skan arglist = build_tree_list (NULL_TREE, arg); 11094169699Skan fn = fn_puts; 11095169699Skan } 11096169699Skan else 11097169699Skan /* We'd like to arrange to call fputs(string,stdout) here, 11098169699Skan but we need stdout and don't have a way to get it yet. */ 11099169699Skan return 0; 11100169699Skan } 11101169699Skan } 11102169699Skan 11103169699Skan /* The other optimizations can be done only on the non-va_list variants. */ 11104169699Skan else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) 11105169699Skan return 0; 11106169699Skan 11107169699Skan /* If the format specifier was "%s\n", call __builtin_puts(arg). */ 11108169699Skan else if (strcmp (fmt_str, target_percent_s_newline) == 0) 11109169699Skan { 11110169699Skan if (! arglist 11111169699Skan || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) 11112169699Skan || TREE_CHAIN (arglist)) 11113169699Skan return 0; 11114169699Skan fn = fn_puts; 11115169699Skan } 11116169699Skan 11117169699Skan /* If the format specifier was "%c", call __builtin_putchar(arg). */ 11118169699Skan else if (strcmp (fmt_str, target_percent_c) == 0) 11119169699Skan { 11120169699Skan if (! arglist 11121169699Skan || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE 11122169699Skan || TREE_CHAIN (arglist)) 11123169699Skan return 0; 11124169699Skan fn = fn_putchar; 11125169699Skan } 11126169699Skan 11127169699Skan if (!fn) 11128169699Skan return 0; 11129169699Skan 11130169699Skan call = build_function_call_expr (fn, arglist); 11131169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call); 11132169699Skan} 11133169699Skan 11134169699Skan/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins. 11135169699Skan 11136169699Skan Return 0 if no simplification was possible, otherwise return the 11137169699Skan simplified form of the call as a tree. FCODE is the BUILT_IN_* 11138169699Skan code of the function to be simplified. */ 11139169699Skan 11140169699Skanstatic tree 11141169699Skanfold_builtin_fprintf (tree fndecl, tree arglist, bool ignore, 11142169699Skan enum built_in_function fcode) 11143169699Skan{ 11144169699Skan tree fp, fmt, fn = NULL_TREE, fn_fputc, fn_fputs, arg, call; 11145169699Skan const char *fmt_str = NULL; 11146169699Skan 11147169699Skan /* If the return value is used, don't do the transformation. */ 11148169699Skan if (! ignore) 11149169699Skan return 0; 11150169699Skan 11151169699Skan /* Verify the required arguments in the original call. */ 11152169699Skan if (! arglist) 11153169699Skan return 0; 11154169699Skan fp = TREE_VALUE (arglist); 11155169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fp))) 11156169699Skan return 0; 11157169699Skan arglist = TREE_CHAIN (arglist); 11158169699Skan 11159169699Skan if (fcode == BUILT_IN_FPRINTF_CHK || fcode == BUILT_IN_VFPRINTF_CHK) 11160169699Skan { 11161169699Skan tree flag; 11162169699Skan 11163169699Skan if (! arglist) 11164169699Skan return 0; 11165169699Skan flag = TREE_VALUE (arglist); 11166169699Skan if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE 11167169699Skan || TREE_SIDE_EFFECTS (flag)) 11168169699Skan return 0; 11169169699Skan arglist = TREE_CHAIN (arglist); 11170169699Skan } 11171169699Skan 11172169699Skan if (! arglist) 11173169699Skan return 0; 11174169699Skan fmt = TREE_VALUE (arglist); 11175169699Skan if (! POINTER_TYPE_P (TREE_TYPE (fmt))) 11176169699Skan return 0; 11177169699Skan arglist = TREE_CHAIN (arglist); 11178169699Skan 11179169699Skan /* Check whether the format is a literal string constant. */ 11180169699Skan fmt_str = c_getstr (fmt); 11181169699Skan if (fmt_str == NULL) 11182169699Skan return NULL_TREE; 11183169699Skan 11184169699Skan if (fcode == BUILT_IN_FPRINTF_UNLOCKED) 11185169699Skan { 11186169699Skan /* If we're using an unlocked function, assume the other 11187169699Skan unlocked functions exist explicitly. */ 11188169699Skan fn_fputc = built_in_decls[BUILT_IN_FPUTC_UNLOCKED]; 11189169699Skan fn_fputs = built_in_decls[BUILT_IN_FPUTS_UNLOCKED]; 11190169699Skan } 11191169699Skan else 11192169699Skan { 11193169699Skan fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC]; 11194169699Skan fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS]; 11195169699Skan } 11196169699Skan 11197169699Skan if (!init_target_chars()) 11198169699Skan return 0; 11199169699Skan 11200169699Skan /* If the format doesn't contain % args or %%, use strcpy. */ 11201169699Skan if (strchr (fmt_str, target_percent) == NULL) 11202169699Skan { 11203169699Skan if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK 11204169699Skan && arglist) 11205169699Skan return 0; 11206169699Skan 11207169699Skan /* If the format specifier was "", fprintf does nothing. */ 11208169699Skan if (fmt_str[0] == '\0') 11209169699Skan { 11210169699Skan /* If FP has side-effects, just wait until gimplification is 11211169699Skan done. */ 11212169699Skan if (TREE_SIDE_EFFECTS (fp)) 11213169699Skan return 0; 11214169699Skan 11215169699Skan return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); 11216169699Skan } 11217169699Skan 11218169699Skan /* When "string" doesn't contain %, replace all cases of 11219169699Skan fprintf (fp, string) with fputs (string, fp). The fputs 11220169699Skan builtin will take care of special cases like length == 1. */ 11221169699Skan arglist = build_tree_list (NULL_TREE, fp); 11222169699Skan arglist = tree_cons (NULL_TREE, fmt, arglist); 11223169699Skan fn = fn_fputs; 11224169699Skan } 11225169699Skan 11226169699Skan /* The other optimizations can be done only on the non-va_list variants. */ 11227169699Skan else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK) 11228169699Skan return 0; 11229169699Skan 11230169699Skan /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */ 11231169699Skan else if (strcmp (fmt_str, target_percent_s) == 0) 11232169699Skan { 11233169699Skan if (! arglist 11234169699Skan || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) 11235169699Skan || TREE_CHAIN (arglist)) 11236169699Skan return 0; 11237169699Skan arg = TREE_VALUE (arglist); 11238169699Skan arglist = build_tree_list (NULL_TREE, fp); 11239169699Skan arglist = tree_cons (NULL_TREE, arg, arglist); 11240169699Skan fn = fn_fputs; 11241169699Skan } 11242169699Skan 11243169699Skan /* If the format specifier was "%c", call __builtin_fputc (arg, fp). */ 11244169699Skan else if (strcmp (fmt_str, target_percent_c) == 0) 11245169699Skan { 11246169699Skan if (! arglist 11247169699Skan || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE 11248169699Skan || TREE_CHAIN (arglist)) 11249169699Skan return 0; 11250169699Skan arg = TREE_VALUE (arglist); 11251169699Skan arglist = build_tree_list (NULL_TREE, fp); 11252169699Skan arglist = tree_cons (NULL_TREE, arg, arglist); 11253169699Skan fn = fn_fputc; 11254169699Skan } 11255169699Skan 11256169699Skan if (!fn) 11257169699Skan return 0; 11258169699Skan 11259169699Skan call = build_function_call_expr (fn, arglist); 11260169699Skan return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call); 11261169699Skan} 11262169699Skan 11263169699Skan/* Initialize format string characters in the target charset. */ 11264169699Skan 11265169699Skanstatic bool 11266169699Skaninit_target_chars (void) 11267169699Skan{ 11268169699Skan static bool init; 11269169699Skan if (!init) 11270169699Skan { 11271169699Skan target_newline = lang_hooks.to_target_charset ('\n'); 11272169699Skan target_percent = lang_hooks.to_target_charset ('%'); 11273169699Skan target_c = lang_hooks.to_target_charset ('c'); 11274169699Skan target_s = lang_hooks.to_target_charset ('s'); 11275169699Skan if (target_newline == 0 || target_percent == 0 || target_c == 0 11276169699Skan || target_s == 0) 11277169699Skan return false; 11278169699Skan 11279169699Skan target_percent_c[0] = target_percent; 11280169699Skan target_percent_c[1] = target_c; 11281169699Skan target_percent_c[2] = '\0'; 11282169699Skan 11283169699Skan target_percent_s[0] = target_percent; 11284169699Skan target_percent_s[1] = target_s; 11285169699Skan target_percent_s[2] = '\0'; 11286169699Skan 11287169699Skan target_percent_s_newline[0] = target_percent; 11288169699Skan target_percent_s_newline[1] = target_s; 11289169699Skan target_percent_s_newline[2] = target_newline; 11290169699Skan target_percent_s_newline[3] = '\0'; 11291169699Skan 11292169699Skan init = true; 11293169699Skan } 11294169699Skan return true; 11295169699Skan} 11296