X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fbuiltins.c;h=f6095e84bed3db8cb8b2700aae47a4d5766d2e8a;hb=300b0e0817a85c8c855f265eeb6e7bdea3160645;hp=1c03803897951313abccc8ccc02c72265a457f96;hpb=f27ba301fb6bbe51a6413456d412d41a9e113037;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/builtins.c b/gcc/builtins.c index 1c038038979..f6095e84bed 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -1,6 +1,6 @@ /* Expand builtin functions. Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GCC. @@ -16,8 +16,8 @@ for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ #include "config.h" #include "system.h" @@ -48,9 +48,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "basic-block.h" #include "tree-mudflap.h" -#define CALLED_AS_BUILT_IN(NODE) \ - (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10)) - #ifndef PAD_VARARGS_DOWN #define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN #endif @@ -78,8 +75,7 @@ static int get_pointer_alignment (tree, unsigned int); static const char *c_getstr (tree); static rtx c_readstr (const char *, enum machine_mode); static int target_char_cast (tree, char *); -static rtx get_memory_rtx (tree); -static tree build_string_literal (int, const char *); +static rtx get_memory_rtx (tree, tree); static int apply_args_size (void); static int apply_result_size (void); #if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) @@ -98,6 +94,8 @@ static void expand_errno_check (tree, rtx); static rtx expand_builtin_mathfn (tree, rtx, rtx); static rtx expand_builtin_mathfn_2 (tree, rtx, rtx); static rtx expand_builtin_mathfn_3 (tree, rtx, rtx); +static rtx expand_builtin_sincos (tree); +static rtx expand_builtin_int_roundingfn (tree, rtx, rtx); static rtx expand_builtin_args_info (tree); static rtx expand_builtin_next_arg (void); static rtx expand_builtin_va_start (tree); @@ -113,15 +111,15 @@ static rtx expand_builtin_strspn (tree, rtx, enum machine_mode); static rtx expand_builtin_strcspn (tree, rtx, enum machine_mode); static rtx expand_builtin_memcpy (tree, rtx, enum machine_mode); static rtx expand_builtin_mempcpy (tree, tree, rtx, enum machine_mode, int); -static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode); -static rtx expand_builtin_bcopy (tree, tree); -static rtx expand_builtin_strcpy (tree, rtx, enum machine_mode); +static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode, tree); +static rtx expand_builtin_bcopy (tree); +static rtx expand_builtin_strcpy (tree, tree, rtx, enum machine_mode); static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode); static rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode); static rtx expand_builtin_strncpy (tree, rtx, enum machine_mode); static rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode); static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode); -static rtx expand_builtin_memset (tree, rtx, enum machine_mode); +static rtx expand_builtin_memset (tree, rtx, enum machine_mode, tree); static rtx expand_builtin_bzero (tree); static rtx expand_builtin_strlen (tree, rtx, enum machine_mode); static rtx expand_builtin_strstr (tree, tree, rtx, enum machine_mode); @@ -144,7 +142,7 @@ static tree fold_builtin_inf (tree, int); static tree fold_builtin_nan (tree, tree, int); static int validate_arglist (tree, ...); static bool integer_valued_real_p (tree); -static tree fold_trunc_transparent_mathfn (tree); +static tree fold_trunc_transparent_mathfn (tree, tree); static bool readonly_data_expr (tree); static rtx expand_builtin_fabs (tree, rtx, rtx); static rtx expand_builtin_signbit (tree, rtx); @@ -157,19 +155,20 @@ static tree fold_builtin_sin (tree); static tree fold_builtin_cos (tree, tree, tree); static tree fold_builtin_tan (tree); static tree fold_builtin_atan (tree, tree); -static tree fold_builtin_trunc (tree); -static tree fold_builtin_floor (tree); -static tree fold_builtin_ceil (tree); -static tree fold_builtin_round (tree); -static tree fold_builtin_bitop (tree); -static tree fold_builtin_memcpy (tree); +static tree fold_builtin_trunc (tree, tree); +static tree fold_builtin_floor (tree, tree); +static tree fold_builtin_ceil (tree, tree); +static tree fold_builtin_round (tree, tree); +static tree fold_builtin_int_roundingfn (tree, tree); +static tree fold_builtin_bitop (tree, tree); +static tree fold_builtin_memcpy (tree, tree); static tree fold_builtin_mempcpy (tree, tree, int); static tree fold_builtin_memmove (tree, tree); static tree fold_builtin_strchr (tree, tree); static tree fold_builtin_memcmp (tree); static tree fold_builtin_strcmp (tree); static tree fold_builtin_strncmp (tree); -static tree fold_builtin_signbit (tree); +static tree fold_builtin_signbit (tree, tree); static tree fold_builtin_copysign (tree, tree, tree); static tree fold_builtin_isascii (tree); static tree fold_builtin_toascii (tree); @@ -178,7 +177,7 @@ static tree fold_builtin_fabs (tree, tree); static tree fold_builtin_abs (tree, tree); static tree fold_builtin_unordered_cmp (tree, tree, enum tree_code, enum tree_code); -static tree fold_builtin_1 (tree, bool); +static tree fold_builtin_1 (tree, tree, bool); static tree fold_builtin_strpbrk (tree, tree); static tree fold_builtin_strstr (tree, tree); @@ -189,6 +188,40 @@ static tree fold_builtin_strspn (tree); static tree fold_builtin_strcspn (tree); static tree fold_builtin_sprintf (tree, int); +static rtx expand_builtin_object_size (tree); +static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode, + enum built_in_function); +static void maybe_emit_chk_warning (tree, enum built_in_function); +static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function); +static tree fold_builtin_object_size (tree); +static tree fold_builtin_strcat_chk (tree, tree); +static tree fold_builtin_strncat_chk (tree, tree); +static tree fold_builtin_sprintf_chk (tree, enum built_in_function); +static tree fold_builtin_printf (tree, tree, bool, enum built_in_function); +static tree fold_builtin_fprintf (tree, tree, bool, enum built_in_function); +static bool init_target_chars (void); + +static unsigned HOST_WIDE_INT target_newline; +static unsigned HOST_WIDE_INT target_percent; +static unsigned HOST_WIDE_INT target_c; +static unsigned HOST_WIDE_INT target_s; +static char target_percent_c[3]; +static char target_percent_s[3]; +static char target_percent_s_newline[4]; + +/* Return true if NODE should be considered for inline expansion regardless + of the optimization level. This means whenever a function is invoked with + its "internal" name, which normally contains the prefix "__builtin". */ + +static bool called_as_built_in (tree node) +{ + const char *name = IDENTIFIER_POINTER (DECL_NAME (node)); + if (strncmp (name, "__builtin_", 10) == 0) + return true; + if (strncmp (name, "__sync_", 7) == 0) + return true; + return false; +} /* Return the alignment in bits of EXP, a pointer valued expression. But don't return more than MAX_ALIGN no matter what. @@ -339,7 +372,7 @@ c_strlen (tree src, int only_value) runtime. */ if (offset < 0 || offset > max) { - warning ("offset outside bounds of constant string"); + warning (0, "offset outside bounds of constant string"); return 0; } @@ -407,7 +440,7 @@ c_readstr (const char *str, enum machine_mode mode) } /* Cast a target constant CST to target CHAR and if that value fits into - host char type, return zero and put that value into variable pointed by + host char type, return zero and put that value into variable pointed to by P. */ static int @@ -461,7 +494,22 @@ expand_builtin_return_addr (enum built_in_function fndecl_code, int count) #ifdef INITIAL_FRAME_ADDRESS_RTX rtx tem = INITIAL_FRAME_ADDRESS_RTX; #else - rtx tem = hard_frame_pointer_rtx; + rtx tem; + + /* For a zero count, we don't care what frame address we return, so frame + pointer elimination is OK, and using the soft frame pointer is OK. + For a nonzero count, we require a stable offset from the current frame + pointer to the previous one, so we must use the hard frame pointer, and + we must disable frame pointer elimination. */ + if (count == 0) + tem = frame_pointer_rtx; + else + { + tem = hard_frame_pointer_rtx; + + /* Tell reload not to eliminate the frame pointer. */ + current_function_accesses_prior_frames = 1; + } #endif /* Some machines need special handling before we can access @@ -490,8 +538,7 @@ expand_builtin_return_addr (enum built_in_function fndecl_code, int count) tem = DYNAMIC_CHAIN_ADDRESS (tem); #endif tem = memory_address (Pmode, tem); - tem = gen_rtx_MEM (Pmode, tem); - set_mem_alias_set (tem, get_frame_alias_set ()); + tem = gen_frame_mem (Pmode, tem); tem = copy_to_reg (tem); } @@ -506,8 +553,7 @@ expand_builtin_return_addr (enum built_in_function fndecl_code, int count) #else tem = memory_address (Pmode, plus_constant (tem, GET_MODE_SIZE (Pmode))); - tem = gen_rtx_MEM (Pmode, tem); - set_mem_alias_set (tem, get_frame_alias_set ()); + tem = gen_frame_mem (Pmode, tem); #endif return tem; } @@ -654,7 +700,7 @@ expand_builtin_setjmp (tree arglist, rtx target) || REGNO (target) < FIRST_PSEUDO_REGISTER) target = gen_reg_rtx (TYPE_MODE (integer_type_node)); - buf_addr = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); + buf_addr = expand_normal (TREE_VALUE (arglist)); next_lab = gen_label_rtx (); cont_lab = gen_label_rtx (); @@ -796,9 +842,9 @@ expand_builtin_nonlocal_goto (tree arglist) arglist = TREE_CHAIN (arglist); t_save_area = TREE_VALUE (arglist); - r_label = expand_expr (t_label, NULL_RTX, VOIDmode, 0); + r_label = expand_normal (t_label); r_label = convert_memory_address (Pmode, r_label); - r_save_area = expand_expr (t_save_area, NULL_RTX, VOIDmode, 0); + r_save_area = expand_normal (t_save_area); r_save_area = convert_memory_address (Pmode, r_save_area); r_fp = gen_rtx_MEM (Pmode, r_save_area); r_sp = gen_rtx_MEM (STACK_SAVEAREA_MODE (SAVE_NONLOCAL), @@ -929,11 +975,11 @@ expand_builtin_prefetch (tree arglist) error ("second argument to %<__builtin_prefetch%> must be a constant"); arg1 = integer_zero_node; } - op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); + op1 = expand_normal (arg1); /* Argument 1 must be either zero or one. */ if (INTVAL (op1) != 0 && INTVAL (op1) != 1) { - warning ("invalid second argument to %<__builtin_prefetch%>;" + warning (0, "invalid second argument to %<__builtin_prefetch%>;" " using zero"); op1 = const0_rtx; } @@ -944,11 +990,11 @@ expand_builtin_prefetch (tree arglist) error ("third argument to %<__builtin_prefetch%> must be a constant"); arg2 = integer_zero_node; } - op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); + op2 = expand_normal (arg2); /* Argument 2 must be 0, 1, 2, or 3. */ if (INTVAL (op2) < 0 || INTVAL (op2) > 3) { - warning ("invalid third argument to %<__builtin_prefetch%>; using zero"); + warning (0, "invalid third argument to %<__builtin_prefetch%>; using zero"); op2 = const0_rtx; } @@ -974,17 +1020,15 @@ expand_builtin_prefetch (tree arglist) } /* Get a MEM rtx for expression EXP which is the address of an operand - to be used to be used in a string instruction (cmpstrsi, movmemsi, ..). */ + to be used in a string instruction (cmpstrsi, movmemsi, ..). LEN is + the maximum length of the block of memory that might be accessed or + NULL if unknown. */ static rtx -get_memory_rtx (tree exp) +get_memory_rtx (tree exp, tree len) { - rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_SUM); - rtx mem; - - addr = convert_memory_address (Pmode, addr); - - mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr)); + rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL); + rtx mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr)); /* Get an expression we can use to find the attributes to assign to MEM. If it is an ADDR_EXPR, use the operand. Otherwise, dereference it if @@ -1007,6 +1051,80 @@ get_memory_rtx (tree exp) if (exp) { set_mem_attributes (mem, exp, 0); + + /* Allow the string and memory builtins to overflow from one + field into another, see http://gcc.gnu.org/PR23561. + Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole + memory accessed by the string or memory builtin will fit + within the field. */ + if (MEM_EXPR (mem) && TREE_CODE (MEM_EXPR (mem)) == COMPONENT_REF) + { + tree mem_expr = MEM_EXPR (mem); + HOST_WIDE_INT offset = -1, length = -1; + tree inner = exp; + + while (TREE_CODE (inner) == ARRAY_REF + || TREE_CODE (inner) == NOP_EXPR + || TREE_CODE (inner) == CONVERT_EXPR + || TREE_CODE (inner) == NON_LVALUE_EXPR + || TREE_CODE (inner) == VIEW_CONVERT_EXPR + || TREE_CODE (inner) == SAVE_EXPR) + inner = TREE_OPERAND (inner, 0); + + gcc_assert (TREE_CODE (inner) == COMPONENT_REF); + + if (MEM_OFFSET (mem) + && GET_CODE (MEM_OFFSET (mem)) == CONST_INT) + offset = INTVAL (MEM_OFFSET (mem)); + + if (offset >= 0 && len && host_integerp (len, 0)) + length = tree_low_cst (len, 0); + + while (TREE_CODE (inner) == COMPONENT_REF) + { + tree field = TREE_OPERAND (inner, 1); + gcc_assert (! DECL_BIT_FIELD (field)); + gcc_assert (TREE_CODE (mem_expr) == COMPONENT_REF); + gcc_assert (field == TREE_OPERAND (mem_expr, 1)); + + if (length >= 0 + && TYPE_SIZE_UNIT (TREE_TYPE (inner)) + && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0)) + { + HOST_WIDE_INT size + = tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0); + /* If we can prove the memory starting at XEXP (mem, 0) + and ending at XEXP (mem, 0) + LENGTH will fit into + this field, we can keep that COMPONENT_REF in MEM_EXPR. */ + if (offset <= size + && length <= size + && offset + length <= size) + break; + } + + if (offset >= 0 + && host_integerp (DECL_FIELD_OFFSET (field), 0)) + offset += tree_low_cst (DECL_FIELD_OFFSET (field), 0) + + tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1) + / BITS_PER_UNIT; + else + { + offset = -1; + length = -1; + } + + mem_expr = TREE_OPERAND (mem_expr, 0); + inner = TREE_OPERAND (inner, 0); + } + + if (mem_expr == NULL) + offset = -1; + if (mem_expr != MEM_EXPR (mem)) + { + set_mem_expr (mem, mem_expr); + set_mem_offset (mem, offset >= 0 ? GEN_INT (offset) : NULL_RTX); + } + } set_mem_alias_set (mem, 0); set_mem_size (mem, NULL_RTX); } @@ -1467,7 +1585,6 @@ type_to_class (tree type) { case VOID_TYPE: return void_type_class; case INTEGER_TYPE: return integer_type_class; - case CHAR_TYPE: return char_type_class; case ENUMERAL_TYPE: return enumeral_type_class; case BOOLEAN_TYPE: return boolean_type_class; case POINTER_TYPE: return pointer_type_class; @@ -1482,7 +1599,6 @@ type_to_class (tree type) case QUAL_UNION_TYPE: return union_type_class; case ARRAY_TYPE: return (TYPE_STRING_FLAG (type) ? string_type_class : array_type_class); - case FILE_TYPE: return file_type_class; case LANG_TYPE: return lang_type_class; default: return no_type_class; } @@ -1552,8 +1668,12 @@ mathfn_built_in (tree type, enum built_in_function fn) CASE_MATHFN (BUILT_IN_J0) CASE_MATHFN (BUILT_IN_J1) CASE_MATHFN (BUILT_IN_JN) + CASE_MATHFN (BUILT_IN_LCEIL) CASE_MATHFN (BUILT_IN_LDEXP) + CASE_MATHFN (BUILT_IN_LFLOOR) CASE_MATHFN (BUILT_IN_LGAMMA) + CASE_MATHFN (BUILT_IN_LLCEIL) + CASE_MATHFN (BUILT_IN_LLFLOOR) CASE_MATHFN (BUILT_IN_LLRINT) CASE_MATHFN (BUILT_IN_LLROUND) CASE_MATHFN (BUILT_IN_LOG) @@ -1669,95 +1789,54 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) switch (DECL_FUNCTION_CODE (fndecl)) { - case BUILT_IN_SQRT: - case BUILT_IN_SQRTF: - case BUILT_IN_SQRTL: + CASE_FLT_FN (BUILT_IN_SQRT): errno_set = ! tree_expr_nonnegative_p (arg); builtin_optab = sqrt_optab; break; - case BUILT_IN_EXP: - case BUILT_IN_EXPF: - case BUILT_IN_EXPL: + CASE_FLT_FN (BUILT_IN_EXP): errno_set = true; builtin_optab = exp_optab; break; - case BUILT_IN_EXP10: - case BUILT_IN_EXP10F: - case BUILT_IN_EXP10L: - case BUILT_IN_POW10: - case BUILT_IN_POW10F: - case BUILT_IN_POW10L: + CASE_FLT_FN (BUILT_IN_EXP10): + CASE_FLT_FN (BUILT_IN_POW10): errno_set = true; builtin_optab = exp10_optab; break; - case BUILT_IN_EXP2: - case BUILT_IN_EXP2F: - case BUILT_IN_EXP2L: + CASE_FLT_FN (BUILT_IN_EXP2): errno_set = true; builtin_optab = exp2_optab; break; - case BUILT_IN_EXPM1: - case BUILT_IN_EXPM1F: - case BUILT_IN_EXPM1L: + CASE_FLT_FN (BUILT_IN_EXPM1): errno_set = true; builtin_optab = expm1_optab; break; - case BUILT_IN_LOGB: - case BUILT_IN_LOGBF: - case BUILT_IN_LOGBL: + CASE_FLT_FN (BUILT_IN_LOGB): errno_set = true; builtin_optab = logb_optab; break; - case BUILT_IN_ILOGB: - case BUILT_IN_ILOGBF: - case BUILT_IN_ILOGBL: + CASE_FLT_FN (BUILT_IN_ILOGB): errno_set = true; builtin_optab = ilogb_optab; break; - case BUILT_IN_LOG: - case BUILT_IN_LOGF: - case BUILT_IN_LOGL: + CASE_FLT_FN (BUILT_IN_LOG): errno_set = true; builtin_optab = log_optab; break; - case BUILT_IN_LOG10: - case BUILT_IN_LOG10F: - case BUILT_IN_LOG10L: + CASE_FLT_FN (BUILT_IN_LOG10): errno_set = true; builtin_optab = log10_optab; break; - case BUILT_IN_LOG2: - case BUILT_IN_LOG2F: - case BUILT_IN_LOG2L: + CASE_FLT_FN (BUILT_IN_LOG2): errno_set = true; builtin_optab = log2_optab; break; - case BUILT_IN_LOG1P: - case BUILT_IN_LOG1PF: - case BUILT_IN_LOG1PL: + CASE_FLT_FN (BUILT_IN_LOG1P): errno_set = true; builtin_optab = log1p_optab; break; - case BUILT_IN_ASIN: - case BUILT_IN_ASINF: - case BUILT_IN_ASINL: + CASE_FLT_FN (BUILT_IN_ASIN): builtin_optab = asin_optab; break; - case BUILT_IN_ACOS: - case BUILT_IN_ACOSF: - case BUILT_IN_ACOSL: + CASE_FLT_FN (BUILT_IN_ACOS): builtin_optab = acos_optab; break; - case BUILT_IN_TAN: - case BUILT_IN_TANF: - case BUILT_IN_TANL: + CASE_FLT_FN (BUILT_IN_TAN): builtin_optab = tan_optab; break; - case BUILT_IN_ATAN: - case BUILT_IN_ATANF: - case BUILT_IN_ATANL: + CASE_FLT_FN (BUILT_IN_ATAN): builtin_optab = atan_optab; break; - case BUILT_IN_FLOOR: - case BUILT_IN_FLOORF: - case BUILT_IN_FLOORL: + CASE_FLT_FN (BUILT_IN_FLOOR): builtin_optab = floor_optab; break; - case BUILT_IN_CEIL: - case BUILT_IN_CEILF: - case BUILT_IN_CEILL: + CASE_FLT_FN (BUILT_IN_CEIL): builtin_optab = ceil_optab; break; - case BUILT_IN_TRUNC: - case BUILT_IN_TRUNCF: - case BUILT_IN_TRUNCL: + CASE_FLT_FN (BUILT_IN_TRUNC): builtin_optab = btrunc_optab; break; - case BUILT_IN_ROUND: - case BUILT_IN_ROUNDF: - case BUILT_IN_ROUNDL: + CASE_FLT_FN (BUILT_IN_ROUND): builtin_optab = round_optab; break; - case BUILT_IN_NEARBYINT: - case BUILT_IN_NEARBYINTF: - case BUILT_IN_NEARBYINTL: + CASE_FLT_FN (BUILT_IN_NEARBYINT): builtin_optab = nearbyint_optab; break; - case BUILT_IN_RINT: - case BUILT_IN_RINTF: - case BUILT_IN_RINTL: + CASE_FLT_FN (BUILT_IN_RINT): builtin_optab = rint_optab; break; + CASE_FLT_FN (BUILT_IN_LRINT): + CASE_FLT_FN (BUILT_IN_LLRINT): + builtin_optab = lrint_optab; break; default: gcc_unreachable (); } @@ -1889,25 +1968,15 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) switch (DECL_FUNCTION_CODE (fndecl)) { - case BUILT_IN_POW: - case BUILT_IN_POWF: - case BUILT_IN_POWL: + CASE_FLT_FN (BUILT_IN_POW): builtin_optab = pow_optab; break; - case BUILT_IN_ATAN2: - case BUILT_IN_ATAN2F: - case BUILT_IN_ATAN2L: + CASE_FLT_FN (BUILT_IN_ATAN2): builtin_optab = atan2_optab; break; - case BUILT_IN_LDEXP: - case BUILT_IN_LDEXPF: - case BUILT_IN_LDEXPL: + CASE_FLT_FN (BUILT_IN_LDEXP): builtin_optab = ldexp_optab; break; - case BUILT_IN_FMOD: - case BUILT_IN_FMODF: - case BUILT_IN_FMODL: + CASE_FLT_FN (BUILT_IN_FMOD): builtin_optab = fmod_optab; break; - case BUILT_IN_DREM: - case BUILT_IN_DREMF: - case BUILT_IN_DREML: + CASE_FLT_FN (BUILT_IN_DREM): builtin_optab = drem_optab; break; default: gcc_unreachable (); @@ -1949,8 +2018,8 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) if (! stable) exp = build_function_call_expr (fndecl, arglist); - op0 = expand_expr (arg0, subtarget, VOIDmode, 0); - op1 = expand_expr (arg1, 0, VOIDmode, 0); + op0 = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL); + op1 = expand_normal (arg1); start_sequence (); @@ -1990,11 +2059,10 @@ static rtx expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) { optab builtin_optab; - rtx op0, insns, before_call; + rtx op0, insns; tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); enum machine_mode mode; - bool errno_set = false; tree arg, narg; if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) @@ -2004,12 +2072,8 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) switch (DECL_FUNCTION_CODE (fndecl)) { - case BUILT_IN_SIN: - case BUILT_IN_SINF: - case BUILT_IN_SINL: - case BUILT_IN_COS: - case BUILT_IN_COSF: - case BUILT_IN_COSL: + CASE_FLT_FN (BUILT_IN_SIN): + CASE_FLT_FN (BUILT_IN_COS): builtin_optab = sincos_optab; break; default: gcc_unreachable (); @@ -2018,21 +2082,14 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) /* Make a suitable register to place result in. */ mode = TYPE_MODE (TREE_TYPE (exp)); - if (! flag_errno_math || ! HONOR_NANS (mode)) - errno_set = false; - /* Check if sincos insn is available, otherwise fallback to sin or cos insn. */ if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) { switch (DECL_FUNCTION_CODE (fndecl)) { - case BUILT_IN_SIN: - case BUILT_IN_SINF: - case BUILT_IN_SINL: + CASE_FLT_FN (BUILT_IN_SIN): builtin_optab = sin_optab; break; - case BUILT_IN_COS: - case BUILT_IN_COSF: - case BUILT_IN_COSL: + CASE_FLT_FN (BUILT_IN_COS): builtin_optab = cos_optab; break; default: gcc_unreachable (); @@ -2067,14 +2124,10 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) switch (DECL_FUNCTION_CODE (fndecl)) { - case BUILT_IN_SIN: - case BUILT_IN_SINF: - case BUILT_IN_SINL: + CASE_FLT_FN (BUILT_IN_SIN): result = expand_twoval_unop (builtin_optab, op0, 0, target, 0); break; - case BUILT_IN_COS: - case BUILT_IN_COSF: - case BUILT_IN_COSL: + CASE_FLT_FN (BUILT_IN_COS): result = expand_twoval_unop (builtin_optab, op0, target, 0, 0); break; default: @@ -2089,9 +2142,6 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) if (target != 0) { - if (errno_set) - expand_errno_check (exp, target); - /* Output the entire sequence. */ insns = get_insns (); end_sequence (); @@ -2105,13 +2155,159 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) end_sequence (); } - before_call = get_last_insn (); - target = expand_call (exp, target, target == const0_rtx); return target; } +/* Expand a call to the builtin sincos math function. + Return 0 if a normal call should be emitted rather than expanding the + function in-line. EXP is the expression that is a call to the builtin + function. */ + +static rtx +expand_builtin_sincos (tree exp) +{ + rtx op0, op1, op2, target1, target2; + tree arglist = TREE_OPERAND (exp, 1); + enum machine_mode mode; + tree arg, sinp, cosp; + int result; + + if (!validate_arglist (arglist, REAL_TYPE, + POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + + arg = TREE_VALUE (arglist); + sinp = TREE_VALUE (TREE_CHAIN (arglist)); + cosp = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* Make a suitable register to place result in. */ + mode = TYPE_MODE (TREE_TYPE (arg)); + + /* Check if sincos insn is available, otherwise emit the call. */ + if (sincos_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + return NULL_RTX; + + target1 = gen_reg_rtx (mode); + target2 = gen_reg_rtx (mode); + + op0 = expand_normal (arg); + op1 = expand_normal (build_fold_indirect_ref (sinp)); + op2 = expand_normal (build_fold_indirect_ref (cosp)); + + /* Compute into target1 and target2. + Set TARGET to wherever the result comes back. */ + result = expand_twoval_unop (sincos_optab, op0, target2, target1, 0); + gcc_assert (result); + + /* Move target1 and target2 to the memory locations indicated + by op1 and op2. */ + emit_move_insn (op1, target1); + emit_move_insn (op2, target2); + + return const0_rtx; +} + +/* Expand a call to one of the builtin rounding functions (lfloor). + If expanding via optab fails, lower expression to (int)(floor(x)). + EXP is the expression that is a call to the builtin function; + if convenient, the result should be placed in TARGET. SUBTARGET may + be used as the target for computing one of EXP's operands. */ + +static rtx +expand_builtin_int_roundingfn (tree exp, rtx target, rtx subtarget) +{ + optab builtin_optab; + rtx op0, insns, tmp; + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + enum built_in_function fallback_fn; + tree fallback_fndecl; + enum machine_mode mode; + tree arg, narg; + + if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + gcc_unreachable (); + + arg = TREE_VALUE (arglist); + + switch (DECL_FUNCTION_CODE (fndecl)) + { + CASE_FLT_FN (BUILT_IN_LCEIL): + CASE_FLT_FN (BUILT_IN_LLCEIL): + builtin_optab = lceil_optab; + fallback_fn = BUILT_IN_CEIL; + break; + + CASE_FLT_FN (BUILT_IN_LFLOOR): + CASE_FLT_FN (BUILT_IN_LLFLOOR): + builtin_optab = lfloor_optab; + fallback_fn = BUILT_IN_FLOOR; + break; + + default: + gcc_unreachable (); + } + + /* Make a suitable register to place result in. */ + mode = TYPE_MODE (TREE_TYPE (exp)); + + /* Before working hard, check whether the instruction is available. */ + if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + target = gen_reg_rtx (mode); + + /* Wrap the computation of the argument in a SAVE_EXPR, as we may + need to expand the argument again. This way, we will not perform + side-effects more the once. */ + narg = builtin_save_expr (arg); + if (narg != arg) + { + arg = narg; + arglist = build_tree_list (NULL_TREE, arg); + exp = build_function_call_expr (fndecl, arglist); + } + + op0 = expand_expr (arg, subtarget, VOIDmode, 0); + + start_sequence (); + + /* Compute into TARGET. + Set TARGET to wherever the result comes back. */ + target = expand_unop (mode, builtin_optab, op0, target, 0); + + if (target != 0) + { + /* Output the entire sequence. */ + insns = get_insns (); + end_sequence (); + emit_insn (insns); + return target; + } + + /* If we were unable to expand via the builtin, stop the sequence + (without outputting the insns). */ + end_sequence (); + } + + /* Fall back to floating point rounding optab. */ + fallback_fndecl = mathfn_built_in (TREE_TYPE (arg), fallback_fn); + /* We shouldn't get here on targets without TARGET_C99_FUNCTIONS. + ??? Perhaps convert (int)floorf(x) into (int)floor((double)x). */ + gcc_assert (fallback_fndecl != NULL_TREE); + exp = build_function_call_expr (fallback_fndecl, arglist); + + tmp = expand_builtin_mathfn (exp, NULL_RTX, NULL_RTX); + + /* Truncate the result of floating point optab to integer + via expand_fix (). */ + target = gen_reg_rtx (mode); + expand_fix (target, tmp, 0); + + return target; +} + /* To evaluate powi(x,n), the floating point value x raised to the constant integer exponent n, we use a hybrid algorithm that combines the "window method" with look-up tables. For an @@ -2379,6 +2575,7 @@ expand_builtin_powi (tree exp, rtx target, rtx subtarget) tree arg0, arg1; rtx op0, op1; enum machine_mode mode; + enum machine_mode mode2; if (! validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; @@ -2410,19 +2607,22 @@ expand_builtin_powi (tree exp, rtx target, rtx subtarget) /* Emit a libcall to libgcc. */ + /* Mode of the 2nd argument must match that of an int. */ + mode2 = mode_for_size (INT_TYPE_SIZE, MODE_INT, 0); + if (target == NULL_RTX) target = gen_reg_rtx (mode); op0 = expand_expr (arg0, subtarget, mode, 0); if (GET_MODE (op0) != mode) op0 = convert_to_mode (mode, op0, 0); - op1 = expand_expr (arg1, 0, word_mode, 0); - if (GET_MODE (op1) != word_mode) - op1 = convert_to_mode (word_mode, op1, 0); + op1 = expand_expr (arg1, 0, mode2, 0); + if (GET_MODE (op1) != mode2) + op1 = convert_to_mode (mode2, op1, 0); target = emit_library_call_value (powi_optab->handlers[(int) mode].libfunc, target, LCT_CONST_MAKE_BLOCK, mode, 2, - op0, mode, op1, word_mode); + op0, mode, op1, mode2); return target; } @@ -2625,6 +2825,7 @@ builtin_memcpy_read_str (void *data, HOST_WIDE_INT offset, static rtx expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode) { + tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -2639,7 +2840,7 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode) unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); rtx dest_mem, src_mem, dest_addr, len_rtx; - tree result = fold_builtin_memcpy (exp); + tree result = fold_builtin_memcpy (fndecl, arglist); if (result) return expand_expr (result, target, mode, EXPAND_NORMAL); @@ -2653,9 +2854,9 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode) if (src_align == 0) return 0; - dest_mem = get_memory_rtx (dest); + dest_mem = get_memory_rtx (dest, len); set_mem_align (dest_mem, dest_align); - len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); + len_rtx = expand_normal (len); src_str = c_getstr (src); /* If SRC is a string constant and block move would be done @@ -2675,12 +2876,13 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode) return dest_mem; } - src_mem = get_memory_rtx (src); + src_mem = get_memory_rtx (src, len); set_mem_align (src_mem, src_align); /* Copy word part most expediently. */ dest_addr = emit_block_move (dest_mem, src_mem, len_rtx, - BLOCK_OP_NORMAL); + CALL_EXPR_TAILCALL (exp) + ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL); if (dest_addr == 0) { @@ -2692,7 +2894,7 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode) } /* Expand a call to the mempcpy builtin, with arguments in ARGLIST. - Return 0 if we failed the caller should emit a normal call, + Return 0 if we failed; the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). If ENDP is 0 return the destination pointer, if ENDP is 1 return the end pointer ala @@ -2741,7 +2943,7 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m if (! host_integerp (len, 1)) return 0; - len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); + len_rtx = expand_normal (len); src_str = c_getstr (src); /* If SRC is a string constant and block move would be done @@ -2753,7 +2955,7 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, (void *) src_str, dest_align)) { - dest_mem = get_memory_rtx (dest); + dest_mem = get_memory_rtx (dest, len); set_mem_align (dest_mem, dest_align); dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), builtin_memcpy_read_str, @@ -2767,9 +2969,9 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m && can_move_by_pieces (INTVAL (len_rtx), MIN (dest_align, src_align))) { - dest_mem = get_memory_rtx (dest); + dest_mem = get_memory_rtx (dest, len); set_mem_align (dest_mem, dest_align); - src_mem = get_memory_rtx (src); + src_mem = get_memory_rtx (src, len); set_mem_align (src_mem, src_align); dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx), MIN (dest_align, src_align), endp); @@ -2783,11 +2985,11 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m } /* Expand expression EXP, which is a call to the memmove builtin. Return 0 - if we failed the caller should emit a normal call. */ + if we failed; the caller should emit a normal call. */ static rtx expand_builtin_memmove (tree arglist, tree type, rtx target, - enum machine_mode mode) + enum machine_mode mode, tree orig_exp) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -2819,11 +3021,13 @@ expand_builtin_memmove (tree arglist, tree type, rtx target, normal memcpy. */ if (readonly_data_expr (src)) { - tree const fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; - return expand_expr (build_function_call_expr (fn, arglist), - target, mode, EXPAND_NORMAL); + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); } /* If length is 1 and we can expand memcpy call inline, @@ -2845,8 +3049,10 @@ expand_builtin_memmove (tree arglist, tree type, rtx target, if we failed the caller should emit a normal call. */ static rtx -expand_builtin_bcopy (tree arglist, tree type) +expand_builtin_bcopy (tree exp) { + tree arglist = TREE_OPERAND (exp, 1); + tree type = TREE_TYPE (exp); tree src, dest, size, newarglist; if (!validate_arglist (arglist, @@ -2866,7 +3072,7 @@ expand_builtin_bcopy (tree arglist, tree type) newarglist = tree_cons (NULL_TREE, src, newarglist); newarglist = tree_cons (NULL_TREE, dest, newarglist); - return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode); + return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode, exp); } #ifndef HAVE_movstr @@ -2893,8 +3099,8 @@ expand_movstr (tree dest, tree src, rtx target, int endp) if (!HAVE_movstr) return 0; - dest_mem = get_memory_rtx (dest); - src_mem = get_memory_rtx (src); + dest_mem = get_memory_rtx (dest, NULL); + src_mem = get_memory_rtx (src, NULL); if (!endp) { target = force_reg (Pmode, XEXP (dest_mem, 0)); @@ -2942,12 +3148,11 @@ expand_movstr (tree dest, tree src, rtx target, int endp) convenient). */ static rtx -expand_builtin_strcpy (tree exp, rtx target, enum machine_mode mode) +expand_builtin_strcpy (tree fndecl, tree arglist, rtx target, enum machine_mode mode) { - tree arglist = TREE_OPERAND (exp, 1); if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) { - tree result = fold_builtin_strcpy (exp, 0); + tree result = fold_builtin_strcpy (fndecl, arglist, 0); if (result) return expand_expr (result, target, mode, EXPAND_NORMAL); @@ -3009,11 +3214,12 @@ expand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode) if (TREE_CODE (len) == INTEGER_CST) { - rtx len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); + rtx len_rtx = expand_normal (len); if (GET_CODE (len_rtx) == CONST_INT) { - ret = expand_builtin_strcpy (exp, target, mode); + ret = expand_builtin_strcpy (get_callee_fndecl (exp), + arglist, target, mode); if (ret) { @@ -3064,13 +3270,14 @@ builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset, static rtx expand_builtin_strncpy (tree exp, rtx target, enum machine_mode mode) { + tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) { tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1); tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); - tree result = fold_builtin_strncpy (exp, slen); + tree result = fold_builtin_strncpy (fndecl, arglist, slen); if (result) return expand_expr (result, target, mode, EXPAND_NORMAL); @@ -3098,7 +3305,7 @@ expand_builtin_strncpy (tree exp, rtx target, enum machine_mode mode) (void *) p, dest_align)) return 0; - dest_mem = get_memory_rtx (dest); + dest_mem = get_memory_rtx (dest, len); store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_strncpy_read_str, (void *) p, dest_align, 0); @@ -3158,7 +3365,8 @@ builtin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, convenient). */ static rtx -expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode, + tree orig_exp) { if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -3187,33 +3395,34 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode) return expand_expr (dest, target, mode, EXPAND_NORMAL); } + len_rtx = expand_normal (len); + dest_mem = get_memory_rtx (dest, len); + if (TREE_CODE (val) != INTEGER_CST) { rtx val_rtx; - if (!host_integerp (len, 1)) - return 0; - - if (optimize_size && tree_low_cst (len, 1) > 1) - return 0; + val = fold_build1 (CONVERT_EXPR, unsigned_char_type_node, val); + val_rtx = expand_normal (val); /* Assume that we can memset by pieces if we can store the * the coefficients by pieces (in the required modes). * We can't pass builtin_memset_gen_str as that emits RTL. */ c = 1; - if (!can_store_by_pieces (tree_low_cst (len, 1), - builtin_memset_read_str, - &c, dest_align)) + if (host_integerp (len, 1) + && !(optimize_size && tree_low_cst (len, 1) > 1) + && can_store_by_pieces (tree_low_cst (len, 1), + builtin_memset_read_str, &c, dest_align)) + { + val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node), + val_rtx); + store_by_pieces (dest_mem, tree_low_cst (len, 1), + builtin_memset_gen_str, val_rtx, dest_align, 0); + } + else if (!set_storage_via_setmem(dest_mem, len_rtx, val_rtx, + dest_align)) return 0; - val = fold (build1 (CONVERT_EXPR, unsigned_char_type_node, val)); - val_rtx = expand_expr (val, NULL_RTX, VOIDmode, 0); - val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node), - val_rtx); - dest_mem = get_memory_rtx (dest); - store_by_pieces (dest_mem, tree_low_cst (len, 1), - builtin_memset_gen_str, - val_rtx, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; @@ -3224,27 +3433,25 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode) if (c) { - if (!host_integerp (len, 1)) - return 0; - if (!can_store_by_pieces (tree_low_cst (len, 1), - builtin_memset_read_str, &c, - dest_align)) + if (host_integerp (len, 1) + && !(optimize_size && tree_low_cst (len, 1) > 1) + && can_store_by_pieces (tree_low_cst (len, 1), + builtin_memset_read_str, &c, dest_align)) + store_by_pieces (dest_mem, tree_low_cst (len, 1), + builtin_memset_read_str, &c, dest_align, 0); + else if (!set_storage_via_setmem (dest_mem, len_rtx, GEN_INT (c), + dest_align)) return 0; - dest_mem = get_memory_rtx (dest); - store_by_pieces (dest_mem, tree_low_cst (len, 1), - builtin_memset_read_str, - &c, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } - len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); - - dest_mem = get_memory_rtx (dest); set_mem_align (dest_mem, dest_align); - dest_addr = clear_storage (dest_mem, len_rtx); + dest_addr = clear_storage (dest_mem, len_rtx, + CALL_EXPR_TAILCALL (orig_exp) + ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL); if (dest_addr == 0) { @@ -3260,8 +3467,9 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode) if we failed the caller should emit a normal call. */ static rtx -expand_builtin_bzero (tree arglist) +expand_builtin_bzero (tree exp) { + tree arglist = TREE_OPERAND (exp, 1); tree dest, size, newarglist; if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -3279,7 +3487,7 @@ expand_builtin_bzero (tree arglist) newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist); newarglist = tree_cons (NULL_TREE, dest, newarglist); - return expand_builtin_memset (newarglist, const0_rtx, VOIDmode); + return expand_builtin_memset (newarglist, const0_rtx, VOIDmode, exp); } /* Expand expression EXP, which is a call to the memcmp built-in function. @@ -3301,7 +3509,7 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, return expand_expr (result, target, mode, EXPAND_NORMAL); } -#if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi +#if defined HAVE_cmpmemsi || defined HAVE_cmpstrnsi { tree arg1 = TREE_VALUE (arglist); tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); @@ -3321,9 +3529,9 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode; else #endif -#ifdef HAVE_cmpstrsi - if (HAVE_cmpstrsi) - insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; +#ifdef HAVE_cmpstrnsi + if (HAVE_cmpstrnsi) + insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; else #endif return 0; @@ -3339,9 +3547,9 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); - arg1_rtx = get_memory_rtx (arg1); - arg2_rtx = get_memory_rtx (arg2); - arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); + arg1_rtx = get_memory_rtx (arg1, len); + arg2_rtx = get_memory_rtx (arg2, len); + arg3_rtx = expand_normal (len); /* Set MEM_SIZE as appropriate. */ if (GET_CODE (arg3_rtx) == CONST_INT) @@ -3356,10 +3564,10 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, GEN_INT (MIN (arg1_align, arg2_align))); else #endif -#ifdef HAVE_cmpstrsi - if (HAVE_cmpstrsi) - insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, - GEN_INT (MIN (arg1_align, arg2_align))); +#ifdef HAVE_cmpstrnsi + if (HAVE_cmpstrnsi) + insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx, + GEN_INT (MIN (arg1_align, arg2_align))); else #endif gcc_unreachable (); @@ -3410,101 +3618,134 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode) return expand_expr (result, target, mode, EXPAND_NORMAL); } -#ifdef HAVE_cmpstrsi - if (HAVE_cmpstrsi) - { - tree arg1 = TREE_VALUE (arglist); - tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); - tree len, len1, len2; - rtx arg1_rtx, arg2_rtx, arg3_rtx; - rtx result, insn; - tree fndecl; - - int arg1_align - = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - int arg2_align - = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - enum machine_mode insn_mode - = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; - - len1 = c_strlen (arg1, 1); - len2 = c_strlen (arg2, 1); +#if defined HAVE_cmpstrsi || defined HAVE_cmpstrnsi + if (cmpstr_optab[SImode] != CODE_FOR_nothing + || cmpstrn_optab[SImode] != CODE_FOR_nothing) + { + rtx arg1_rtx, arg2_rtx; + rtx result, insn = NULL_RTX; + tree fndecl, fn; + + tree arg1 = TREE_VALUE (arglist); + tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + int arg1_align + = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + int arg2_align + = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + + /* If we don't have POINTER_TYPE, call the function. */ + if (arg1_align == 0 || arg2_align == 0) + return 0; - if (len1) - len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); - if (len2) - len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); + /* Stabilize the arguments in case gen_cmpstr(n)si fail. */ + arg1 = builtin_save_expr (arg1); + arg2 = builtin_save_expr (arg2); - /* If we don't have a constant length for the first, use the length - of the second, if we know it. We don't require a constant for - this case; some cost analysis could be done if both are available - but neither is constant. For now, assume they're equally cheap, - unless one has side effects. If both strings have constant lengths, - use the smaller. */ + arg1_rtx = get_memory_rtx (arg1, NULL); + arg2_rtx = get_memory_rtx (arg2, NULL); - if (!len1) - len = len2; - else if (!len2) - len = len1; - else if (TREE_SIDE_EFFECTS (len1)) - len = len2; - else if (TREE_SIDE_EFFECTS (len2)) - len = len1; - else if (TREE_CODE (len1) != INTEGER_CST) - len = len2; - else if (TREE_CODE (len2) != INTEGER_CST) - len = len1; - else if (tree_int_cst_lt (len1, len2)) - len = len1; - else - len = len2; +#ifdef HAVE_cmpstrsi + /* Try to call cmpstrsi. */ + if (HAVE_cmpstrsi) + { + enum machine_mode insn_mode + = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; + + /* Make a place to write the result of the instruction. */ + result = target; + if (! (result != 0 + && REG_P (result) && GET_MODE (result) == insn_mode + && REGNO (result) >= FIRST_PSEUDO_REGISTER)) + result = gen_reg_rtx (insn_mode); + + insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, + GEN_INT (MIN (arg1_align, arg2_align))); + } +#endif +#if HAVE_cmpstrnsi + /* Try to determine at least one length and call cmpstrnsi. */ + if (!insn && HAVE_cmpstrnsi) + { + tree len; + rtx arg3_rtx; + + enum machine_mode insn_mode + = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; + tree len1 = c_strlen (arg1, 1); + tree len2 = c_strlen (arg2, 1); + + if (len1) + len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); + if (len2) + len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); + + /* If we don't have a constant length for the first, use the length + of the second, if we know it. We don't require a constant for + this case; some cost analysis could be done if both are available + but neither is constant. For now, assume they're equally cheap, + unless one has side effects. If both strings have constant lengths, + use the smaller. */ + + if (!len1) + len = len2; + else if (!len2) + len = len1; + else if (TREE_SIDE_EFFECTS (len1)) + len = len2; + else if (TREE_SIDE_EFFECTS (len2)) + len = len1; + else if (TREE_CODE (len1) != INTEGER_CST) + len = len2; + else if (TREE_CODE (len2) != INTEGER_CST) + len = len1; + else if (tree_int_cst_lt (len1, len2)) + len = len1; + else + len = len2; - /* If both arguments have side effects, we cannot optimize. */ - if (!len || TREE_SIDE_EFFECTS (len)) - return 0; + /* If both arguments have side effects, we cannot optimize. */ + if (!len || TREE_SIDE_EFFECTS (len)) + return 0; - /* If we don't have POINTER_TYPE, call the function. */ - if (arg1_align == 0 || arg2_align == 0) - return 0; + /* Stabilize the arguments in case gen_cmpstrnsi fails. */ + arg3_rtx = expand_normal (len); - /* Make a place to write the result of the instruction. */ - result = target; - if (! (result != 0 - && REG_P (result) && GET_MODE (result) == insn_mode - && REGNO (result) >= FIRST_PSEUDO_REGISTER)) - result = gen_reg_rtx (insn_mode); + /* Make a place to write the result of the instruction. */ + result = target; + if (! (result != 0 + && REG_P (result) && GET_MODE (result) == insn_mode + && REGNO (result) >= FIRST_PSEUDO_REGISTER)) + result = gen_reg_rtx (insn_mode); - /* Stabilize the arguments in case gen_cmpstrsi fails. */ - arg1 = builtin_save_expr (arg1); - arg2 = builtin_save_expr (arg2); + insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx, + GEN_INT (MIN (arg1_align, arg2_align))); + } +#endif - arg1_rtx = get_memory_rtx (arg1); - arg2_rtx = get_memory_rtx (arg2); - arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); - insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, - GEN_INT (MIN (arg1_align, arg2_align))); - if (insn) - { - emit_insn (insn); + if (insn) + { + emit_insn (insn); - /* Return the value in the proper mode for this function. */ - mode = TYPE_MODE (TREE_TYPE (exp)); - if (GET_MODE (result) == mode) - return result; - if (target == 0) - return convert_to_mode (mode, result, 0); - convert_move (target, result, 0); - return target; - } + /* Return the value in the proper mode for this function. */ + mode = TYPE_MODE (TREE_TYPE (exp)); + if (GET_MODE (result) == mode) + return result; + if (target == 0) + return convert_to_mode (mode, result, 0); + convert_move (target, result, 0); + return target; + } - /* Expand the library call ourselves using a stabilized argument - list to avoid re-evaluating the function's arguments twice. */ - arglist = build_tree_list (NULL_TREE, arg2); - arglist = tree_cons (NULL_TREE, arg1, arglist); - fndecl = get_callee_fndecl (exp); - exp = build_function_call_expr (fndecl, arglist); - return expand_call (exp, target, target == const0_rtx); - } + /* Expand the library call ourselves using a stabilized argument + list to avoid re-evaluating the function's arguments twice. */ + arglist = build_tree_list (NULL_TREE, arg2); + arglist = tree_cons (NULL_TREE, arg1, arglist); + fndecl = get_callee_fndecl (exp); + fn = build_function_call_expr (fndecl, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_call (fn, target, target == const0_rtx); + } #endif return 0; } @@ -3529,10 +3770,10 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) } /* If c_strlen can determine an expression for one of the string - lengths, and it doesn't have side effects, then emit cmpstrsi + lengths, and it doesn't have side effects, then emit cmpstrnsi using length MIN(strlen(string)+1, arg3). */ -#ifdef HAVE_cmpstrsi - if (HAVE_cmpstrsi) +#ifdef HAVE_cmpstrnsi + if (HAVE_cmpstrnsi) { tree arg1 = TREE_VALUE (arglist); tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); @@ -3540,14 +3781,14 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) tree len, len1, len2; rtx arg1_rtx, arg2_rtx, arg3_rtx; rtx result, insn; - tree fndecl; + tree fndecl, fn; int arg1_align = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; int arg2_align = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; enum machine_mode insn_mode - = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; + = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; len1 = c_strlen (arg1, 1); len2 = c_strlen (arg2, 1); @@ -3586,8 +3827,8 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) return 0; /* The actual new length parameter is MIN(len,arg3). */ - len = fold (build2 (MIN_EXPR, TREE_TYPE (len), len, - fold_convert (TREE_TYPE (len), arg3))); + len = fold_build2 (MIN_EXPR, TREE_TYPE (len), len, + fold_convert (TREE_TYPE (len), arg3)); /* If we don't have POINTER_TYPE, call the function. */ if (arg1_align == 0 || arg2_align == 0) @@ -3600,16 +3841,16 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); - /* Stabilize the arguments in case gen_cmpstrsi fails. */ + /* Stabilize the arguments in case gen_cmpstrnsi fails. */ arg1 = builtin_save_expr (arg1); arg2 = builtin_save_expr (arg2); len = builtin_save_expr (len); - arg1_rtx = get_memory_rtx (arg1); - arg2_rtx = get_memory_rtx (arg2); - arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); - insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, - GEN_INT (MIN (arg1_align, arg2_align))); + arg1_rtx = get_memory_rtx (arg1, len); + arg2_rtx = get_memory_rtx (arg2, len); + arg3_rtx = expand_normal (len); + insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx, + GEN_INT (MIN (arg1_align, arg2_align))); if (insn) { emit_insn (insn); @@ -3630,8 +3871,10 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) arglist = tree_cons (NULL_TREE, arg2, arglist); arglist = tree_cons (NULL_TREE, arg1, arglist); fndecl = get_callee_fndecl (exp); - exp = build_function_call_expr (fndecl, arglist); - return expand_call (exp, target, target == const0_rtx); + fn = build_function_call_expr (fndecl, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_call (fn, target, target == const0_rtx); } #endif return 0; @@ -3642,61 +3885,61 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) otherwise try to get the result in TARGET, if convenient. */ static rtx -expand_builtin_strcat (tree arglist, tree type, rtx target, enum machine_mode mode) +expand_builtin_strcat (tree fndecl, tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; else { tree dst = TREE_VALUE (arglist), - src = TREE_VALUE (TREE_CHAIN (arglist)); + src = TREE_VALUE (TREE_CHAIN (arglist)); const char *p = c_getstr (src); - if (p) - { - /* If the string length is zero, return the dst parameter. */ - if (*p == '\0') - return expand_expr (dst, target, mode, EXPAND_NORMAL); - else if (!optimize_size) + /* If the string length is zero, return the dst parameter. */ + if (p && *p == '\0') + return expand_expr (dst, target, mode, EXPAND_NORMAL); + + if (!optimize_size) + { + /* See if we can store by pieces into (dst + strlen(dst)). */ + tree newsrc, newdst, + strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN]; + rtx insns; + + /* Stabilize the argument list. */ + newsrc = builtin_save_expr (src); + if (newsrc != src) + arglist = build_tree_list (NULL_TREE, newsrc); + else + arglist = TREE_CHAIN (arglist); /* Reusing arglist if safe. */ + + dst = builtin_save_expr (dst); + + start_sequence (); + + /* Create strlen (dst). */ + newdst = + build_function_call_expr (strlen_fn, + build_tree_list (NULL_TREE, dst)); + /* Create (dst + (cast) strlen (dst)). */ + newdst = fold_convert (TREE_TYPE (dst), newdst); + newdst = fold_build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst); + + newdst = builtin_save_expr (newdst); + arglist = tree_cons (NULL_TREE, newdst, arglist); + + if (!expand_builtin_strcpy (fndecl, arglist, target, mode)) { - /* Otherwise if !optimize_size, see if we can store by - pieces into (dst + strlen(dst)). */ - tree newdst, arglist, - strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN]; - - /* This is the length argument. */ - arglist = build_tree_list (NULL_TREE, - fold (size_binop (PLUS_EXPR, - c_strlen (src, 0), - ssize_int (1)))); - /* Prepend src argument. */ - arglist = tree_cons (NULL_TREE, src, arglist); - - /* We're going to use dst more than once. */ - dst = builtin_save_expr (dst); - - /* Create strlen (dst). */ - newdst = - fold (build_function_call_expr (strlen_fn, - build_tree_list (NULL_TREE, - dst))); - /* Create (dst + strlen (dst)). */ - newdst = fold (build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst)); - - /* Prepend the new dst argument. */ - arglist = tree_cons (NULL_TREE, newdst, arglist); - - /* We don't want to get turned into a memcpy if the - target is const0_rtx, i.e. when the return value - isn't used. That would produce pessimized code so - pass in a target of zero, it should never actually be - used. If this was successful return the original - dst, not the result of mempcpy. */ - if (expand_builtin_mempcpy (arglist, type, /*target=*/0, mode, /*endp=*/0)) - return expand_expr (dst, target, mode, EXPAND_NORMAL); - else - return 0; + end_sequence (); /* Stop sequence. */ + return 0; } + + /* Output the entire sequence. */ + insns = get_insns (); + end_sequence (); + emit_insn (insns); + + return expand_expr (dst, target, mode, EXPAND_NORMAL); } return 0; @@ -3865,7 +4108,7 @@ stabilize_va_list (tree valist, int needs_lvalue) return valist; pt = build_pointer_type (va_list_type_node); - valist = fold (build1 (ADDR_EXPR, pt, valist)); + valist = fold_build1 (ADDR_EXPR, pt, valist); TREE_SIDE_EFFECTS (valist) = 1; } @@ -3960,7 +4203,8 @@ std_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually requires greater alignment, we must perform dynamic alignment. */ - if (boundary > align) + if (boundary > align + && !integer_zerop (TYPE_SIZE (type))) { t = fold_convert (TREE_TYPE (valist), size_int (boundary - 1)); t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, @@ -3997,11 +4241,11 @@ std_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) if (PAD_VARARGS_DOWN && !integer_zerop (rounded_size)) { /* Small args are padded downward. */ - t = fold (build2 (GT_EXPR, sizetype, rounded_size, size_int (align))); - t = fold (build3 (COND_EXPR, sizetype, t, size_zero_node, - size_binop (MINUS_EXPR, rounded_size, type_size))); + t = fold_build2 (GT_EXPR, sizetype, rounded_size, size_int (align)); + t = fold_build3 (COND_EXPR, sizetype, t, size_zero_node, + size_binop (MINUS_EXPR, rounded_size, type_size)); t = fold_convert (TREE_TYPE (addr), t); - addr = fold (build2 (PLUS_EXPR, TREE_TYPE (addr), addr, t)); + addr = fold_build2 (PLUS_EXPR, TREE_TYPE (addr), addr, t); } /* Compute new value for AP. */ @@ -4089,12 +4333,12 @@ gimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p) /* Unfortunately, this is merely undefined, rather than a constraint violation, so we cannot make this an error. If this call is never executed, the program is still strictly conforming. */ - warning ("%qT is promoted to %qT when passed through %<...%>", + warning (0, "%qT is promoted to %qT when passed through %<...%>", type, promoted_type); if (! gave_help) { gave_help = true; - warning ("(so you should pass %qT not %qT to %)", + warning (0, "(so you should pass %qT not %qT to %)", promoted_type, type); } @@ -4131,7 +4375,8 @@ gimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p) gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue); if (!targetm.gimplify_va_arg_expr) - /* Once most targets are converted this should abort. */ + /* FIXME:Once most targets are converted we should merely + assert this is non-null. */ return GS_ALL_DONE; *expr_p = targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p); @@ -4233,9 +4478,9 @@ expand_builtin_frame_address (tree fndecl, tree arglist) if (tem == NULL) { if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) - warning ("unsupported argument to %<__builtin_frame_address%>"); + warning (0, "unsupported argument to %<__builtin_frame_address%>"); else - warning ("unsupported argument to %<__builtin_return_address%>"); + warning (0, "unsupported argument to %<__builtin_return_address%>"); return const0_rtx; } @@ -4270,7 +4515,7 @@ expand_builtin_alloca (tree arglist, rtx target) return 0; /* Compute the argument. */ - op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); + op0 = expand_normal (TREE_VALUE (arglist)); /* Allocate the desired space. */ result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT); @@ -4479,7 +4724,7 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) return ret; } -static void +void expand_builtin_trap (void) { #ifdef HAVE_trap @@ -4528,17 +4773,17 @@ expand_builtin_copysign (tree arglist, rtx target, rtx subtarget) return 0; arg = TREE_VALUE (arglist); - op0 = expand_expr (arg, subtarget, VOIDmode, 0); + op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL); arg = TREE_VALUE (TREE_CHAIN (arglist)); - op1 = expand_expr (arg, NULL, VOIDmode, 0); + op1 = expand_normal (arg); return expand_copysign (op0, op1, target); } /* Create a new constant string literal and return a char* pointer to it. The STRING_CST value is the LEN characters at STR. */ -static tree +tree build_string_literal (int len, const char *str) { tree t, elem, index, type; @@ -4561,20 +4806,22 @@ build_string_literal (int len, const char *str) return t; } -/* Expand a call to printf or printf_unlocked with argument list ARGLIST. +/* Expand EXP, a call to printf or printf_unlocked. Return 0 if a normal call should be emitted rather than transforming the function inline. If convenient, the result should be placed in TARGET with mode MODE. UNLOCKED indicates this is a printf_unlocked call. */ static rtx -expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, +expand_builtin_printf (tree exp, rtx target, enum machine_mode mode, bool unlocked) { - tree fn_putchar = unlocked - ? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_PUTCHAR]; - tree fn_puts = unlocked ? implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_PUTS]; + tree arglist = TREE_OPERAND (exp, 1); + /* If we're using an unlocked function, assume the other unlocked + functions exist explicitly. */ + tree const fn_putchar = unlocked ? built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_PUTCHAR]; + tree const fn_puts = unlocked ? built_in_decls[BUILT_IN_PUTS_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_PUTS]; const char *fmt_str; tree fn, fmt, arg; @@ -4595,8 +4842,11 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, if (fmt_str == NULL) return 0; + if (!init_target_chars()) + return 0; + /* If the format specifier was "%s\n", call __builtin_puts(arg). */ - if (strcmp (fmt_str, "%s\n") == 0) + if (strcmp (fmt_str, target_percent_s_newline) == 0) { if (! arglist || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) @@ -4605,7 +4855,7 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, fn = fn_puts; } /* If the format specifier was "%c", call __builtin_putchar(arg). */ - else if (strcmp (fmt_str, "%c") == 0) + else if (strcmp (fmt_str, target_percent_c) == 0) { if (! arglist || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE @@ -4616,7 +4866,7 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, else { /* We can't handle anything else with % args or %% ... yet. */ - if (strchr (fmt_str, '%')) + if (strchr (fmt_str, target_percent)) return 0; if (arglist) @@ -4639,7 +4889,7 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, { /* If the format specifier was "string\n", call puts("string"). */ size_t len = strlen (fmt_str); - if (fmt_str[len - 1] == '\n') + if ((unsigned char)fmt_str[len - 1] == target_newline) { /* Create a NUL-terminated string that's one char shorter than the original, stripping off the trailing '\n'. */ @@ -4660,23 +4910,28 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, if (!fn) return 0; - return expand_expr (build_function_call_expr (fn, arglist), - target, mode, EXPAND_NORMAL); + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); } -/* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST. +/* Expand EXP, a call to fprintf or fprintf_unlocked. Return 0 if a normal call should be emitted rather than transforming the function inline. If convenient, the result should be placed in TARGET with mode MODE. UNLOCKED indicates this is a fprintf_unlocked call. */ static rtx -expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode, +expand_builtin_fprintf (tree exp, rtx target, enum machine_mode mode, bool unlocked) { - tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_FPUTC]; - tree fn_fputs = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_FPUTS]; + tree arglist = TREE_OPERAND (exp, 1); + /* If we're using an unlocked function, assume the other unlocked + functions exist explicitly. */ + tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_FPUTC]; + tree const fn_fputs = unlocked ? built_in_decls[BUILT_IN_FPUTS_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_FPUTS]; const char *fmt_str; tree fn, fmt, fp, arg; @@ -4703,8 +4958,11 @@ expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode, if (fmt_str == NULL) return 0; + if (!init_target_chars()) + return 0; + /* If the format specifier was "%s", call __builtin_fputs(arg,fp). */ - if (strcmp (fmt_str, "%s") == 0) + if (strcmp (fmt_str, target_percent_s) == 0) { if (! arglist || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) @@ -4716,7 +4974,7 @@ expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode, fn = fn_fputs; } /* If the format specifier was "%c", call __builtin_fputc(arg,fp). */ - else if (strcmp (fmt_str, "%c") == 0) + else if (strcmp (fmt_str, target_percent_c) == 0) { if (! arglist || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE @@ -4730,7 +4988,7 @@ expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode, else { /* We can't handle anything else with % args or %% ... yet. */ - if (strchr (fmt_str, '%')) + if (strchr (fmt_str, target_percent)) return 0; if (arglist) @@ -4754,8 +5012,10 @@ expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode, if (!fn) return 0; - return expand_expr (build_function_call_expr (fn, arglist), - target, mode, EXPAND_NORMAL); + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); } /* Expand a call to sprintf with argument list ARGLIST. Return 0 if @@ -4790,8 +5050,11 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) if (fmt_str == NULL) return 0; + if (!init_target_chars()) + return 0; + /* If the format doesn't contain % args or %%, use strcpy. */ - if (strchr (fmt_str, '%') == 0) + if (strchr (fmt_str, target_percent) == 0) { tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; tree exp; @@ -4806,7 +5069,7 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) return expand_expr (exp, target, mode, EXPAND_NORMAL); } /* If the format is "%s", use strcpy if the result isn't used. */ - else if (strcmp (fmt_str, "%s") == 0) + else if (strcmp (fmt_str, target_percent_s) == 0) { tree fn, arg, len; fn = implicit_built_in_decls[BUILT_IN_STRCPY]; @@ -4910,9 +5173,9 @@ expand_builtin_init_trampoline (tree arglist) arglist = TREE_CHAIN (arglist); t_chain = TREE_VALUE (arglist); - r_tramp = expand_expr (t_tramp, NULL_RTX, VOIDmode, 0); - r_func = expand_expr (t_func, NULL_RTX, VOIDmode, 0); - r_chain = expand_expr (t_chain, NULL_RTX, VOIDmode, 0); + r_tramp = expand_normal (t_tramp); + r_func = expand_normal (t_func); + r_chain = expand_normal (t_chain); /* Generate insns to initialize the trampoline. */ r_tramp = round_trampoline_addr (r_tramp); @@ -4936,7 +5199,7 @@ expand_builtin_adjust_trampoline (tree arglist) if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) return NULL_RTX; - tramp = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); + tramp = expand_normal (TREE_VALUE (arglist)); tramp = round_trampoline_addr (tramp); #ifdef TRAMPOLINE_ADJUST_ADDRESS TRAMPOLINE_ADJUST_ADDRESS (tramp); @@ -4971,19 +5234,19 @@ expand_builtin_signbit (tree exp, rtx target) /* For floating point formats without a sign bit, implement signbit as "ARG < 0.0". */ - bitpos = fmt->signbit; + bitpos = fmt->signbit_ro; if (bitpos < 0) { /* But we can't do this if the format supports signed zero. */ if (fmt->has_signed_zero && HONOR_SIGNED_ZEROS (fmode)) return 0; - arg = fold (build2 (LT_EXPR, TREE_TYPE (exp), arg, - build_real (TREE_TYPE (arg), dconst0))); + arg = fold_build2 (LT_EXPR, TREE_TYPE (exp), arg, + build_real (TREE_TYPE (arg), dconst0)); return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL); } - temp = expand_expr (arg, NULL_RTX, VOIDmode, 0); + temp = expand_normal (arg); if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD) { imode = int_mode_for_mode (fmode); @@ -5108,6 +5371,176 @@ expand_builtin_fork_or_exec (tree fn, tree arglist, rtx target, int ignore) return expand_call (call, target, ignore); } + + +/* Reconstitute a mode for a __sync intrinsic operation. Since the type of + the pointer in these functions is void*, the tree optimizers may remove + casts. The mode computed in expand_builtin isn't reliable either, due + to __sync_bool_compare_and_swap. + + FCODE_DIFF should be fcode - base, where base is the FOO_1 code for the + group of builtins. This gives us log2 of the mode size. */ + +static inline enum machine_mode +get_builtin_sync_mode (int fcode_diff) +{ + /* The size is not negotiable, so ask not to get BLKmode in return + if the target indicates that a smaller size would be better. */ + return mode_for_size (BITS_PER_UNIT << fcode_diff, MODE_INT, 0); +} + +/* Expand the memory expression LOC and return the appropriate memory operand + for the builtin_sync operations. */ + +static rtx +get_builtin_sync_mem (tree loc, enum machine_mode mode) +{ + rtx addr, mem; + + addr = expand_expr (loc, NULL, Pmode, EXPAND_SUM); + + /* Note that we explicitly do not want any alias information for this + memory, so that we kill all other live memories. Otherwise we don't + satisfy the full barrier semantics of the intrinsic. */ + mem = validize_mem (gen_rtx_MEM (mode, addr)); + + set_mem_align (mem, get_pointer_alignment (loc, BIGGEST_ALIGNMENT)); + set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER); + MEM_VOLATILE_P (mem) = 1; + + return mem; +} + +/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics. + ARGLIST is the operands list to the function. CODE is the rtx code + that corresponds to the arithmetic or logical operation from the name; + an exception here is that NOT actually means NAND. TARGET is an optional + place for us to store the results; AFTER is true if this is the + fetch_and_xxx form. IGNORE is true if we don't actually care about + the result of the operation at all. */ + +static rtx +expand_builtin_sync_operation (enum machine_mode mode, tree arglist, + enum rtx_code code, bool after, + rtx target, bool ignore) +{ + rtx val, mem; + + /* Expand the operands. */ + mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode); + + arglist = TREE_CHAIN (arglist); + val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); + + if (ignore) + return expand_sync_operation (mem, val, code); + else + return expand_sync_fetch_operation (mem, val, code, after, target); +} + +/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap + intrinsics. ARGLIST is the operands list to the function. IS_BOOL is + true if this is the boolean form. TARGET is a place for us to store the + results; this is NOT optional if IS_BOOL is true. */ + +static rtx +expand_builtin_compare_and_swap (enum machine_mode mode, tree arglist, + bool is_bool, rtx target) +{ + rtx old_val, new_val, mem; + + /* Expand the operands. */ + mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode); + + arglist = TREE_CHAIN (arglist); + old_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); + + arglist = TREE_CHAIN (arglist); + new_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); + + if (is_bool) + return expand_bool_compare_and_swap (mem, old_val, new_val, target); + else + return expand_val_compare_and_swap (mem, old_val, new_val, target); +} + +/* Expand the __sync_lock_test_and_set intrinsic. Note that the most + general form is actually an atomic exchange, and some targets only + support a reduced form with the second argument being a constant 1. + ARGLIST is the operands list to the function; TARGET is an optional + place for us to store the results. */ + +static rtx +expand_builtin_lock_test_and_set (enum machine_mode mode, tree arglist, + rtx target) +{ + rtx val, mem; + + /* Expand the operands. */ + mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode); + + arglist = TREE_CHAIN (arglist); + val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); + + return expand_sync_lock_test_and_set (mem, val, target); +} + +/* Expand the __sync_synchronize intrinsic. */ + +static void +expand_builtin_synchronize (void) +{ + tree x; + +#ifdef HAVE_memory_barrier + if (HAVE_memory_barrier) + { + emit_insn (gen_memory_barrier ()); + return; + } +#endif + + /* If no explicit memory barrier instruction is available, create an + empty asm stmt with a memory clobber. */ + x = build4 (ASM_EXPR, void_type_node, build_string (0, ""), NULL, NULL, + tree_cons (NULL, build_string (6, "memory"), NULL)); + ASM_VOLATILE_P (x) = 1; + expand_asm_expr (x); +} + +/* Expand the __sync_lock_release intrinsic. ARGLIST is the operands list + to the function. */ + +static void +expand_builtin_lock_release (enum machine_mode mode, tree arglist) +{ + enum insn_code icode; + rtx mem, insn; + rtx val = const0_rtx; + + /* Expand the operands. */ + mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode); + + /* If there is an explicit operation in the md file, use it. */ + icode = sync_lock_release[mode]; + if (icode != CODE_FOR_nothing) + { + if (!insn_data[icode].operand[1].predicate (val, mode)) + val = force_reg (mode, val); + + insn = GEN_FCN (icode) (mem, val); + if (insn) + { + emit_insn (insn); + return; + } + } + + /* Otherwise we can implement this operation by emitting a barrier + followed by a store of zero. */ + expand_builtin_synchronize (); + emit_move_insn (mem, val); +} /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient @@ -5126,11 +5559,19 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) return targetm.expand_builtin (exp, target, subtarget, mode, ignore); + else + { + /* Try expanding the builtin via the generic target hook. */ + rtx tmp = targetm.expand_library_builtin (exp, target, subtarget, + mode, ignore); + if (tmp != NULL_RTX) + return tmp; + } /* When not optimizing, generate calls to library functions for a certain set of builtins. */ if (!optimize - && !CALLED_AS_BUILT_IN (fndecl) + && !called_as_built_in (fndecl) && DECL_ASSEMBLER_NAME_SET_P (fndecl) && fcode != BUILT_IN_ALLOCA) return expand_call (exp, target, ignore); @@ -5167,17 +5608,13 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, switch (fcode) { - case BUILT_IN_FABS: - case BUILT_IN_FABSF: - case BUILT_IN_FABSL: + CASE_FLT_FN (BUILT_IN_FABS): target = expand_builtin_fabs (arglist, target, subtarget); if (target) return target; break; - case BUILT_IN_COPYSIGN: - case BUILT_IN_COPYSIGNF: - case BUILT_IN_COPYSIGNL: + CASE_FLT_FN (BUILT_IN_COPYSIGN): target = expand_builtin_copysign (arglist, target, subtarget); if (target) return target; @@ -5185,114 +5622,67 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, /* Just do a normal library call if we were unable to fold the values. */ - case BUILT_IN_CABS: - case BUILT_IN_CABSF: - case BUILT_IN_CABSL: + CASE_FLT_FN (BUILT_IN_CABS): break; - case BUILT_IN_EXP: - case BUILT_IN_EXPF: - case BUILT_IN_EXPL: - case BUILT_IN_EXP10: - case BUILT_IN_EXP10F: - case BUILT_IN_EXP10L: - case BUILT_IN_POW10: - case BUILT_IN_POW10F: - case BUILT_IN_POW10L: - case BUILT_IN_EXP2: - case BUILT_IN_EXP2F: - case BUILT_IN_EXP2L: - case BUILT_IN_EXPM1: - case BUILT_IN_EXPM1F: - case BUILT_IN_EXPM1L: - case BUILT_IN_LOGB: - case BUILT_IN_LOGBF: - case BUILT_IN_LOGBL: - case BUILT_IN_ILOGB: - case BUILT_IN_ILOGBF: - case BUILT_IN_ILOGBL: - case BUILT_IN_LOG: - case BUILT_IN_LOGF: - case BUILT_IN_LOGL: - case BUILT_IN_LOG10: - case BUILT_IN_LOG10F: - case BUILT_IN_LOG10L: - case BUILT_IN_LOG2: - case BUILT_IN_LOG2F: - case BUILT_IN_LOG2L: - case BUILT_IN_LOG1P: - case BUILT_IN_LOG1PF: - case BUILT_IN_LOG1PL: - case BUILT_IN_TAN: - case BUILT_IN_TANF: - case BUILT_IN_TANL: - case BUILT_IN_ASIN: - case BUILT_IN_ASINF: - case BUILT_IN_ASINL: - case BUILT_IN_ACOS: - case BUILT_IN_ACOSF: - case BUILT_IN_ACOSL: - case BUILT_IN_ATAN: - case BUILT_IN_ATANF: - case BUILT_IN_ATANL: + CASE_FLT_FN (BUILT_IN_EXP): + CASE_FLT_FN (BUILT_IN_EXP10): + CASE_FLT_FN (BUILT_IN_POW10): + CASE_FLT_FN (BUILT_IN_EXP2): + CASE_FLT_FN (BUILT_IN_EXPM1): + CASE_FLT_FN (BUILT_IN_LOGB): + CASE_FLT_FN (BUILT_IN_ILOGB): + CASE_FLT_FN (BUILT_IN_LOG): + CASE_FLT_FN (BUILT_IN_LOG10): + CASE_FLT_FN (BUILT_IN_LOG2): + CASE_FLT_FN (BUILT_IN_LOG1P): + CASE_FLT_FN (BUILT_IN_TAN): + CASE_FLT_FN (BUILT_IN_ASIN): + CASE_FLT_FN (BUILT_IN_ACOS): + CASE_FLT_FN (BUILT_IN_ATAN): /* Treat these like sqrt only if unsafe math optimizations are allowed, because of possible accuracy problems. */ if (! flag_unsafe_math_optimizations) break; - case BUILT_IN_SQRT: - case BUILT_IN_SQRTF: - case BUILT_IN_SQRTL: - case BUILT_IN_FLOOR: - case BUILT_IN_FLOORF: - case BUILT_IN_FLOORL: - case BUILT_IN_CEIL: - case BUILT_IN_CEILF: - case BUILT_IN_CEILL: - case BUILT_IN_TRUNC: - case BUILT_IN_TRUNCF: - case BUILT_IN_TRUNCL: - case BUILT_IN_ROUND: - case BUILT_IN_ROUNDF: - case BUILT_IN_ROUNDL: - case BUILT_IN_NEARBYINT: - case BUILT_IN_NEARBYINTF: - case BUILT_IN_NEARBYINTL: - case BUILT_IN_RINT: - case BUILT_IN_RINTF: - case BUILT_IN_RINTL: + CASE_FLT_FN (BUILT_IN_SQRT): + CASE_FLT_FN (BUILT_IN_FLOOR): + CASE_FLT_FN (BUILT_IN_CEIL): + CASE_FLT_FN (BUILT_IN_TRUNC): + CASE_FLT_FN (BUILT_IN_ROUND): + CASE_FLT_FN (BUILT_IN_NEARBYINT): + CASE_FLT_FN (BUILT_IN_RINT): + CASE_FLT_FN (BUILT_IN_LRINT): + CASE_FLT_FN (BUILT_IN_LLRINT): target = expand_builtin_mathfn (exp, target, subtarget); if (target) return target; break; - case BUILT_IN_POW: - case BUILT_IN_POWF: - case BUILT_IN_POWL: + CASE_FLT_FN (BUILT_IN_LCEIL): + CASE_FLT_FN (BUILT_IN_LLCEIL): + CASE_FLT_FN (BUILT_IN_LFLOOR): + CASE_FLT_FN (BUILT_IN_LLFLOOR): + target = expand_builtin_int_roundingfn (exp, target, subtarget); + if (target) + return target; + break; + + CASE_FLT_FN (BUILT_IN_POW): target = expand_builtin_pow (exp, target, subtarget); if (target) return target; break; - case BUILT_IN_POWI: - case BUILT_IN_POWIF: - case BUILT_IN_POWIL: + CASE_FLT_FN (BUILT_IN_POWI): target = expand_builtin_powi (exp, target, subtarget); if (target) return target; break; - case BUILT_IN_ATAN2: - case BUILT_IN_ATAN2F: - case BUILT_IN_ATAN2L: - case BUILT_IN_LDEXP: - case BUILT_IN_LDEXPF: - case BUILT_IN_LDEXPL: - case BUILT_IN_FMOD: - case BUILT_IN_FMODF: - case BUILT_IN_FMODL: - case BUILT_IN_DREM: - case BUILT_IN_DREMF: - case BUILT_IN_DREML: + CASE_FLT_FN (BUILT_IN_ATAN2): + CASE_FLT_FN (BUILT_IN_LDEXP): + CASE_FLT_FN (BUILT_IN_FMOD): + CASE_FLT_FN (BUILT_IN_DREM): if (! flag_unsafe_math_optimizations) break; target = expand_builtin_mathfn_2 (exp, target, subtarget); @@ -5300,12 +5690,8 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - case BUILT_IN_SIN: - case BUILT_IN_SINF: - case BUILT_IN_SINL: - case BUILT_IN_COS: - case BUILT_IN_COSF: - case BUILT_IN_COSL: + CASE_FLT_FN (BUILT_IN_SIN): + CASE_FLT_FN (BUILT_IN_COS): if (! flag_unsafe_math_optimizations) break; target = expand_builtin_mathfn_3 (exp, target, subtarget); @@ -5313,6 +5699,14 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; + CASE_FLT_FN (BUILT_IN_SINCOS): + if (! flag_unsafe_math_optimizations) + break; + target = expand_builtin_sincos (exp); + if (target) + return target; + break; + case BUILT_IN_APPLY_ARGS: return expand_builtin_apply_args (); @@ -5339,7 +5733,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, rtx ops[3]; for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++) - ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0); + ops[i] = expand_normal (TREE_VALUE (t)); return expand_builtin_apply (ops[0], ops[1], ops[2]); } @@ -5349,8 +5743,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, memory returned by __builtin_apply. */ case BUILT_IN_RETURN: if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) - expand_builtin_return (expand_expr (TREE_VALUE (arglist), - NULL_RTX, VOIDmode, 0)); + expand_builtin_return (expand_normal (TREE_VALUE (arglist))); return const0_rtx; case BUILT_IN_SAVEREGS: @@ -5398,9 +5791,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_stack_restore (TREE_VALUE (arglist)); return const0_rtx; - case BUILT_IN_FFS: - case BUILT_IN_FFSL: - case BUILT_IN_FFSLL: + CASE_INT_FN (BUILT_IN_FFS): case BUILT_IN_FFSIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, ffs_optab); @@ -5408,9 +5799,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - case BUILT_IN_CLZ: - case BUILT_IN_CLZL: - case BUILT_IN_CLZLL: + CASE_INT_FN (BUILT_IN_CLZ): case BUILT_IN_CLZIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, clz_optab); @@ -5418,9 +5807,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - case BUILT_IN_CTZ: - case BUILT_IN_CTZL: - case BUILT_IN_CTZLL: + CASE_INT_FN (BUILT_IN_CTZ): case BUILT_IN_CTZIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, ctz_optab); @@ -5428,9 +5815,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - case BUILT_IN_POPCOUNT: - case BUILT_IN_POPCOUNTL: - case BUILT_IN_POPCOUNTLL: + CASE_INT_FN (BUILT_IN_POPCOUNT): case BUILT_IN_POPCOUNTIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, popcount_optab); @@ -5438,9 +5823,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - case BUILT_IN_PARITY: - case BUILT_IN_PARITYL: - case BUILT_IN_PARITYLL: + CASE_INT_FN (BUILT_IN_PARITY): case BUILT_IN_PARITYIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, parity_optab); @@ -5455,7 +5838,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; case BUILT_IN_STRCPY: - target = expand_builtin_strcpy (exp, target, mode); + target = expand_builtin_strcpy (fndecl, arglist, target, mode); if (target) return target; break; @@ -5473,7 +5856,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; case BUILT_IN_STRCAT: - target = expand_builtin_strcat (arglist, TREE_TYPE (exp), target, mode); + target = expand_builtin_strcat (fndecl, arglist, target, mode); if (target) return target; break; @@ -5535,25 +5918,26 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; case BUILT_IN_MEMMOVE: - target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target, mode); + target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target, + mode, exp); if (target) return target; break; case BUILT_IN_BCOPY: - target = expand_builtin_bcopy (arglist, TREE_TYPE (exp)); + target = expand_builtin_bcopy (exp); if (target) return target; break; case BUILT_IN_MEMSET: - target = expand_builtin_memset (arglist, target, mode); + target = expand_builtin_memset (arglist, target, mode, exp); if (target) return target; break; case BUILT_IN_BZERO: - target = expand_builtin_bzero (arglist); + target = expand_builtin_bzero (exp); if (target) return target; break; @@ -5592,9 +5976,8 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, else { rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget, - VOIDmode, 0); - rtx value = expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), - NULL_RTX, VOIDmode, 0); + VOIDmode, EXPAND_NORMAL); + rtx value = expand_normal (TREE_VALUE (TREE_CHAIN (arglist))); if (value != const1_rtx) { @@ -5618,7 +6001,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) { rtx buf_addr - = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); + = expand_normal (TREE_VALUE (arglist)); expand_builtin_update_setjmp_buf (buf_addr); return const0_rtx; @@ -5630,13 +6013,13 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return const0_rtx; case BUILT_IN_PRINTF: - target = expand_builtin_printf (arglist, target, mode, false); + target = expand_builtin_printf (exp, target, mode, false); if (target) return target; break; case BUILT_IN_PRINTF_UNLOCKED: - target = expand_builtin_printf (arglist, target, mode, true); + target = expand_builtin_printf (exp, target, mode, true); if (target) return target; break; @@ -5653,13 +6036,13 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; case BUILT_IN_FPRINTF: - target = expand_builtin_fprintf (arglist, target, mode, false); + target = expand_builtin_fprintf (exp, target, mode, false); if (target) return target; break; case BUILT_IN_FPRINTF_UNLOCKED: - target = expand_builtin_fprintf (arglist, target, mode, true); + target = expand_builtin_fprintf (exp, target, mode, true); if (target) return target; break; @@ -5670,9 +6053,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - case BUILT_IN_SIGNBIT: - case BUILT_IN_SIGNBITF: - case BUILT_IN_SIGNBITL: + CASE_FLT_FN (BUILT_IN_SIGNBIT): target = expand_builtin_signbit (exp, target); if (target) return target; @@ -5741,6 +6122,227 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; + case BUILT_IN_FETCH_AND_ADD_1: + case BUILT_IN_FETCH_AND_ADD_2: + case BUILT_IN_FETCH_AND_ADD_4: + case BUILT_IN_FETCH_AND_ADD_8: + case BUILT_IN_FETCH_AND_ADD_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_ADD_1); + target = expand_builtin_sync_operation (mode, arglist, PLUS, + false, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_FETCH_AND_SUB_1: + case BUILT_IN_FETCH_AND_SUB_2: + case BUILT_IN_FETCH_AND_SUB_4: + case BUILT_IN_FETCH_AND_SUB_8: + case BUILT_IN_FETCH_AND_SUB_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_SUB_1); + target = expand_builtin_sync_operation (mode, arglist, MINUS, + false, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_FETCH_AND_OR_1: + case BUILT_IN_FETCH_AND_OR_2: + case BUILT_IN_FETCH_AND_OR_4: + case BUILT_IN_FETCH_AND_OR_8: + case BUILT_IN_FETCH_AND_OR_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_OR_1); + target = expand_builtin_sync_operation (mode, arglist, IOR, + false, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_FETCH_AND_AND_1: + case BUILT_IN_FETCH_AND_AND_2: + case BUILT_IN_FETCH_AND_AND_4: + case BUILT_IN_FETCH_AND_AND_8: + case BUILT_IN_FETCH_AND_AND_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_AND_1); + target = expand_builtin_sync_operation (mode, arglist, AND, + false, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_FETCH_AND_XOR_1: + case BUILT_IN_FETCH_AND_XOR_2: + case BUILT_IN_FETCH_AND_XOR_4: + case BUILT_IN_FETCH_AND_XOR_8: + case BUILT_IN_FETCH_AND_XOR_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_XOR_1); + target = expand_builtin_sync_operation (mode, arglist, XOR, + false, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_FETCH_AND_NAND_1: + case BUILT_IN_FETCH_AND_NAND_2: + case BUILT_IN_FETCH_AND_NAND_4: + case BUILT_IN_FETCH_AND_NAND_8: + case BUILT_IN_FETCH_AND_NAND_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_NAND_1); + target = expand_builtin_sync_operation (mode, arglist, NOT, + false, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_ADD_AND_FETCH_1: + case BUILT_IN_ADD_AND_FETCH_2: + case BUILT_IN_ADD_AND_FETCH_4: + case BUILT_IN_ADD_AND_FETCH_8: + case BUILT_IN_ADD_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ADD_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, arglist, PLUS, + true, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_SUB_AND_FETCH_1: + case BUILT_IN_SUB_AND_FETCH_2: + case BUILT_IN_SUB_AND_FETCH_4: + case BUILT_IN_SUB_AND_FETCH_8: + case BUILT_IN_SUB_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SUB_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, arglist, MINUS, + true, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_OR_AND_FETCH_1: + case BUILT_IN_OR_AND_FETCH_2: + case BUILT_IN_OR_AND_FETCH_4: + case BUILT_IN_OR_AND_FETCH_8: + case BUILT_IN_OR_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_OR_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, arglist, IOR, + true, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_AND_AND_FETCH_1: + case BUILT_IN_AND_AND_FETCH_2: + case BUILT_IN_AND_AND_FETCH_4: + case BUILT_IN_AND_AND_FETCH_8: + case BUILT_IN_AND_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_AND_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, arglist, AND, + true, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_XOR_AND_FETCH_1: + case BUILT_IN_XOR_AND_FETCH_2: + case BUILT_IN_XOR_AND_FETCH_4: + case BUILT_IN_XOR_AND_FETCH_8: + case BUILT_IN_XOR_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_XOR_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, arglist, XOR, + true, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_NAND_AND_FETCH_1: + case BUILT_IN_NAND_AND_FETCH_2: + case BUILT_IN_NAND_AND_FETCH_4: + case BUILT_IN_NAND_AND_FETCH_8: + case BUILT_IN_NAND_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_NAND_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, arglist, NOT, + true, target, ignore); + if (target) + return target; + break; + + case BUILT_IN_BOOL_COMPARE_AND_SWAP_1: + case BUILT_IN_BOOL_COMPARE_AND_SWAP_2: + case BUILT_IN_BOOL_COMPARE_AND_SWAP_4: + case BUILT_IN_BOOL_COMPARE_AND_SWAP_8: + case BUILT_IN_BOOL_COMPARE_AND_SWAP_16: + if (mode == VOIDmode) + mode = TYPE_MODE (boolean_type_node); + if (!target || !register_operand (target, mode)) + target = gen_reg_rtx (mode); + + mode = get_builtin_sync_mode (fcode - BUILT_IN_BOOL_COMPARE_AND_SWAP_1); + target = expand_builtin_compare_and_swap (mode, arglist, true, target); + if (target) + return target; + break; + + case BUILT_IN_VAL_COMPARE_AND_SWAP_1: + case BUILT_IN_VAL_COMPARE_AND_SWAP_2: + case BUILT_IN_VAL_COMPARE_AND_SWAP_4: + case BUILT_IN_VAL_COMPARE_AND_SWAP_8: + case BUILT_IN_VAL_COMPARE_AND_SWAP_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_VAL_COMPARE_AND_SWAP_1); + target = expand_builtin_compare_and_swap (mode, arglist, false, target); + if (target) + return target; + break; + + case BUILT_IN_LOCK_TEST_AND_SET_1: + case BUILT_IN_LOCK_TEST_AND_SET_2: + case BUILT_IN_LOCK_TEST_AND_SET_4: + case BUILT_IN_LOCK_TEST_AND_SET_8: + case BUILT_IN_LOCK_TEST_AND_SET_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_TEST_AND_SET_1); + target = expand_builtin_lock_test_and_set (mode, arglist, target); + if (target) + return target; + break; + + case BUILT_IN_LOCK_RELEASE_1: + case BUILT_IN_LOCK_RELEASE_2: + case BUILT_IN_LOCK_RELEASE_4: + case BUILT_IN_LOCK_RELEASE_8: + case BUILT_IN_LOCK_RELEASE_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_RELEASE_1); + expand_builtin_lock_release (mode, arglist); + return const0_rtx; + + case BUILT_IN_SYNCHRONIZE: + expand_builtin_synchronize (); + return const0_rtx; + + case BUILT_IN_OBJECT_SIZE: + return expand_builtin_object_size (exp); + + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + target = expand_builtin_memory_chk (exp, target, mode, fcode); + if (target) + return target; + break; + + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_STRCAT_CHK: + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + maybe_emit_chk_warning (exp, fcode); + break; + + case BUILT_IN_SPRINTF_CHK: + case BUILT_IN_VSPRINTF_CHK: + maybe_emit_sprintf_chk_warning (exp, fcode); + break; + default: /* just do library call, if unknown builtin */ break; } @@ -5841,10 +6443,17 @@ fold_builtin_constant_p (tree arglist) /* If we know this is a constant, emit the constant of one. */ if (CONSTANT_CLASS_P (arglist) || (TREE_CODE (arglist) == CONSTRUCTOR - && TREE_CONSTANT (arglist)) - || (TREE_CODE (arglist) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST)) + && TREE_CONSTANT (arglist))) return integer_one_node; + if (TREE_CODE (arglist) == ADDR_EXPR) + { + tree op = TREE_OPERAND (arglist, 0); + if (TREE_CODE (op) == STRING_CST + || (TREE_CODE (op) == ARRAY_REF + && integer_zerop (TREE_OPERAND (op, 1)) + && TREE_CODE (TREE_OPERAND (op, 0)) == STRING_CST)) + return integer_one_node; + } /* If this expression has side effects, show we don't know it to be a constant. Likewise if it's a pointer or aggregate type since in @@ -6018,6 +6627,7 @@ integer_valued_real_p (tree t) real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c); return real_identical (&c, &cint); } + break; case NOP_EXPR: { @@ -6032,24 +6642,12 @@ integer_valued_real_p (tree t) case CALL_EXPR: switch (builtin_mathfn_code (t)) { - case BUILT_IN_CEIL: - case BUILT_IN_CEILF: - case BUILT_IN_CEILL: - case BUILT_IN_FLOOR: - case BUILT_IN_FLOORF: - case BUILT_IN_FLOORL: - case BUILT_IN_NEARBYINT: - case BUILT_IN_NEARBYINTF: - case BUILT_IN_NEARBYINTL: - case BUILT_IN_RINT: - case BUILT_IN_RINTF: - case BUILT_IN_RINTL: - case BUILT_IN_ROUND: - case BUILT_IN_ROUNDF: - case BUILT_IN_ROUNDL: - case BUILT_IN_TRUNC: - case BUILT_IN_TRUNCF: - case BUILT_IN_TRUNCL: + CASE_FLT_FN (BUILT_IN_CEIL): + CASE_FLT_FN (BUILT_IN_FLOOR): + CASE_FLT_FN (BUILT_IN_NEARBYINT): + CASE_FLT_FN (BUILT_IN_RINT): + CASE_FLT_FN (BUILT_IN_ROUND): + CASE_FLT_FN (BUILT_IN_TRUNC): return true; default: @@ -6068,10 +6666,8 @@ integer_valued_real_p (tree t) Do the transformation. */ static tree -fold_trunc_transparent_mathfn (tree exp) +fold_trunc_transparent_mathfn (tree fndecl, tree arglist) { - tree fndecl = get_callee_fndecl (exp); - tree arglist = TREE_OPERAND (exp, 1); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); tree arg; @@ -6091,7 +6687,7 @@ fold_trunc_transparent_mathfn (tree exp) if (optimize) { tree arg0 = strip_float_extensions (arg); - tree ftype = TREE_TYPE (exp); + tree ftype = TREE_TYPE (TREE_TYPE (fndecl)); tree newtype = TREE_TYPE (arg0); tree decl; @@ -6111,10 +6707,8 @@ fold_trunc_transparent_mathfn (tree exp) the argument, for instance lround((double)f) -> lroundf (f). */ static tree -fold_fixed_mathfn (tree exp) +fold_fixed_mathfn (tree fndecl, tree arglist) { - tree fndecl = get_callee_fndecl (exp); - tree arglist = TREE_OPERAND (exp, 1); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); tree arg; @@ -6126,7 +6720,7 @@ fold_fixed_mathfn (tree exp) /* If argument is already integer valued, and we don't need to worry about setting errno, there's no need to perform rounding. */ if (! flag_errno_math && integer_valued_real_p (arg)) - return fold (build1 (FIX_TRUNC_EXPR, TREE_TYPE (exp), arg)); + return fold_build1 (FIX_TRUNC_EXPR, TREE_TYPE (TREE_TYPE (fndecl)), arg); if (optimize) { @@ -6187,10 +6781,10 @@ fold_builtin_cabs (tree arglist, tree type) /* If either part is zero, cabs is fabs of the other. */ if (TREE_CODE (arg) == COMPLEX_EXPR && real_zerop (TREE_OPERAND (arg, 0))) - return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1))); + return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1)); if (TREE_CODE (arg) == COMPLEX_EXPR && real_zerop (TREE_OPERAND (arg, 1))) - return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0))); + return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0)); /* Don't do this when optimizing for size. */ if (flag_unsafe_math_optimizations @@ -6204,17 +6798,17 @@ fold_builtin_cabs (tree arglist, tree type) arg = builtin_save_expr (arg); - rpart = fold (build1 (REALPART_EXPR, type, arg)); - ipart = fold (build1 (IMAGPART_EXPR, type, arg)); + rpart = fold_build1 (REALPART_EXPR, type, arg); + ipart = fold_build1 (IMAGPART_EXPR, type, arg); rpart = builtin_save_expr (rpart); ipart = builtin_save_expr (ipart); - result = fold (build2 (PLUS_EXPR, type, - fold (build2 (MULT_EXPR, type, - rpart, rpart)), - fold (build2 (MULT_EXPR, type, - ipart, ipart)))); + result = fold_build2 (PLUS_EXPR, type, + fold_build2 (MULT_EXPR, type, + rpart, rpart), + fold_build2 (MULT_EXPR, type, + ipart, ipart)); arglist = build_tree_list (NULL_TREE, result); return build_function_call_expr (sqrtfn, arglist); @@ -6254,9 +6848,9 @@ fold_builtin_sqrt (tree arglist, tree type) if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode)) { tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); - arg = fold (build2 (MULT_EXPR, type, - TREE_VALUE (TREE_OPERAND (arg, 1)), - build_real (type, dconsthalf))); + arg = fold_build2 (MULT_EXPR, type, + TREE_VALUE (TREE_OPERAND (arg, 1)), + build_real (type, dconsthalf)); arglist = build_tree_list (NULL_TREE, arg); return build_function_call_expr (expfn, arglist); } @@ -6296,8 +6890,8 @@ fold_builtin_sqrt (tree arglist, tree type) tree narg1; if (!tree_expr_nonnegative_p (arg0)) arg0 = build1 (ABS_EXPR, type, arg0); - narg1 = fold (build2 (MULT_EXPR, type, arg1, - build_real (type, dconsthalf))); + narg1 = fold_build2 (MULT_EXPR, type, arg1, + build_real (type, dconsthalf)); arglist = tree_cons (NULL_TREE, arg0, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (powfn, arglist); @@ -6329,9 +6923,9 @@ fold_builtin_cbrt (tree arglist, tree type) tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); const REAL_VALUE_TYPE third_trunc = real_value_truncate (TYPE_MODE (type), dconstthird); - arg = fold (build2 (MULT_EXPR, type, - TREE_VALUE (TREE_OPERAND (arg, 1)), - build_real (type, third_trunc))); + arg = fold_build2 (MULT_EXPR, type, + TREE_VALUE (TREE_OPERAND (arg, 1)), + build_real (type, third_trunc)); arglist = build_tree_list (NULL_TREE, arg); return build_function_call_expr (expfn, arglist); } @@ -6390,8 +6984,8 @@ fold_builtin_cbrt (tree arglist, tree type) tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); const REAL_VALUE_TYPE dconstroot = real_value_truncate (TYPE_MODE (type), dconstthird); - tree narg01 = fold (build2 (MULT_EXPR, type, arg01, - build_real (type, dconstroot))); + tree narg01 = fold_build2 (MULT_EXPR, type, arg01, + build_real (type, dconstroot)); arglist = tree_cons (NULL_TREE, arg00, build_tree_list (NULL_TREE, narg01)); return build_function_call_expr (powfn, arglist); @@ -6502,9 +7096,8 @@ fold_builtin_atan (tree arglist, tree type) NULL_TREE if no simplification can be made. */ static tree -fold_builtin_trunc (tree exp) +fold_builtin_trunc (tree fndecl, tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) @@ -6515,23 +7108,22 @@ fold_builtin_trunc (tree exp) if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) { REAL_VALUE_TYPE r, x; - tree type = TREE_TYPE (exp); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); x = TREE_REAL_CST (arg); real_trunc (&r, TYPE_MODE (type), &x); return build_real (type, r); } - return fold_trunc_transparent_mathfn (exp); + return fold_trunc_transparent_mathfn (fndecl, arglist); } /* Fold function call to builtin floor, floorf or floorl. Return NULL_TREE if no simplification can be made. */ static tree -fold_builtin_floor (tree exp) +fold_builtin_floor (tree fndecl, tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) @@ -6546,7 +7138,7 @@ fold_builtin_floor (tree exp) x = TREE_REAL_CST (arg); if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) { - tree type = TREE_TYPE (exp); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); REAL_VALUE_TYPE r; real_floor (&r, TYPE_MODE (type), &x); @@ -6554,16 +7146,15 @@ fold_builtin_floor (tree exp) } } - return fold_trunc_transparent_mathfn (exp); + return fold_trunc_transparent_mathfn (fndecl, arglist); } /* Fold function call to builtin ceil, ceilf or ceill. Return NULL_TREE if no simplification can be made. */ static tree -fold_builtin_ceil (tree exp) +fold_builtin_ceil (tree fndecl, tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) @@ -6578,7 +7169,7 @@ fold_builtin_ceil (tree exp) x = TREE_REAL_CST (arg); if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) { - tree type = TREE_TYPE (exp); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); REAL_VALUE_TYPE r; real_ceil (&r, TYPE_MODE (type), &x); @@ -6586,16 +7177,15 @@ fold_builtin_ceil (tree exp) } } - return fold_trunc_transparent_mathfn (exp); + return fold_trunc_transparent_mathfn (fndecl, arglist); } /* Fold function call to builtin round, roundf or roundl. Return NULL_TREE if no simplification can be made. */ static tree -fold_builtin_round (tree exp) +fold_builtin_round (tree fndecl, tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) @@ -6610,7 +7200,7 @@ fold_builtin_round (tree exp) x = TREE_REAL_CST (arg); if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) { - tree type = TREE_TYPE (exp); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); REAL_VALUE_TYPE r; real_round (&r, TYPE_MODE (type), &x); @@ -6618,17 +7208,16 @@ fold_builtin_round (tree exp) } } - return fold_trunc_transparent_mathfn (exp); + return fold_trunc_transparent_mathfn (fndecl, arglist); } /* Fold function call to builtin lround, lroundf or lroundl (or the - corresponding long long versions). Return NULL_TREE if no - simplification can be made. */ + corresponding long long versions) and other rounding functions. + Return NULL_TREE if no simplification can be made. */ static tree -fold_builtin_lround (tree exp) +fold_builtin_int_roundingfn (tree fndecl, tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) @@ -6642,11 +7231,32 @@ fold_builtin_lround (tree exp) if (! REAL_VALUE_ISNAN (x) && ! REAL_VALUE_ISINF (x)) { - tree itype = TREE_TYPE (exp), ftype = TREE_TYPE (arg), result; + tree itype = TREE_TYPE (TREE_TYPE (fndecl)); + tree ftype = TREE_TYPE (arg), result; HOST_WIDE_INT hi, lo; REAL_VALUE_TYPE r; - real_round (&r, TYPE_MODE (ftype), &x); + switch (DECL_FUNCTION_CODE (fndecl)) + { + CASE_FLT_FN (BUILT_IN_LFLOOR): + CASE_FLT_FN (BUILT_IN_LLFLOOR): + real_floor (&r, TYPE_MODE (ftype), &x); + break; + + CASE_FLT_FN (BUILT_IN_LCEIL): + CASE_FLT_FN (BUILT_IN_LLCEIL): + real_ceil (&r, TYPE_MODE (ftype), &x); + break; + + CASE_FLT_FN (BUILT_IN_LROUND): + CASE_FLT_FN (BUILT_IN_LLROUND): + real_round (&r, TYPE_MODE (ftype), &x); + break; + + default: + gcc_unreachable (); + } + REAL_VALUE_TO_INT (&lo, &hi, r); result = build_int_cst_wide (NULL_TREE, lo, hi); if (int_fits_type_p (result, itype)) @@ -6654,7 +7264,7 @@ fold_builtin_lround (tree exp) } } - return fold_fixed_mathfn (exp); + return fold_fixed_mathfn (fndecl, arglist); } /* Fold function call to builtin ffs, clz, ctz, popcount and parity @@ -6662,10 +7272,8 @@ fold_builtin_lround (tree exp) Return NULL_TREE if no simplification can be made. */ static tree -fold_builtin_bitop (tree exp) +fold_builtin_bitop (tree fndecl, tree arglist) { - tree fndecl = get_callee_fndecl (exp); - tree arglist = TREE_OPERAND (exp, 1); tree arg; if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) @@ -6699,9 +7307,7 @@ fold_builtin_bitop (tree exp) switch (DECL_FUNCTION_CODE (fndecl)) { - case BUILT_IN_FFS: - case BUILT_IN_FFSL: - case BUILT_IN_FFSLL: + CASE_INT_FN (BUILT_IN_FFS): if (lo != 0) result = exact_log2 (lo & -lo) + 1; else if (hi != 0) @@ -6710,9 +7316,7 @@ fold_builtin_bitop (tree exp) result = 0; break; - case BUILT_IN_CLZ: - case BUILT_IN_CLZL: - case BUILT_IN_CLZLL: + CASE_INT_FN (BUILT_IN_CLZ): if (hi != 0) result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT; else if (lo != 0) @@ -6721,9 +7325,7 @@ fold_builtin_bitop (tree exp) result = width; break; - case BUILT_IN_CTZ: - case BUILT_IN_CTZL: - case BUILT_IN_CTZLL: + CASE_INT_FN (BUILT_IN_CTZ): if (lo != 0) result = exact_log2 (lo & -lo); else if (hi != 0) @@ -6732,9 +7334,7 @@ fold_builtin_bitop (tree exp) result = width; break; - case BUILT_IN_POPCOUNT: - case BUILT_IN_POPCOUNTL: - case BUILT_IN_POPCOUNTLL: + CASE_INT_FN (BUILT_IN_POPCOUNT): result = 0; while (lo) result++, lo &= lo - 1; @@ -6742,9 +7342,7 @@ fold_builtin_bitop (tree exp) result++, hi &= hi - 1; break; - case BUILT_IN_PARITY: - case BUILT_IN_PARITYL: - case BUILT_IN_PARITYLL: + CASE_INT_FN (BUILT_IN_PARITY): result = 0; while (lo) result++, lo &= lo - 1; @@ -6757,7 +7355,7 @@ fold_builtin_bitop (tree exp) gcc_unreachable (); } - return build_int_cst (TREE_TYPE (exp), result); + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), result); } return NULL_TREE; @@ -6829,49 +7427,35 @@ fold_builtin_logarithm (tree fndecl, tree arglist, switch (fcode) { - case BUILT_IN_EXP: - case BUILT_IN_EXPF: - case BUILT_IN_EXPL: + CASE_FLT_FN (BUILT_IN_EXP): /* Prepare to do logN(exp(exponent) -> exponent*logN(e). */ x = build_real (type, real_value_truncate (TYPE_MODE (type), dconste)); exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); break; - case BUILT_IN_EXP2: - case BUILT_IN_EXP2F: - case BUILT_IN_EXP2L: + CASE_FLT_FN (BUILT_IN_EXP2): /* Prepare to do logN(exp2(exponent) -> exponent*logN(2). */ x = build_real (type, dconst2); exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); break; - case BUILT_IN_EXP10: - case BUILT_IN_EXP10F: - case BUILT_IN_EXP10L: - case BUILT_IN_POW10: - case BUILT_IN_POW10F: - case BUILT_IN_POW10L: + CASE_FLT_FN (BUILT_IN_EXP10): + CASE_FLT_FN (BUILT_IN_POW10): /* Prepare to do logN(exp10(exponent) -> exponent*logN(10). */ x = build_real (type, dconst10); exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); break; - case BUILT_IN_SQRT: - case BUILT_IN_SQRTF: - case BUILT_IN_SQRTL: + CASE_FLT_FN (BUILT_IN_SQRT): /* Prepare to do logN(sqrt(x) -> 0.5*logN(x). */ x = TREE_VALUE (TREE_OPERAND (arg, 1)); exponent = build_real (type, dconsthalf); break; - case BUILT_IN_CBRT: - case BUILT_IN_CBRTF: - case BUILT_IN_CBRTL: + CASE_FLT_FN (BUILT_IN_CBRT): /* Prepare to do logN(cbrt(x) -> (1/3)*logN(x). */ x = TREE_VALUE (TREE_OPERAND (arg, 1)); exponent = build_real (type, real_value_truncate (TYPE_MODE (type), dconstthird)); break; - case BUILT_IN_POW: - case BUILT_IN_POWF: - case BUILT_IN_POWL: + CASE_FLT_FN (BUILT_IN_POW): /* Prepare to do logN(pow(x,exponent) -> exponent*logN(x). */ x = TREE_VALUE (TREE_OPERAND (arg, 1)); exponent = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); @@ -6886,7 +7470,7 @@ fold_builtin_logarithm (tree fndecl, tree arglist, tree logfn; arglist = build_tree_list (NULL_TREE, x); logfn = build_function_call_expr (fndecl, arglist); - return fold (build2 (MULT_EXPR, type, exponent, logfn)); + return fold_build2 (MULT_EXPR, type, exponent, logfn); } } } @@ -6929,8 +7513,8 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) /* Optimize pow(x,-1.0) = 1.0/x. */ if (REAL_VALUES_EQUAL (c, dconstm1)) - return fold (build2 (RDIV_EXPR, type, - build_real (type, dconst1), arg0)); + return fold_build2 (RDIV_EXPR, type, + build_real (type, dconst1), arg0); /* Optimize pow(x,0.5) = sqrt(x). */ if (flag_unsafe_math_optimizations @@ -6986,7 +7570,7 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) { tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0); tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1)); - arg = fold (build2 (MULT_EXPR, type, arg, arg1)); + arg = fold_build2 (MULT_EXPR, type, arg, arg1); arglist = build_tree_list (NULL_TREE, arg); return build_function_call_expr (expfn, arglist); } @@ -6995,8 +7579,8 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) if (BUILTIN_SQRT_P (fcode)) { tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1)); - tree narg1 = fold (build2 (MULT_EXPR, type, arg1, - build_real (type, dconsthalf))); + tree narg1 = fold_build2 (MULT_EXPR, type, arg1, + build_real (type, dconsthalf)); arglist = tree_cons (NULL_TREE, narg0, build_tree_list (NULL_TREE, narg1)); @@ -7011,8 +7595,8 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) { const REAL_VALUE_TYPE dconstroot = real_value_truncate (TYPE_MODE (type), dconstthird); - tree narg1 = fold (build2 (MULT_EXPR, type, arg1, - build_real (type, dconstroot))); + tree narg1 = fold_build2 (MULT_EXPR, type, arg1, + build_real (type, dconstroot)); arglist = tree_cons (NULL_TREE, arg, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (fndecl, arglist); @@ -7025,7 +7609,7 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) { tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1)); tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1))); - tree narg1 = fold (build2 (MULT_EXPR, type, arg01, arg1)); + tree narg1 = fold_build2 (MULT_EXPR, type, arg01, arg1); arglist = tree_cons (NULL_TREE, arg00, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (fndecl, arglist); @@ -7075,8 +7659,8 @@ fold_builtin_powi (tree fndecl ATTRIBUTE_UNUSED, tree arglist, tree type) /* Optimize pow(x,-1) = 1.0/x. */ if (c == -1) - return fold (build2 (RDIV_EXPR, type, - build_real (type, dconst1), arg0)); + return fold_build2 (RDIV_EXPR, type, + build_real (type, dconst1), arg0); } return NULL_TREE; @@ -7158,9 +7742,8 @@ fold_builtin_exponent (tree fndecl, tree arglist, NULL_TREE if no simplification can be made. */ static tree -fold_builtin_memcpy (tree exp) +fold_builtin_memcpy (tree fndecl, tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree dest, src, len; if (!validate_arglist (arglist, @@ -7173,11 +7756,11 @@ fold_builtin_memcpy (tree exp) /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) - return omit_one_operand (TREE_TYPE (exp), dest, src); + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); /* If SRC and DEST are the same (and not volatile), return DEST. */ if (operand_equal_p (src, dest, 0)) - return omit_one_operand (TREE_TYPE (exp), dest, len); + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); return 0; } @@ -7206,11 +7789,11 @@ fold_builtin_mempcpy (tree arglist, tree type, int endp) return omit_one_operand (type, dest, len); if (endp == 2) - len = fold (build2 (MINUS_EXPR, TREE_TYPE (len), len, - ssize_int (1))); + len = fold_build2 (MINUS_EXPR, TREE_TYPE (len), len, + ssize_int (1)); len = fold_convert (TREE_TYPE (dest), len); - len = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len)); + len = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len); return fold_convert (type, len); } } @@ -7249,9 +7832,8 @@ fold_builtin_memmove (tree arglist, tree type) simplification can be made. */ tree -fold_builtin_strcpy (tree exp, tree len) +fold_builtin_strcpy (tree fndecl, tree arglist, tree len) { - tree arglist = TREE_OPERAND (exp, 1); tree dest, src, fn; if (!validate_arglist (arglist, @@ -7263,7 +7845,7 @@ fold_builtin_strcpy (tree exp, tree len) /* If SRC and DEST are the same (and not volatile), return DEST. */ if (operand_equal_p (src, dest, 0)) - return fold_convert (TREE_TYPE (exp), dest); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest); if (optimize_size) return 0; @@ -7283,7 +7865,7 @@ fold_builtin_strcpy (tree exp, tree len) arglist = build_tree_list (NULL_TREE, len); arglist = tree_cons (NULL_TREE, src, arglist); arglist = tree_cons (NULL_TREE, dest, arglist); - return fold_convert (TREE_TYPE (exp), + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), build_function_call_expr (fn, arglist)); } @@ -7292,9 +7874,8 @@ fold_builtin_strcpy (tree exp, tree len) can be made. */ tree -fold_builtin_strncpy (tree exp, tree slen) +fold_builtin_strncpy (tree fndecl, tree arglist, tree slen) { - tree arglist = TREE_OPERAND (exp, 1); tree dest, src, len, fn; if (!validate_arglist (arglist, @@ -7307,7 +7888,7 @@ fold_builtin_strncpy (tree exp, tree slen) /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) - return omit_one_operand (TREE_TYPE (exp), dest, src); + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); /* We can't compare slen with len as constants below if len is not a constant. */ @@ -7333,7 +7914,7 @@ fold_builtin_strncpy (tree exp, tree slen) fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; - return fold_convert (TREE_TYPE (exp), + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), build_function_call_expr (fn, arglist)); } @@ -7387,7 +7968,9 @@ fold_builtin_memcmp (tree arglist) if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1) { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); - tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree cst_uchar_ptr_node + = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); + tree ind1 = fold_convert (integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, @@ -7396,7 +7979,7 @@ fold_builtin_memcmp (tree arglist) build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, arg2))); - return fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2)); + return fold_build2 (MINUS_EXPR, integer_type_node, ind1, ind2); } return 0; @@ -7439,7 +8022,9 @@ fold_builtin_strcmp (tree arglist) if (p2 && *p2 == '\0') { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); - tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree cst_uchar_ptr_node + = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); + return fold_convert (integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, @@ -7450,12 +8035,14 @@ fold_builtin_strcmp (tree arglist) if (p1 && *p1 == '\0') { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); - tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree cst_uchar_ptr_node + = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); + tree temp = fold_convert (integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, arg2))); - return fold (build1 (NEGATE_EXPR, integer_type_node, temp)); + return fold_build1 (NEGATE_EXPR, integer_type_node, temp); } return 0; @@ -7508,7 +8095,9 @@ fold_builtin_strncmp (tree arglist) && tree_int_cst_sgn (len) == 1) { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); - tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree cst_uchar_ptr_node + = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); + return fold_convert (integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, @@ -7522,12 +8111,14 @@ fold_builtin_strncmp (tree arglist) && tree_int_cst_sgn (len) == 1) { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); - tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree cst_uchar_ptr_node + = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); + tree temp = fold_convert (integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, arg2))); - return fold (build1 (NEGATE_EXPR, integer_type_node, temp)); + return fold_build1 (NEGATE_EXPR, integer_type_node, temp); } /* If len parameter is one, return an expression corresponding to @@ -7535,7 +8126,9 @@ fold_builtin_strncmp (tree arglist) if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1) { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); - tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree cst_uchar_ptr_node + = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true); + tree ind1 = fold_convert (integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, @@ -7544,7 +8137,7 @@ fold_builtin_strncmp (tree arglist) build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, arg2))); - return fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2)); + return fold_build2 (MINUS_EXPR, integer_type_node, ind1, ind2); } return 0; @@ -7554,9 +8147,9 @@ fold_builtin_strncmp (tree arglist) NULL_TREE if no simplification can be made. */ static tree -fold_builtin_signbit (tree exp) +fold_builtin_signbit (tree fndecl, tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); tree arg, temp; if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) @@ -7572,17 +8165,17 @@ fold_builtin_signbit (tree exp) c = TREE_REAL_CST (arg); temp = REAL_VALUE_NEGATIVE (c) ? integer_one_node : integer_zero_node; - return fold_convert (TREE_TYPE (exp), temp); + return fold_convert (type, temp); } /* If ARG is non-negative, the result is always zero. */ if (tree_expr_nonnegative_p (arg)) - return omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg); + return omit_one_operand (type, integer_zero_node, arg); /* If ARG's format doesn't have signed zeros, return "arg < 0.0". */ if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg)))) - return fold (build2 (LT_EXPR, TREE_TYPE (exp), arg, - build_real (TREE_TYPE (arg), dconst0))); + return fold_build2 (LT_EXPR, type, arg, + build_real (TREE_TYPE (arg), dconst0)); return NULL_TREE; } @@ -7615,16 +8208,16 @@ fold_builtin_copysign (tree fndecl, tree arglist, tree type) c1 = TREE_REAL_CST (arg1); c2 = TREE_REAL_CST (arg2); + /* c1.sign := c2.sign. */ real_copysign (&c1, &c2); return build_real (type, c1); - c1.sign = c2.sign; } /* copysign(X, Y) is fabs(X) when Y is always non-negative. Remember to evaluate Y for side-effects. */ if (tree_expr_nonnegative_p (arg2)) return omit_one_operand (type, - fold (build1 (ABS_EXPR, type, arg1)), + fold_build1 (ABS_EXPR, type, arg1), arg2); /* Strip sign changing operations for the first argument. */ @@ -7653,8 +8246,8 @@ fold_builtin_isascii (tree arglist) arg = build2 (BIT_AND_EXPR, integer_type_node, arg, build_int_cst (NULL_TREE, ~ (unsigned HOST_WIDE_INT) 0x7f)); - arg = fold (build2 (EQ_EXPR, integer_type_node, - arg, integer_zero_node)); + arg = fold_build2 (EQ_EXPR, integer_type_node, + arg, integer_zero_node); if (in_gimple_form && !TREE_CONSTANT (arg)) return NULL_TREE; @@ -7675,8 +8268,8 @@ fold_builtin_toascii (tree arglist) /* Transform toascii(c) -> (c & 0x7f). */ tree arg = TREE_VALUE (arglist); - return fold (build2 (BIT_AND_EXPR, integer_type_node, arg, - build_int_cst (NULL_TREE, 0x7f))); + return fold_build2 (BIT_AND_EXPR, integer_type_node, arg, + build_int_cst (NULL_TREE, 0x7f)); } } @@ -7702,9 +8295,8 @@ fold_builtin_isdigit (tree arglist) arg = fold_convert (unsigned_type_node, TREE_VALUE (arglist)); arg = build2 (MINUS_EXPR, unsigned_type_node, arg, build_int_cst (unsigned_type_node, target_digit0)); - arg = build2 (LE_EXPR, integer_type_node, arg, - build_int_cst (unsigned_type_node, 9)); - arg = fold (arg); + arg = fold_build2 (LE_EXPR, integer_type_node, arg, + build_int_cst (unsigned_type_node, 9)); if (in_gimple_form && !TREE_CONSTANT (arg)) return NULL_TREE; else @@ -7726,7 +8318,7 @@ fold_builtin_fabs (tree arglist, tree type) arg = fold_convert (type, arg); if (TREE_CODE (arg) == REAL_CST) return fold_abs_const (arg, type); - return fold (build1 (ABS_EXPR, type, arg)); + return fold_build1 (ABS_EXPR, type, arg); } /* Fold a call to abs, labs, llabs or imaxabs. */ @@ -7743,17 +8335,15 @@ fold_builtin_abs (tree arglist, tree type) arg = fold_convert (type, arg); if (TREE_CODE (arg) == INTEGER_CST) return fold_abs_const (arg, type); - return fold (build1 (ABS_EXPR, type, arg)); + return fold_build1 (ABS_EXPR, type, arg); } /* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite. EXP is the CALL_EXPR for the call. */ static tree -fold_builtin_classify (tree exp, int builtin_index) +fold_builtin_classify (tree fndecl, tree arglist, int builtin_index) { - tree fndecl = get_callee_fndecl (exp); - tree arglist = TREE_OPERAND (exp, 1); tree type = TREE_TYPE (TREE_TYPE (fndecl)); tree arg; REAL_VALUE_TYPE r; @@ -7825,7 +8415,7 @@ fold_builtin_classify (tree exp, int builtin_index) } arg = builtin_save_expr (arg); - return fold (build2 (UNORDERED_EXPR, type, arg, arg)); + return fold_build2 (UNORDERED_EXPR, type, arg, arg); default: gcc_unreachable (); @@ -7900,51 +8490,13 @@ fold_builtin_unordered_cmp (tree fndecl, tree arglist, { if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0)))) return omit_two_operands (type, integer_zero_node, arg0, arg1); - return fold (build2 (UNORDERED_EXPR, type, arg0, arg1)); + return fold_build2 (UNORDERED_EXPR, type, arg0, arg1); } code = MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0))) ? unordered_code : ordered_code; - return fold (build1 (TRUTH_NOT_EXPR, type, - fold (build2 (code, type, arg0, arg1)))); -} - -/* Fold a call to one of the external complex multiply libcalls. */ - -static tree -fold_builtin_complex_mul (tree type, tree arglist) -{ - tree ar, ai, br, bi; - - if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, REAL_TYPE, - REAL_TYPE, VOID_TYPE)) - return NULL; - - ar = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist); - ai = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist); - br = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist); - bi = TREE_VALUE (arglist); - - return fold_complex_mult_parts (type, ar, ai, br, bi); -} - -/* Fold a call to one of the external complex division libcalls. */ - -static tree -fold_builtin_complex_div (tree type, tree arglist) -{ - tree ar, ai, br, bi; - - if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, REAL_TYPE, - REAL_TYPE, VOID_TYPE)) - return NULL; - - ar = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist); - ai = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist); - br = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist); - bi = TREE_VALUE (arglist); - - return fold_complex_div_parts (type, ar, ai, br, bi, RDIV_EXPR); + return fold_build1 (TRUTH_NOT_EXPR, type, + fold_build2 (code, type, arg0, arg1)); } /* Used by constant folding to simplify calls to builtin functions. EXP is @@ -7953,15 +8505,13 @@ fold_builtin_complex_div (tree type, tree arglist) if no simplification was possible. */ static tree -fold_builtin_1 (tree exp, bool ignore) +fold_builtin_1 (tree fndecl, tree arglist, bool ignore) { - tree fndecl = get_callee_fndecl (exp); - tree arglist = TREE_OPERAND (exp, 1); tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode; if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) - return targetm.fold_builtin (exp, ignore); + return targetm.fold_builtin (fndecl, arglist, ignore); fcode = DECL_FUNCTION_CODE (fndecl); switch (fcode) @@ -7996,10 +8546,10 @@ fold_builtin_1 (tree exp, bool ignore) return fold_builtin_strrchr (arglist, type); case BUILT_IN_STRCPY: - return fold_builtin_strcpy (exp, NULL_TREE); + return fold_builtin_strcpy (fndecl, arglist, NULL_TREE); case BUILT_IN_STRNCPY: - return fold_builtin_strncpy (exp, NULL_TREE); + return fold_builtin_strncpy (fndecl, arglist, NULL_TREE); case BUILT_IN_STRCMP: return fold_builtin_strcmp (arglist); @@ -8040,9 +8590,7 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_STRLEN: return fold_builtin_strlen (arglist); - case BUILT_IN_FABS: - case BUILT_IN_FABSF: - case BUILT_IN_FABSL: + CASE_FLT_FN (BUILT_IN_FABS): return fold_builtin_fabs (arglist, type); case BUILT_IN_ABS: @@ -8051,190 +8599,124 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_IMAXABS: return fold_builtin_abs (arglist, type); - case BUILT_IN_CONJ: - case BUILT_IN_CONJF: - case BUILT_IN_CONJL: + CASE_FLT_FN (BUILT_IN_CONJ): if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) - return fold (build1 (CONJ_EXPR, type, TREE_VALUE (arglist))); + return fold_build1 (CONJ_EXPR, type, TREE_VALUE (arglist)); break; - case BUILT_IN_CREAL: - case BUILT_IN_CREALF: - case BUILT_IN_CREALL: + CASE_FLT_FN (BUILT_IN_CREAL): if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) - return non_lvalue (fold (build1 (REALPART_EXPR, type, - TREE_VALUE (arglist)))); + return non_lvalue (fold_build1 (REALPART_EXPR, type, + TREE_VALUE (arglist))); break; - case BUILT_IN_CIMAG: - case BUILT_IN_CIMAGF: - case BUILT_IN_CIMAGL: + CASE_FLT_FN (BUILT_IN_CIMAG): if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) - return non_lvalue (fold (build1 (IMAGPART_EXPR, type, - TREE_VALUE (arglist)))); + return non_lvalue (fold_build1 (IMAGPART_EXPR, type, + TREE_VALUE (arglist))); break; - case BUILT_IN_CABS: - case BUILT_IN_CABSF: - case BUILT_IN_CABSL: + CASE_FLT_FN (BUILT_IN_CABS): return fold_builtin_cabs (arglist, type); - case BUILT_IN_SQRT: - case BUILT_IN_SQRTF: - case BUILT_IN_SQRTL: + CASE_FLT_FN (BUILT_IN_SQRT): return fold_builtin_sqrt (arglist, type); - case BUILT_IN_CBRT: - case BUILT_IN_CBRTF: - case BUILT_IN_CBRTL: + CASE_FLT_FN (BUILT_IN_CBRT): return fold_builtin_cbrt (arglist, type); - case BUILT_IN_SIN: - case BUILT_IN_SINF: - case BUILT_IN_SINL: + CASE_FLT_FN (BUILT_IN_SIN): return fold_builtin_sin (arglist); - case BUILT_IN_COS: - case BUILT_IN_COSF: - case BUILT_IN_COSL: + CASE_FLT_FN (BUILT_IN_COS): return fold_builtin_cos (arglist, type, fndecl); - case BUILT_IN_EXP: - case BUILT_IN_EXPF: - case BUILT_IN_EXPL: + CASE_FLT_FN (BUILT_IN_EXP): return fold_builtin_exponent (fndecl, arglist, &dconste); - case BUILT_IN_EXP2: - case BUILT_IN_EXP2F: - case BUILT_IN_EXP2L: + CASE_FLT_FN (BUILT_IN_EXP2): return fold_builtin_exponent (fndecl, arglist, &dconst2); - case BUILT_IN_EXP10: - case BUILT_IN_EXP10F: - case BUILT_IN_EXP10L: - case BUILT_IN_POW10: - case BUILT_IN_POW10F: - case BUILT_IN_POW10L: + CASE_FLT_FN (BUILT_IN_EXP10): + CASE_FLT_FN (BUILT_IN_POW10): return fold_builtin_exponent (fndecl, arglist, &dconst10); - case BUILT_IN_LOG: - case BUILT_IN_LOGF: - case BUILT_IN_LOGL: + CASE_FLT_FN (BUILT_IN_LOG): return fold_builtin_logarithm (fndecl, arglist, &dconste); - case BUILT_IN_LOG2: - case BUILT_IN_LOG2F: - case BUILT_IN_LOG2L: + CASE_FLT_FN (BUILT_IN_LOG2): return fold_builtin_logarithm (fndecl, arglist, &dconst2); - case BUILT_IN_LOG10: - case BUILT_IN_LOG10F: - case BUILT_IN_LOG10L: + CASE_FLT_FN (BUILT_IN_LOG10): return fold_builtin_logarithm (fndecl, arglist, &dconst10); - case BUILT_IN_TAN: - case BUILT_IN_TANF: - case BUILT_IN_TANL: + CASE_FLT_FN (BUILT_IN_TAN): return fold_builtin_tan (arglist); - case BUILT_IN_ATAN: - case BUILT_IN_ATANF: - case BUILT_IN_ATANL: + CASE_FLT_FN (BUILT_IN_ATAN): return fold_builtin_atan (arglist, type); - case BUILT_IN_POW: - case BUILT_IN_POWF: - case BUILT_IN_POWL: + CASE_FLT_FN (BUILT_IN_POW): return fold_builtin_pow (fndecl, arglist, type); - case BUILT_IN_POWI: - case BUILT_IN_POWIF: - case BUILT_IN_POWIL: + CASE_FLT_FN (BUILT_IN_POWI): return fold_builtin_powi (fndecl, arglist, type); - case BUILT_IN_INF: - case BUILT_IN_INFF: - case BUILT_IN_INFL: + CASE_FLT_FN (BUILT_IN_INF): + case BUILT_IN_INFD32: + case BUILT_IN_INFD64: + case BUILT_IN_INFD128: return fold_builtin_inf (type, true); - case BUILT_IN_HUGE_VAL: - case BUILT_IN_HUGE_VALF: - case BUILT_IN_HUGE_VALL: + CASE_FLT_FN (BUILT_IN_HUGE_VAL): return fold_builtin_inf (type, false); - case BUILT_IN_NAN: - case BUILT_IN_NANF: - case BUILT_IN_NANL: + CASE_FLT_FN (BUILT_IN_NAN): + case BUILT_IN_NAND32: + case BUILT_IN_NAND64: + case BUILT_IN_NAND128: return fold_builtin_nan (arglist, type, true); - case BUILT_IN_NANS: - case BUILT_IN_NANSF: - case BUILT_IN_NANSL: + CASE_FLT_FN (BUILT_IN_NANS): return fold_builtin_nan (arglist, type, false); - case BUILT_IN_FLOOR: - case BUILT_IN_FLOORF: - case BUILT_IN_FLOORL: - return fold_builtin_floor (exp); - - case BUILT_IN_CEIL: - case BUILT_IN_CEILF: - case BUILT_IN_CEILL: - return fold_builtin_ceil (exp); - - case BUILT_IN_TRUNC: - case BUILT_IN_TRUNCF: - case BUILT_IN_TRUNCL: - return fold_builtin_trunc (exp); - - case BUILT_IN_ROUND: - case BUILT_IN_ROUNDF: - case BUILT_IN_ROUNDL: - return fold_builtin_round (exp); - - case BUILT_IN_NEARBYINT: - case BUILT_IN_NEARBYINTF: - case BUILT_IN_NEARBYINTL: - case BUILT_IN_RINT: - case BUILT_IN_RINTF: - case BUILT_IN_RINTL: - return fold_trunc_transparent_mathfn (exp); - - case BUILT_IN_LROUND: - case BUILT_IN_LROUNDF: - case BUILT_IN_LROUNDL: - case BUILT_IN_LLROUND: - case BUILT_IN_LLROUNDF: - case BUILT_IN_LLROUNDL: - return fold_builtin_lround (exp); - - case BUILT_IN_LRINT: - case BUILT_IN_LRINTF: - case BUILT_IN_LRINTL: - case BUILT_IN_LLRINT: - case BUILT_IN_LLRINTF: - case BUILT_IN_LLRINTL: - return fold_fixed_mathfn (exp); - - case BUILT_IN_FFS: - case BUILT_IN_FFSL: - case BUILT_IN_FFSLL: - case BUILT_IN_CLZ: - case BUILT_IN_CLZL: - case BUILT_IN_CLZLL: - case BUILT_IN_CTZ: - case BUILT_IN_CTZL: - case BUILT_IN_CTZLL: - case BUILT_IN_POPCOUNT: - case BUILT_IN_POPCOUNTL: - case BUILT_IN_POPCOUNTLL: - case BUILT_IN_PARITY: - case BUILT_IN_PARITYL: - case BUILT_IN_PARITYLL: - return fold_builtin_bitop (exp); + CASE_FLT_FN (BUILT_IN_FLOOR): + return fold_builtin_floor (fndecl, arglist); + + CASE_FLT_FN (BUILT_IN_CEIL): + return fold_builtin_ceil (fndecl, arglist); + + CASE_FLT_FN (BUILT_IN_TRUNC): + return fold_builtin_trunc (fndecl, arglist); + + CASE_FLT_FN (BUILT_IN_ROUND): + return fold_builtin_round (fndecl, arglist); + + CASE_FLT_FN (BUILT_IN_NEARBYINT): + CASE_FLT_FN (BUILT_IN_RINT): + return fold_trunc_transparent_mathfn (fndecl, arglist); + + CASE_FLT_FN (BUILT_IN_LCEIL): + CASE_FLT_FN (BUILT_IN_LLCEIL): + CASE_FLT_FN (BUILT_IN_LFLOOR): + CASE_FLT_FN (BUILT_IN_LLFLOOR): + CASE_FLT_FN (BUILT_IN_LROUND): + CASE_FLT_FN (BUILT_IN_LLROUND): + return fold_builtin_int_roundingfn (fndecl, arglist); + + CASE_FLT_FN (BUILT_IN_LRINT): + CASE_FLT_FN (BUILT_IN_LLRINT): + return fold_fixed_mathfn (fndecl, arglist); + + CASE_INT_FN (BUILT_IN_FFS): + CASE_INT_FN (BUILT_IN_CLZ): + CASE_INT_FN (BUILT_IN_CTZ): + CASE_INT_FN (BUILT_IN_POPCOUNT): + CASE_INT_FN (BUILT_IN_PARITY): + return fold_builtin_bitop (fndecl, arglist); case BUILT_IN_MEMCPY: - return fold_builtin_memcpy (exp); + return fold_builtin_memcpy (fndecl, arglist); case BUILT_IN_MEMPCPY: return fold_builtin_mempcpy (arglist, type, /*endp=*/1); @@ -8242,10 +8724,8 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_MEMMOVE: return fold_builtin_memmove (arglist, type); - case BUILT_IN_SIGNBIT: - case BUILT_IN_SIGNBITF: - case BUILT_IN_SIGNBITL: - return fold_builtin_signbit (exp); + CASE_FLT_FN (BUILT_IN_SIGNBIT): + return fold_builtin_signbit (fndecl, arglist); case BUILT_IN_ISASCII: return fold_builtin_isascii (arglist); @@ -8256,25 +8736,26 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_ISDIGIT: return fold_builtin_isdigit (arglist); - case BUILT_IN_COPYSIGN: - case BUILT_IN_COPYSIGNF: - case BUILT_IN_COPYSIGNL: + CASE_FLT_FN (BUILT_IN_COPYSIGN): return fold_builtin_copysign (fndecl, arglist, type); - case BUILT_IN_FINITE: - case BUILT_IN_FINITEF: - case BUILT_IN_FINITEL: - return fold_builtin_classify (exp, BUILT_IN_FINITE); + CASE_FLT_FN (BUILT_IN_FINITE): + case BUILT_IN_FINITED32: + case BUILT_IN_FINITED64: + case BUILT_IN_FINITED128: + return fold_builtin_classify (fndecl, arglist, BUILT_IN_FINITE); - case BUILT_IN_ISINF: - case BUILT_IN_ISINFF: - case BUILT_IN_ISINFL: - return fold_builtin_classify (exp, BUILT_IN_ISINF); + CASE_FLT_FN (BUILT_IN_ISINF): + case BUILT_IN_ISINFD32: + case BUILT_IN_ISINFD64: + case BUILT_IN_ISINFD128: + return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISINF); - case BUILT_IN_ISNAN: - case BUILT_IN_ISNANF: - case BUILT_IN_ISNANL: - return fold_builtin_classify (exp, BUILT_IN_ISNAN); + CASE_FLT_FN (BUILT_IN_ISNAN): + case BUILT_IN_ISNAND32: + case BUILT_IN_ISNAND64: + case BUILT_IN_ISNAND128: + return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISNAN); case BUILT_IN_ISGREATER: return fold_builtin_unordered_cmp (fndecl, arglist, UNLE_EXPR, LE_EXPR); @@ -8294,15 +8775,51 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_VA_START: break; - default: - if (fcode >= BUILT_IN_COMPLEX_MUL_MIN - && fcode <= BUILT_IN_COMPLEX_MUL_MAX) - return fold_builtin_complex_mul (type, arglist); - if (fcode >= BUILT_IN_COMPLEX_DIV_MIN - && fcode <= BUILT_IN_COMPLEX_DIV_MAX) - return fold_builtin_complex_div (type, arglist); - break; - } + case BUILT_IN_OBJECT_SIZE: + return fold_builtin_object_size (arglist); + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + return fold_builtin_memory_chk (fndecl, arglist, NULL_TREE, ignore, + DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + return fold_builtin_stxcpy_chk (fndecl, arglist, NULL_TREE, ignore, + DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_STRNCPY_CHK: + return fold_builtin_strncpy_chk (arglist, NULL_TREE); + case BUILT_IN_STRCAT_CHK: + return fold_builtin_strcat_chk (fndecl, arglist); + case BUILT_IN_STRNCAT_CHK: + return fold_builtin_strncat_chk (fndecl, arglist); + case BUILT_IN_SPRINTF_CHK: + case BUILT_IN_VSPRINTF_CHK: + return fold_builtin_sprintf_chk (arglist, DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + return fold_builtin_snprintf_chk (arglist, NULL_TREE, + DECL_FUNCTION_CODE (fndecl)); + + case BUILT_IN_PRINTF: + case BUILT_IN_PRINTF_UNLOCKED: + case BUILT_IN_VPRINTF: + case BUILT_IN_PRINTF_CHK: + case BUILT_IN_VPRINTF_CHK: + return fold_builtin_printf (fndecl, arglist, ignore, + DECL_FUNCTION_CODE (fndecl)); + + case BUILT_IN_FPRINTF: + case BUILT_IN_FPRINTF_UNLOCKED: + case BUILT_IN_VFPRINTF: + case BUILT_IN_FPRINTF_CHK: + case BUILT_IN_VFPRINTF_CHK: + return fold_builtin_fprintf (fndecl, arglist, ignore, + DECL_FUNCTION_CODE (fndecl)); + + default: + break; + } return 0; } @@ -8312,14 +8829,12 @@ fold_builtin_1 (tree exp, bool ignore) call node earlier than the warning is generated. */ tree -fold_builtin (tree exp, bool ignore) +fold_builtin (tree fndecl, tree arglist, bool ignore) { - exp = fold_builtin_1 (exp, ignore); + tree exp = fold_builtin_1 (fndecl, arglist, ignore); if (exp) { - /* ??? Don't clobber shared nodes such as integer_zero_node. */ - if (CONSTANT_CLASS_P (exp)) - exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp); + exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp); TREE_NO_WARNING (exp) = 1; } @@ -8334,9 +8849,8 @@ build_function_call_expr (tree fn, tree arglist) tree call_expr; call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); - call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), + return fold_build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), call_expr, arglist, NULL_TREE); - return fold (call_expr); } /* This function validates the types of a function call argument list @@ -8406,6 +8920,18 @@ default_expand_builtin (tree exp ATTRIBUTE_UNUSED, return NULL_RTX; } +/* Default target-specific library builtin expander that does nothing. */ + +rtx +default_expand_library_builtin (tree exp ATTRIBUTE_UNUSED, + rtx target ATTRIBUTE_UNUSED, + rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED) +{ + return NULL_RTX; +} + /* Returns true is EXP represents data that would potentially reside in a readonly section. */ @@ -8474,13 +9000,15 @@ fold_builtin_strstr (tree arglist, tree type) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), - s1, build_int_cst (TREE_TYPE (s1), r - p1))); + tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, build_int_cst (TREE_TYPE (s1), r - p1)); return fold_convert (type, tem); } + /* The argument is const char *, and the result is char *, so we need + a type conversion here to avoid a warning. */ if (p2[0] == '\0') - return s1; + return fold_convert (type, s1); if (p2[1] != '\0') return 0; @@ -8544,8 +9072,8 @@ fold_builtin_strchr (tree arglist, tree type) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), - s1, build_int_cst (TREE_TYPE (s1), r - p1))); + tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, build_int_cst (TREE_TYPE (s1), r - p1)); return fold_convert (type, tem); } return 0; @@ -8599,8 +9127,8 @@ fold_builtin_strrchr (tree arglist, tree type) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), - s1, build_int_cst (TREE_TYPE (s1), r - p1))); + tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, build_int_cst (TREE_TYPE (s1), r - p1)); return fold_convert (type, tem); } @@ -8658,8 +9186,8 @@ fold_builtin_strpbrk (tree arglist, tree type) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), - s1, build_int_cst (TREE_TYPE (s1), r - p1))); + tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, build_int_cst (TREE_TYPE (s1), r - p1)); return fold_convert (type, tem); } @@ -8751,7 +9279,7 @@ fold_builtin_strncat (tree arglist) const char *p = c_getstr (src); /* If the requested length is zero, or the src parameter string - length is zero, return the dst parameter. */ + length is zero, return the dst parameter. */ if (integer_zerop (len) || (p && *p == '\0')) return omit_two_operands (TREE_TYPE (dst), dst, src, len); @@ -8889,14 +9417,15 @@ tree fold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len) { tree fn; - tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED] + /* If we're using an unlocked function, assume the other unlocked + functions exist explicitly. */ + tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED] : implicit_built_in_decls[BUILT_IN_FPUTC]; - tree fn_fwrite = unlocked ? implicit_built_in_decls[BUILT_IN_FWRITE_UNLOCKED] + tree const fn_fwrite = unlocked ? built_in_decls[BUILT_IN_FWRITE_UNLOCKED] : implicit_built_in_decls[BUILT_IN_FWRITE]; - /* If the return value is used, or the replacement _DECL isn't - initialized, don't do the transformation. */ - if (!ignore || !fn_fputc || !fn_fwrite) + /* If the return value is used, don't do the transformation. */ + if (!ignore) return 0; /* Verify the arguments in the original call. */ @@ -8958,6 +9487,11 @@ fold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len) gcc_unreachable (); } + /* If the replacement _DECL isn't initialized, don't do the + transformation. */ + if (!fn) + return 0; + /* These optimizations are only performed when the result is ignored, hence there's no need to cast the result to integer_type_node. */ return build_function_call_expr (fn, arglist); @@ -8982,7 +9516,7 @@ fold_builtin_next_arg (tree arglist) { /* Evidently an out of date version of ; can't validate va_start's second argument, but can still work as intended. */ - warning ("%<__builtin_next_arg%> called without an argument"); + warning (0, "%<__builtin_next_arg%> called without an argument"); return true; } /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0) @@ -9017,7 +9551,7 @@ fold_builtin_next_arg (tree arglist) argument. We just warn and set the arg to be the last argument so that we will get wrong-code because of it. */ - warning ("second parameter of % not last named argument"); + warning (0, "second parameter of % not last named argument"); } /* We want to verify the second parameter just once before the tree optimizers are run and then avoid keeping it in the tree, @@ -9048,7 +9582,7 @@ fold_builtin_sprintf (tree arglist, int ignored) 'sprintf (dest, "%s", orig)'. */ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE) && !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE, - VOID_TYPE)) + VOID_TYPE)) return NULL_TREE; /* Get the destination string and the format specifier. */ @@ -9063,8 +9597,11 @@ fold_builtin_sprintf (tree arglist, int ignored) call = NULL_TREE; retval = NULL_TREE; + if (!init_target_chars()) + return 0; + /* If the format doesn't contain % args or %%, use strcpy. */ - if (strchr (fmt_str, '%') == NULL) + if (strchr (fmt_str, target_percent) == NULL) { tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; @@ -9081,7 +9618,7 @@ fold_builtin_sprintf (tree arglist, int ignored) } /* If the format is "%s", use strcpy if the result isn't used. */ - else if (fmt_str && strcmp (fmt_str, "%s") == 0) + else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0) { tree fn, orig; fn = implicit_built_in_decls[BUILT_IN_STRCPY]; @@ -9112,3 +9649,1201 @@ fold_builtin_sprintf (tree arglist, int ignored) else return call; } + +/* Expand a call to __builtin_object_size. */ + +rtx +expand_builtin_object_size (tree exp) +{ + tree ost; + int object_size_type; + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + location_t locus = EXPR_LOCATION (exp); + + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + { + error ("%Hfirst argument of %D must be a pointer, second integer constant", + &locus, fndecl); + expand_builtin_trap (); + return const0_rtx; + } + + ost = TREE_VALUE (TREE_CHAIN (arglist)); + STRIP_NOPS (ost); + + if (TREE_CODE (ost) != INTEGER_CST + || tree_int_cst_sgn (ost) < 0 + || compare_tree_int (ost, 3) > 0) + { + error ("%Hlast argument of %D is not integer constant between 0 and 3", + &locus, fndecl); + expand_builtin_trap (); + return const0_rtx; + } + + object_size_type = tree_low_cst (ost, 0); + + return object_size_type < 2 ? constm1_rtx : const0_rtx; +} + +/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin. + FCODE is the BUILT_IN_* to use. + Return 0 if we failed; the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in + mode MODE if that's convenient). */ + +static rtx +expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, + enum built_in_function fcode) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, src, len, size; + + if (!validate_arglist (arglist, + POINTER_TYPE, + fcode == BUILT_IN_MEMSET_CHK + ? INTEGER_TYPE : POINTER_TYPE, + INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + if (! host_integerp (size, 1)) + return 0; + + if (host_integerp (len, 1) || integer_all_onesp (size)) + { + tree fn; + + if (! integer_all_onesp (size) && tree_int_cst_lt (size, len)) + { + location_t locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + fn = NULL_TREE; + /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume + mem{cpy,pcpy,move,set} is available. */ + switch (fcode) + { + case BUILT_IN_MEMCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMCPY]; + break; + case BUILT_IN_MEMPCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMPCPY]; + break; + case BUILT_IN_MEMMOVE_CHK: + fn = built_in_decls[BUILT_IN_MEMMOVE]; + break; + case BUILT_IN_MEMSET_CHK: + fn = built_in_decls[BUILT_IN_MEMSET]; + break; + default: + break; + } + + if (! fn) + return 0; + + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); + } + else if (fcode == BUILT_IN_MEMSET_CHK) + return 0; + else + { + unsigned int dest_align + = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + + /* If DEST is not a pointer type, call the normal function. */ + if (dest_align == 0) + return 0; + + /* If SRC and DEST are the same (and not volatile), do nothing. */ + if (operand_equal_p (src, dest, 0)) + { + tree expr; + + if (fcode != BUILT_IN_MEMPCPY_CHK) + { + /* Evaluate and ignore LEN in case it has side-effects. */ + expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); + return expand_expr (dest, target, mode, EXPAND_NORMAL); + } + + len = fold_convert (TREE_TYPE (dest), len); + expr = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len); + return expand_expr (expr, target, mode, EXPAND_NORMAL); + } + + /* __memmove_chk special case. */ + if (fcode == BUILT_IN_MEMMOVE_CHK) + { + unsigned int src_align + = get_pointer_alignment (src, BIGGEST_ALIGNMENT); + + if (src_align == 0) + return 0; + + /* If src is categorized for a readonly section we can use + normal __memcpy_chk. */ + if (readonly_data_expr (src)) + { + tree fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); + } + } + return 0; + } +} + +/* Emit warning if a buffer overflow is detected at compile time. */ + +static void +maybe_emit_chk_warning (tree exp, enum built_in_function fcode) +{ + int arg_mask, is_strlen = 0; + tree arglist = TREE_OPERAND (exp, 1), a; + tree len, size; + location_t locus; + + switch (fcode) + { + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + /* For __strcat_chk the warning will be emitted only if overflowing + by at least strlen (dest) + 1 bytes. */ + case BUILT_IN_STRCAT_CHK: + arg_mask = 6; + is_strlen = 1; + break; + case BUILT_IN_STRNCPY_CHK: + arg_mask = 12; + break; + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + arg_mask = 10; + break; + default: + gcc_unreachable (); + } + + len = NULL_TREE; + size = NULL_TREE; + for (a = arglist; a && arg_mask; a = TREE_CHAIN (a), arg_mask >>= 1) + if (arg_mask & 1) + { + if (len) + size = a; + else + len = a; + } + + if (!len || !size) + return; + + len = TREE_VALUE (len); + size = TREE_VALUE (size); + + if (! host_integerp (size, 1) || integer_all_onesp (size)) + return; + + if (is_strlen) + { + len = c_strlen (len, 1); + if (! len || ! host_integerp (len, 1) || tree_int_cst_lt (len, size)) + return; + } + else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len)) + return; + + locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); +} + +/* Emit warning if a buffer overflow is detected at compile time + in __sprintf_chk/__vsprintf_chk calls. */ + +static void +maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, size, len, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return; + dest = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + flag = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + size = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + fmt = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1) || integer_all_onesp (size)) + return; + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return; + + if (!init_target_chars()) + return; + + /* If the format doesn't contain % args or %%, we know its size. */ + if (strchr (fmt_str, target_percent) == 0) + len = build_int_cstu (size_type_node, strlen (fmt_str)); + /* If the format is "%s" and first ... argument is a string literal, + we know it too. */ + else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, target_percent_s) == 0) + { + tree arg; + + if (! arglist) + return; + arg = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (arg))) + return; + + len = c_strlen (arg, 1); + if (!len || ! host_integerp (len, 1)) + return; + } + else + return; + + if (! tree_int_cst_lt (len, size)) + { + location_t locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); + } +} + +/* Fold a call to __builtin_object_size, if possible. */ + +tree +fold_builtin_object_size (tree arglist) +{ + tree ptr, ost, ret = 0; + int object_size_type; + + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + ptr = TREE_VALUE (arglist); + ost = TREE_VALUE (TREE_CHAIN (arglist)); + STRIP_NOPS (ost); + + if (TREE_CODE (ost) != INTEGER_CST + || tree_int_cst_sgn (ost) < 0 + || compare_tree_int (ost, 3) > 0) + return 0; + + object_size_type = tree_low_cst (ost, 0); + + /* __builtin_object_size doesn't evaluate side-effects in its arguments; + if there are any side-effects, it returns (size_t) -1 for types 0 and 1 + and (size_t) 0 for types 2 and 3. */ + if (TREE_SIDE_EFFECTS (ptr)) + return fold_convert (size_type_node, + object_size_type < 2 + ? integer_minus_one_node : integer_zero_node); + + if (TREE_CODE (ptr) == ADDR_EXPR) + ret = build_int_cstu (size_type_node, + compute_builtin_object_size (ptr, object_size_type)); + + else if (TREE_CODE (ptr) == SSA_NAME) + { + unsigned HOST_WIDE_INT bytes; + + /* If object size is not known yet, delay folding until + later. Maybe subsequent passes will help determining + it. */ + bytes = compute_builtin_object_size (ptr, object_size_type); + if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 + ? -1 : 0)) + ret = build_int_cstu (size_type_node, bytes); + } + + if (ret) + { + ret = force_fit_type (ret, -1, false, false); + if (TREE_CONSTANT_OVERFLOW (ret)) + ret = 0; + } + + return ret; +} + +/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin. + IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* + code of the builtin. If MAXLEN is not NULL, it is maximum length + passed as third argument. */ + +tree +fold_builtin_memory_chk (tree fndecl, tree arglist, tree maxlen, bool ignore, + enum built_in_function fcode) +{ + tree dest, src, len, size, fn; + + if (!validate_arglist (arglist, + POINTER_TYPE, + fcode == BUILT_IN_MEMSET_CHK + ? INTEGER_TYPE : POINTER_TYPE, + INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + /* Actually val for __memset_chk, but it doesn't matter. */ + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + /* If SRC and DEST are the same (and not volatile), return DEST + (resp. DEST+LEN for __mempcpy_chk). */ + if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0)) + { + if (fcode != BUILT_IN_MEMPCPY_CHK) + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); + else + { + tree temp = fold_convert (TREE_TYPE (dest), len); + temp = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), temp); + } + } + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + { + if (fcode == BUILT_IN_MEMPCPY_CHK && ignore) + { + /* (void) __mempcpy_chk () can be optimized into + (void) __memcpy_chk (). */ + fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); + } + return 0; + } + } + else + maxlen = len; + + if (tree_int_cst_lt (size, maxlen)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + fn = NULL_TREE; + /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume + mem{cpy,pcpy,move,set} is available. */ + switch (fcode) + { + case BUILT_IN_MEMCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMCPY]; + break; + case BUILT_IN_MEMPCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMPCPY]; + break; + case BUILT_IN_MEMMOVE_CHK: + fn = built_in_decls[BUILT_IN_MEMMOVE]; + break; + case BUILT_IN_MEMSET_CHK: + fn = built_in_decls[BUILT_IN_MEMSET]; + break; + default: + break; + } + + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __st[rp]cpy_chk builtin. + IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* + code of the builtin. If MAXLEN is not NULL, it is maximum length of + strings passed as second argument. */ + +tree +fold_builtin_stxcpy_chk (tree fndecl, tree arglist, tree maxlen, bool ignore, + enum built_in_function fcode) +{ + tree dest, src, size, len, fn; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* If SRC and DEST are the same (and not volatile), return DEST. */ + if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0)) + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + len = c_strlen (src, 1); + if (! len || ! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + { + if (fcode == BUILT_IN_STPCPY_CHK) + { + if (! ignore) + return 0; + + /* If return value of __stpcpy_chk is ignored, + optimize into __strcpy_chk. */ + fn = built_in_decls[BUILT_IN_STRCPY_CHK]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); + } + + if (! len || TREE_SIDE_EFFECTS (len)) + return 0; + + /* If c_strlen returned something, but not a constant, + transform __strcpy_chk into __memcpy_chk. */ + fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + + len = size_binop (PLUS_EXPR, len, ssize_int (1)); + arglist = build_tree_list (NULL_TREE, size); + arglist = tree_cons (NULL_TREE, len, arglist); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), + build_function_call_expr (fn, arglist)); + } + } + else + maxlen = len; + + if (! tree_int_cst_lt (maxlen, size)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, src); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */ + fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK + ? BUILT_IN_STPCPY : BUILT_IN_STRCPY]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strncpy_chk builtin. + If MAXLEN is not NULL, it is maximum length passed as third argument. */ + +tree +fold_builtin_strncpy_chk (tree arglist, tree maxlen) +{ + tree dest, src, size, len, fn; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + return 0; + } + else + maxlen = len; + + if (tree_int_cst_lt (size, maxlen)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strncpy_chk is used, assume strncpy is available. */ + fn = built_in_decls[BUILT_IN_STRNCPY]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strcat_chk builtin FNDECL with ARGLIST. */ + +static tree +fold_builtin_strcat_chk (tree fndecl, tree arglist) +{ + tree dest, src, size, fn; + const char *p; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + p = c_getstr (src); + /* If the SRC parameter is "", return DEST. */ + if (p && *p == '\0') + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); + + if (! host_integerp (size, 1) || ! integer_all_onesp (size)) + return 0; + + arglist = build_tree_list (NULL_TREE, src); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strcat_chk is used, assume strcat is available. */ + fn = built_in_decls[BUILT_IN_STRCAT]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strncat_chk builtin EXP. */ + +static tree +fold_builtin_strncat_chk (tree fndecl, tree arglist) +{ + tree dest, src, size, len, fn; + const char *p; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + p = c_getstr (src); + /* If the SRC parameter is "" or if LEN is 0, return DEST. */ + if (p && *p == '\0') + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); + else if (integer_zerop (len)) + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + tree src_len = c_strlen (src, 1); + if (src_len + && host_integerp (src_len, 1) + && host_integerp (len, 1) + && ! tree_int_cst_lt (len, src_len)) + { + /* If LEN >= strlen (SRC), optimize into __strcat_chk. */ + fn = built_in_decls[BUILT_IN_STRCAT_CHK]; + if (!fn) + return 0; + + arglist = build_tree_list (NULL_TREE, size); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + return build_function_call_expr (fn, arglist); + } + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strncat_chk is used, assume strncat is available. */ + fn = built_in_decls[BUILT_IN_STRNCAT]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to __{,v}sprintf_chk with argument list ARGLIST. Return 0 if + a normal call should be emitted rather than expanding the function + inline. FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK. */ + +static tree +fold_builtin_sprintf_chk (tree arglist, enum built_in_function fcode) +{ + tree dest, size, len, fn, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + dest = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (dest))) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + size = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1)) + return 0; + + len = NULL_TREE; + + if (!init_target_chars()) + return 0; + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str != NULL) + { + /* If the format doesn't contain % args or %%, we know the size. */ + if (strchr (fmt_str, target_percent) == 0) + { + if (fcode != BUILT_IN_SPRINTF_CHK || arglist == NULL_TREE) + len = build_int_cstu (size_type_node, strlen (fmt_str)); + } + /* If the format is "%s" and first ... argument is a string literal, + we know the size too. */ + else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, target_percent_s) == 0) + { + tree arg; + + if (arglist && !TREE_CHAIN (arglist)) + { + arg = TREE_VALUE (arglist); + if (POINTER_TYPE_P (TREE_TYPE (arg))) + { + len = c_strlen (arg, 1); + if (! len || ! host_integerp (len, 1)) + len = NULL_TREE; + } + } + } + } + + if (! integer_all_onesp (size)) + { + if (! len || ! tree_int_cst_lt (len, size)) + return 0; + } + + /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0 + or if format doesn't contain % chars or is "%s". */ + if (! integer_zerop (flag)) + { + if (fmt_str == NULL) + return 0; + if (strchr (fmt_str, target_percent) != NULL && strcmp (fmt_str, target_percent_s)) + return 0; + } + + arglist = tree_cons (NULL_TREE, fmt, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */ + fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK + ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to {,v}snprintf with argument list ARGLIST. Return 0 if + a normal call should be emitted rather than expanding the function + inline. FCODE is either BUILT_IN_SNPRINTF_CHK or + BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length + passed as second argument. */ + +tree +fold_builtin_snprintf_chk (tree arglist, tree maxlen, + enum built_in_function fcode) +{ + tree dest, size, len, fn, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + dest = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (dest))) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + len = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + size = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + return 0; + } + else + maxlen = len; + + if (tree_int_cst_lt (size, maxlen)) + return 0; + } + + if (!init_target_chars()) + return 0; + + /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0 + or if format doesn't contain % chars or is "%s". */ + if (! integer_zerop (flag)) + { + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return 0; + if (strchr (fmt_str, target_percent) != NULL && strcmp (fmt_str, target_percent_s)) + return 0; + } + + arglist = tree_cons (NULL_TREE, fmt, arglist); + arglist = tree_cons (NULL_TREE, len, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is + available. */ + fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK + ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. FCODE is the BUILT_IN_* + code of the function to be simplified. */ + +static tree +fold_builtin_printf (tree fndecl, tree arglist, bool ignore, + enum built_in_function fcode) +{ + tree fmt, fn = NULL_TREE, fn_putchar, fn_puts, arg, call; + const char *fmt_str = NULL; + + /* If the return value is used, don't do the transformation. */ + if (! ignore) + return 0; + + /* Verify the required arguments in the original call. */ + if (fcode == BUILT_IN_PRINTF_CHK || fcode == BUILT_IN_VPRINTF_CHK) + { + tree flag; + + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE + || TREE_SIDE_EFFECTS (flag)) + return 0; + arglist = TREE_CHAIN (arglist); + } + + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return NULL_TREE; + + if (fcode == BUILT_IN_PRINTF_UNLOCKED) + { + /* If we're using an unlocked function, assume the other + unlocked functions exist explicitly. */ + fn_putchar = built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]; + fn_puts = built_in_decls[BUILT_IN_PUTS_UNLOCKED]; + } + else + { + fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR]; + fn_puts = implicit_built_in_decls[BUILT_IN_PUTS]; + } + + if (!init_target_chars()) + return 0; + + if (strcmp (fmt_str, target_percent_s) == 0 || strchr (fmt_str, target_percent) == NULL) + { + const char *str; + + if (strcmp (fmt_str, target_percent_s) == 0) + { + if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) + return 0; + + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + + str = c_getstr (TREE_VALUE (arglist)); + if (str == NULL) + return 0; + } + else + { + /* The format specifier doesn't contain any '%' characters. */ + if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK + && arglist) + return 0; + str = fmt_str; + } + + /* If the string was "", printf does nothing. */ + if (str[0] == '\0') + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); + + /* If the string has length of 1, call putchar. */ + if (str[1] == '\0') + { + /* Given printf("c"), (where c is any one character,) + convert "c"[0] to an int and pass that to the replacement + function. */ + arg = build_int_cst (NULL_TREE, str[0]); + arglist = build_tree_list (NULL_TREE, arg); + fn = fn_putchar; + } + else + { + /* If the string was "string\n", call puts("string"). */ + size_t len = strlen (str); + if ((unsigned char)str[len - 1] == target_newline) + { + /* Create a NUL-terminated string that's one char shorter + than the original, stripping off the trailing '\n'. */ + char *newstr = alloca (len); + memcpy (newstr, str, len - 1); + newstr[len - 1] = 0; + + arg = build_string_literal (len, newstr); + arglist = build_tree_list (NULL_TREE, arg); + fn = fn_puts; + } + else + /* We'd like to arrange to call fputs(string,stdout) here, + but we need stdout and don't have a way to get it yet. */ + return 0; + } + } + + /* The other optimizations can be done only on the non-va_list variants. */ + else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) + return 0; + + /* If the format specifier was "%s\n", call __builtin_puts(arg). */ + else if (strcmp (fmt_str, target_percent_s_newline) == 0) + { + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + fn = fn_puts; + } + + /* If the format specifier was "%c", call __builtin_putchar(arg). */ + else if (strcmp (fmt_str, target_percent_c) == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE + || TREE_CHAIN (arglist)) + return 0; + fn = fn_putchar; + } + + if (!fn) + return 0; + + call = build_function_call_expr (fn, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call); +} + +/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. FCODE is the BUILT_IN_* + code of the function to be simplified. */ + +static tree +fold_builtin_fprintf (tree fndecl, tree arglist, bool ignore, + enum built_in_function fcode) +{ + tree fp, fmt, fn = NULL_TREE, fn_fputc, fn_fputs, arg, call; + const char *fmt_str = NULL; + + /* If the return value is used, don't do the transformation. */ + if (! ignore) + return 0; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + fp = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fp))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (fcode == BUILT_IN_FPRINTF_CHK || fcode == BUILT_IN_VFPRINTF_CHK) + { + tree flag; + + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE + || TREE_SIDE_EFFECTS (flag)) + return 0; + arglist = TREE_CHAIN (arglist); + } + + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return NULL_TREE; + + if (fcode == BUILT_IN_FPRINTF_UNLOCKED) + { + /* If we're using an unlocked function, assume the other + unlocked functions exist explicitly. */ + fn_fputc = built_in_decls[BUILT_IN_FPUTC_UNLOCKED]; + fn_fputs = built_in_decls[BUILT_IN_FPUTS_UNLOCKED]; + } + else + { + fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC]; + fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS]; + } + + if (!init_target_chars()) + return 0; + + /* If the format doesn't contain % args or %%, use strcpy. */ + if (strchr (fmt_str, target_percent) == NULL) + { + if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK + && arglist) + return 0; + + /* If the format specifier was "", fprintf does nothing. */ + if (fmt_str[0] == '\0') + { + /* If FP has side-effects, just wait until gimplification is + done. */ + if (TREE_SIDE_EFFECTS (fp)) + return 0; + + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); + } + + /* When "string" doesn't contain %, replace all cases of + fprintf (fp, string) with fputs (string, fp). The fputs + builtin will take care of special cases like length == 1. */ + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, fmt, arglist); + fn = fn_fputs; + } + + /* The other optimizations can be done only on the non-va_list variants. */ + else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK) + return 0; + + /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */ + else if (strcmp (fmt_str, target_percent_s) == 0) + { + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, arg, arglist); + fn = fn_fputs; + } + + /* If the format specifier was "%c", call __builtin_fputc (arg, fp). */ + else if (strcmp (fmt_str, target_percent_c) == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE + || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, arg, arglist); + fn = fn_fputc; + } + + if (!fn) + return 0; + + call = build_function_call_expr (fn, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call); +} + +/* Initialize format string characters in the target charset. */ + +static bool +init_target_chars (void) +{ + static bool init; + if (!init) + { + target_newline = lang_hooks.to_target_charset ('\n'); + target_percent = lang_hooks.to_target_charset ('%'); + target_c = lang_hooks.to_target_charset ('c'); + target_s = lang_hooks.to_target_charset ('s'); + if (target_newline == 0 || target_percent == 0 || target_c == 0 + || target_s == 0) + return false; + + target_percent_c[0] = target_percent; + target_percent_c[1] = target_c; + target_percent_c[2] = '\0'; + + target_percent_s[0] = target_percent; + target_percent_s[1] = target_s; + target_percent_s[2] = '\0'; + + target_percent_s_newline[0] = target_percent; + target_percent_s_newline[1] = target_s; + target_percent_s_newline[2] = target_newline; + target_percent_s_newline[3] = '\0'; + + init = true; + } + return true; +}