X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;ds=sidebyside;f=gcc%2Fbuiltins.c;h=cdc5cebcbc6dea3bcc5c076ebbde0bd488fb803b;hb=576aff9d272cd8ebec2b93b10dcf5eae125e9667;hp=dd8c8ea87730573ac190313e7d22ab31b370198c;hpb=218bc19e98901a257b8214c98d63f46d171de5f3;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/builtins.c b/gcc/builtins.c index dd8c8ea8773..cdc5cebcbc6 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 Free Software Foundation, Inc. + 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GCC. @@ -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 @@ -59,8 +56,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA const char *const built_in_class_names[4] = {"NOT_BUILT_IN", "BUILT_IN_FRONTEND", "BUILT_IN_MD", "BUILT_IN_NORMAL"}; -#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM) #X, -const char *const built_in_names[(int) END_BUILTINS] = +#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM, COND) #X, +const char * built_in_names[(int) END_BUILTINS] = { #include "builtins.def" }; @@ -98,8 +95,9 @@ 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_int_roundingfn (tree, rtx, rtx); static rtx expand_builtin_args_info (tree); -static rtx expand_builtin_next_arg (tree); +static rtx expand_builtin_next_arg (void); static rtx expand_builtin_va_start (tree); static rtx expand_builtin_va_end (tree); static rtx expand_builtin_va_copy (tree); @@ -107,13 +105,13 @@ static rtx expand_builtin_memcmp (tree, tree, rtx, enum machine_mode); static rtx expand_builtin_strcmp (tree, rtx, enum machine_mode); static rtx expand_builtin_strncmp (tree, rtx, enum machine_mode); static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, enum machine_mode); -static rtx expand_builtin_strcat (tree, rtx, enum machine_mode); +static rtx expand_builtin_strcat (tree, tree, rtx, enum machine_mode); static rtx expand_builtin_strncat (tree, rtx, enum machine_mode); 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, rtx, enum machine_mode, int); -static rtx expand_builtin_memmove (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, tree); static rtx expand_builtin_bcopy (tree); static rtx expand_builtin_strcpy (tree, rtx, enum machine_mode); static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode); @@ -121,13 +119,13 @@ 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, rtx, enum machine_mode); -static rtx expand_builtin_strpbrk (tree, rtx, enum machine_mode); -static rtx expand_builtin_strchr (tree, rtx, enum machine_mode); -static rtx expand_builtin_strrchr (tree, rtx, enum machine_mode); +static rtx expand_builtin_strstr (tree, tree, rtx, enum machine_mode); +static rtx expand_builtin_strpbrk (tree, tree, rtx, enum machine_mode); +static rtx expand_builtin_strchr (tree, tree, rtx, enum machine_mode); +static rtx expand_builtin_strrchr (tree, tree, rtx, enum machine_mode); static rtx expand_builtin_alloca (tree, rtx); static rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab); static rtx expand_builtin_frame_address (tree, tree); @@ -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); @@ -152,42 +150,57 @@ static tree fold_builtin_cabs (tree, tree); static tree fold_builtin_sqrt (tree, tree); static tree fold_builtin_cbrt (tree, tree); static tree fold_builtin_pow (tree, tree, tree); +static tree fold_builtin_powi (tree, tree, tree); 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_mempcpy (tree); -static tree fold_builtin_memmove (tree); -static tree fold_builtin_strchr (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_copysign (tree, 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); static tree fold_builtin_isdigit (tree); static tree fold_builtin_fabs (tree, tree); static tree fold_builtin_abs (tree, tree); -static tree fold_builtin_unordered_cmp (tree, enum tree_code, enum tree_code); -static tree fold_builtin_1 (tree, bool); +static tree fold_builtin_unordered_cmp (tree, tree, enum tree_code, + enum tree_code); +static tree fold_builtin_1 (tree, tree, bool); -static tree fold_builtin_strpbrk (tree); -static tree fold_builtin_strstr (tree); -static tree fold_builtin_strrchr (tree); +static tree fold_builtin_strpbrk (tree, tree); +static tree fold_builtin_strstr (tree, tree); +static tree fold_builtin_strrchr (tree, tree); static tree fold_builtin_strcat (tree); static tree fold_builtin_strncat (tree); static tree fold_builtin_strspn (tree); static tree fold_builtin_strcspn (tree); -static void fold_builtin_next_arg (tree); static tree fold_builtin_sprintf (tree, int); +/* 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. @@ -202,7 +215,7 @@ get_pointer_alignment (tree exp, unsigned int max_align) { unsigned int align, inner; - if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE) + if (! POINTER_TYPE_P (TREE_TYPE (exp))) return 0; align = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); @@ -216,7 +229,7 @@ get_pointer_alignment (tree exp, unsigned int max_align) case CONVERT_EXPR: case NON_LVALUE_EXPR: exp = TREE_OPERAND (exp, 0); - if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE) + if (! POINTER_TYPE_P (TREE_TYPE (exp))) return align; inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); @@ -338,7 +351,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; } @@ -452,12 +465,17 @@ builtin_save_expr (tree exp) times to get the address of either a higher stack frame, or a return address located within it (depending on FNDECL_CODE). */ -rtx -expand_builtin_return_addr (enum built_in_function fndecl_code, int count, - rtx tem) +static rtx +expand_builtin_return_addr (enum built_in_function fndecl_code, int count) { int i; +#ifdef INITIAL_FRAME_ADDRESS_RTX + rtx tem = INITIAL_FRAME_ADDRESS_RTX; +#else + rtx tem = hard_frame_pointer_rtx; +#endif + /* Some machines need special handling before we can access arbitrary frames. For example, on the sparc, we must first flush all register windows to the stack. */ @@ -688,7 +706,7 @@ expand_builtin_setjmp (tree arglist, rtx target) scheme in the compiler and will only work in the method used by them. */ -void +static void expand_builtin_longjmp (rtx buf_addr, rtx value) { rtx fp, lab, stack, insn, last; @@ -708,8 +726,6 @@ expand_builtin_longjmp (rtx buf_addr, rtx value) longer copying around a value that we don't care about. */ gcc_assert (value == const1_rtx); - current_function_calls_longjmp = 1; - last = get_last_insn (); #ifdef HAVE_builtin_longjmp if (HAVE_builtin_longjmp) @@ -922,28 +938,29 @@ expand_builtin_prefetch (tree arglist) /* Argument 1 (read/write flag) must be a compile-time constant int. */ if (TREE_CODE (arg1) != INTEGER_CST) { - error ("second arg to %<__builtin_prefetch%> must be a constant"); + error ("second argument to %<__builtin_prefetch%> must be a constant"); arg1 = integer_zero_node; } op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); /* Argument 1 must be either zero or one. */ if (INTVAL (op1) != 0 && INTVAL (op1) != 1) { - warning ("invalid second arg to __builtin_prefetch; using zero"); + warning (0, "invalid second argument to %<__builtin_prefetch%>;" + " using zero"); op1 = const0_rtx; } /* Argument 2 (locality) must be a compile-time constant int. */ if (TREE_CODE (arg2) != INTEGER_CST) { - error ("third arg to %<__builtin_prefetch%> must be a constant"); + error ("third argument to %<__builtin_prefetch%> must be a constant"); arg2 = integer_zero_node; } op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0); /* Argument 2 must be 0, 1, 2, or 3. */ if (INTVAL (op2) < 0 || INTVAL (op2) > 3) { - warning ("invalid third arg to __builtin_prefetch; using zero"); + warning (0, "invalid third argument to %<__builtin_prefetch%>; using zero"); op2 = const0_rtx; } @@ -974,12 +991,8 @@ expand_builtin_prefetch (tree arglist) static rtx get_memory_rtx (tree exp) { - 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 @@ -1477,8 +1490,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 SET_TYPE: return set_type_class; - case FILE_TYPE: return file_type_class; case LANG_TYPE: return lang_type_class; default: return no_type_class; } @@ -1548,8 +1559,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) @@ -1566,6 +1581,7 @@ mathfn_built_in (tree type, enum built_in_function fn) CASE_MATHFN (BUILT_IN_NEXTAFTER) CASE_MATHFN (BUILT_IN_NEXTTOWARD) CASE_MATHFN (BUILT_IN_POW) + CASE_MATHFN (BUILT_IN_POWI) CASE_MATHFN (BUILT_IN_POW10) CASE_MATHFN (BUILT_IN_REMAINDER) CASE_MATHFN (BUILT_IN_REMQUO) @@ -1753,6 +1769,13 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) case BUILT_IN_RINTF: case BUILT_IN_RINTL: builtin_optab = rint_optab; break; + case BUILT_IN_LRINT: + case BUILT_IN_LRINTF: + case BUILT_IN_LRINTL: + case BUILT_IN_LLRINT: + case BUILT_IN_LLRINTF: + case BUILT_IN_LLRINTL: + builtin_optab = lrint_optab; break; default: gcc_unreachable (); } @@ -1774,6 +1797,7 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) narg = builtin_save_expr (arg); if (narg != arg) { + arg = narg; arglist = build_tree_list (NULL_TREE, arg); exp = build_function_call_expr (fndecl, arglist); } @@ -1862,6 +1886,7 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) { optab builtin_optab; rtx op0, op1, insns; + int op1_type = REAL_TYPE; tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); tree arg0, arg1, temp, narg; @@ -1869,7 +1894,12 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) bool errno_set = true; bool stable = true; - if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + if ((DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXP) + || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPF) + || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPL)) + op1_type = INTEGER_TYPE; + + if (!validate_arglist (arglist, REAL_TYPE, op1_type, VOID_TYPE)) return 0; arg0 = TREE_VALUE (arglist); @@ -1885,6 +1915,10 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) case BUILT_IN_ATAN2F: case BUILT_IN_ATAN2L: builtin_optab = atan2_optab; break; + case BUILT_IN_LDEXP: + case BUILT_IN_LDEXPF: + case BUILT_IN_LDEXPL: + builtin_optab = ldexp_optab; break; case BUILT_IN_FMOD: case BUILT_IN_FMODF: case BUILT_IN_FMODL: @@ -1913,6 +1947,7 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) narg = builtin_save_expr (arg1); if (narg != arg1) { + arg1 = narg; temp = build_tree_list (NULL_TREE, narg); stable = false; } @@ -1922,6 +1957,7 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) narg = builtin_save_expr (arg0); if (narg != arg0) { + arg0 = narg; arglist = tree_cons (NULL_TREE, narg, temp); stable = false; } @@ -1972,7 +2008,7 @@ 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; @@ -2032,6 +2068,7 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) narg = save_expr (arg); if (narg != arg) { + arg = narg; arglist = build_tree_list (NULL_TREE, arg); exp = build_function_call_expr (fndecl, arglist); } @@ -2086,13 +2123,118 @@ 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 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 BUILT_IN_LCEIL: + case BUILT_IN_LCEILF: + case BUILT_IN_LCEILL: + case BUILT_IN_LLCEIL: + case BUILT_IN_LLCEILF: + case BUILT_IN_LLCEILL: + builtin_optab = lceil_optab; + fallback_fn = BUILT_IN_CEIL; + break; + + case BUILT_IN_LFLOOR: + case BUILT_IN_LFLOORF: + case BUILT_IN_LFLOORL: + case BUILT_IN_LLFLOOR: + case BUILT_IN_LLFLOORF: + case BUILT_IN_LLFLOORL: + 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 @@ -2348,6 +2490,70 @@ expand_builtin_pow (tree exp, rtx target, rtx subtarget) return expand_builtin_mathfn_2 (exp, target, subtarget); } +/* Expand a call to the powi built-in mathematical 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; if convenient, the result should be placed in TARGET. */ + +static rtx +expand_builtin_powi (tree exp, rtx target, rtx subtarget) +{ + tree arglist = TREE_OPERAND (exp, 1); + 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; + + arg0 = TREE_VALUE (arglist); + arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + mode = TYPE_MODE (TREE_TYPE (exp)); + + /* Handle constant power. */ + + if (TREE_CODE (arg1) == INTEGER_CST + && ! TREE_CONSTANT_OVERFLOW (arg1)) + { + HOST_WIDE_INT n = TREE_INT_CST_LOW (arg1); + + /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact. + Otherwise, check the number of multiplications required. */ + if ((TREE_INT_CST_HIGH (arg1) == 0 + || TREE_INT_CST_HIGH (arg1) == -1) + && ((n >= -1 && n <= 2) + || (! optimize_size + && powi_cost (n) <= POWI_MAX_MULTS))) + { + op0 = expand_expr (arg0, subtarget, VOIDmode, 0); + op0 = force_reg (mode, op0); + return expand_powi (op0, mode, n); + } + } + + /* 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, 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, mode2); + + return target; +} + /* Expand expression EXP which is a call to the strlen builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ @@ -2461,11 +2667,11 @@ expand_builtin_strlen (tree arglist, rtx target, in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_strstr (tree arglist, tree type, rtx target, enum machine_mode mode) { if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) { - tree result = fold_builtin_strstr (arglist); + tree result = fold_builtin_strstr (arglist, type); if (result) return expand_expr (result, target, mode, EXPAND_NORMAL); } @@ -2477,11 +2683,11 @@ expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode) in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_strchr (tree arglist, tree type, rtx target, enum machine_mode mode) { if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) { - tree result = fold_builtin_strchr (arglist); + tree result = fold_builtin_strchr (arglist, type); if (result) return expand_expr (result, target, mode, EXPAND_NORMAL); @@ -2495,11 +2701,11 @@ expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode) in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_strrchr (tree arglist, tree type, rtx target, enum machine_mode mode) { if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) { - tree result = fold_builtin_strrchr (arglist); + tree result = fold_builtin_strrchr (arglist, type); if (result) return expand_expr (result, target, mode, EXPAND_NORMAL); } @@ -2511,11 +2717,11 @@ expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode) in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_strpbrk (tree arglist, tree type, rtx target, enum machine_mode mode) { if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) { - tree result = fold_builtin_strpbrk (arglist); + tree result = fold_builtin_strpbrk (arglist, type); if (result) return expand_expr (result, target, mode, EXPAND_NORMAL); } @@ -2544,8 +2750,10 @@ builtin_memcpy_read_str (void *data, HOST_WIDE_INT offset, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode) +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)) return 0; @@ -2559,27 +2767,15 @@ expand_builtin_memcpy (tree arglist, 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 (fndecl, arglist); + + if (result) + return expand_expr (result, target, mode, EXPAND_NORMAL); /* If DEST is not a pointer type, call the normal function. */ if (dest_align == 0) return 0; - /* If the LEN parameter is zero, return DEST. */ - if (integer_zerop (len)) - { - /* Evaluate and ignore SRC in case it has side-effects. */ - expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); - return expand_expr (dest, target, mode, EXPAND_NORMAL); - } - - /* If SRC and DEST are the same (and not volatile), return DEST. */ - if (operand_equal_p (src, dest, 0)) - { - /* 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); - } - /* If either SRC is not a pointer type, don't do this operation in-line. */ if (src_align == 0) @@ -2612,7 +2808,8 @@ expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode) /* 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) { @@ -2632,7 +2829,7 @@ expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode) stpcpy. */ static rtx -expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode, +expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode mode, int endp) { if (!validate_arglist (arglist, @@ -2659,48 +2856,20 @@ expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode, unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); rtx dest_mem, src_mem, len_rtx; + tree result = fold_builtin_mempcpy (arglist, type, endp); - /* If DEST is not a pointer type, call the normal function. */ - if (dest_align == 0) + if (result) + return expand_expr (result, target, mode, EXPAND_NORMAL); + + /* If either SRC or DEST is not a pointer type, don't do this + operation in-line. */ + if (dest_align == 0 || src_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 (endp == 0) - { - /* 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); - } - - if (endp == 2) - len = fold (build2 (MINUS_EXPR, TREE_TYPE (len), len, - integer_one_node)); - 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); - } - /* If LEN is not constant, call the normal function. */ if (! host_integerp (len, 1)) return 0; - /* If the LEN parameter is zero, return DEST. */ - if (tree_low_cst (len, 1) == 0) - { - /* Evaluate and ignore SRC in case it has side-effects. */ - expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); - return expand_expr (dest, target, mode, EXPAND_NORMAL); - } - - /* If either SRC is not a pointer type, don't do this - operation in-line. */ - if (src_align == 0) - return 0; - len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); src_str = c_getstr (src); @@ -2746,7 +2915,8 @@ expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode, if we failed the caller should emit a normal call. */ static rtx -expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_memmove (tree arglist, tree type, rtx target, + enum machine_mode mode, tree orig_exp) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -2760,27 +2930,15 @@ expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode) unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + tree result = fold_builtin_memmove (arglist, type); + + if (result) + return expand_expr (result, target, mode, EXPAND_NORMAL); /* If DEST is not a pointer type, call the normal function. */ if (dest_align == 0) return 0; - /* If the LEN parameter is zero, return DEST. */ - if (integer_zerop (len)) - { - /* Evaluate and ignore SRC in case it has side-effects. */ - expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); - return expand_expr (dest, target, mode, EXPAND_NORMAL); - } - - /* If SRC and DEST are the same (and not volatile), return DEST. */ - if (operand_equal_p (src, dest, 0)) - { - /* 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); - } - /* If either SRC is not a pointer type, don't do this operation in-line. */ if (src_align == 0) @@ -2790,18 +2948,20 @@ expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode) 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, it is ok to use memcpy as well. */ if (integer_onep (len)) { - rtx ret = expand_builtin_mempcpy (arglist, target, mode, + rtx ret = expand_builtin_mempcpy (arglist, type, target, mode, /*endp=*/0); if (ret) return ret; @@ -2816,8 +2976,10 @@ expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode) if we failed the caller should emit a normal call. */ static rtx -expand_builtin_bcopy (tree arglist) +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, @@ -2837,7 +2999,7 @@ expand_builtin_bcopy (tree arglist) newarglist = tree_cons (NULL_TREE, src, newarglist); newarglist = tree_cons (NULL_TREE, dest, newarglist); - return expand_builtin_memmove (newarglist, const0_rtx, VOIDmode); + return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode, exp); } #ifndef HAVE_movstr @@ -2913,36 +3075,21 @@ expand_movstr (tree dest, tree src, rtx target, int endp) convenient). */ static rtx -expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_strcpy (tree exp, rtx target, enum machine_mode mode) { - tree fn, len, src, dst; - - if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) - return 0; - - src = TREE_VALUE (TREE_CHAIN (arglist)); - dst = TREE_VALUE (arglist); - - /* If SRC and DST are equal (and not volatile), return DST. */ - if (operand_equal_p (src, dst, 0)) - return expand_expr (dst, target, mode, EXPAND_NORMAL); - - len = c_strlen (src, 1); - if (len == 0 || TREE_SIDE_EFFECTS (len)) - return expand_movstr (TREE_VALUE (arglist), - TREE_VALUE (TREE_CHAIN (arglist)), - target, /*endp=*/0); - - fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; - if (!fn) - return 0; + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + { + tree result = fold_builtin_strcpy (fndecl, arglist, 0); + if (result) + return expand_expr (result, target, mode, EXPAND_NORMAL); - len = size_binop (PLUS_EXPR, len, ssize_int (1)); - arglist = build_tree_list (NULL_TREE, len); - arglist = tree_cons (NULL_TREE, src, arglist); - arglist = tree_cons (NULL_TREE, dst, arglist); - return expand_expr (build_function_call_expr (fn, arglist), - target, mode, EXPAND_NORMAL); + return expand_movstr (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist)), + target, /*endp=*/0); + } + return 0; } /* Expand a call to the stpcpy builtin, with arguments in ARGLIST. @@ -2951,8 +3098,9 @@ expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode) mode MODE if that's convenient). */ static rtx -expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode) { + tree arglist = TREE_OPERAND (exp, 1); /* If return value is ignored, transform stpcpy into strcpy. */ if (target == const0_rtx) { @@ -2987,7 +3135,8 @@ expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode) narglist = build_tree_list (NULL_TREE, lenp1); narglist = tree_cons (NULL_TREE, src, narglist); narglist = tree_cons (NULL_TREE, dst, narglist); - ret = expand_builtin_mempcpy (narglist, target, mode, /*endp=*/2); + ret = expand_builtin_mempcpy (narglist, TREE_TYPE (exp), + target, mode, /*endp=*/2); if (ret) return ret; @@ -2998,7 +3147,7 @@ expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode) if (GET_CODE (len_rtx) == CONST_INT) { - ret = expand_builtin_strcpy (arglist, target, mode); + ret = expand_builtin_strcpy (exp, target, mode); if (ret) { @@ -3047,35 +3196,22 @@ builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset, if we failed the caller should emit a normal call. */ static rtx -expand_builtin_strncpy (tree arglist, rtx target, enum machine_mode mode) +expand_builtin_strncpy (tree exp, rtx target, enum machine_mode mode) { - if (!validate_arglist (arglist, - POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) - return 0; - else + 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 fn; - - /* We must be passed a constant len parameter. */ - if (TREE_CODE (len) != INTEGER_CST) - return 0; - - /* If the len parameter is zero, return the dst parameter. */ - if (integer_zerop (len)) - { - /* Evaluate and ignore the src argument in case it has - side-effects. */ - expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), const0_rtx, - VOIDmode, EXPAND_NORMAL); - /* Return the dst parameter. */ - return expand_expr (TREE_VALUE (arglist), target, mode, - EXPAND_NORMAL); - } + tree result = fold_builtin_strncpy (fndecl, arglist, slen); + + if (result) + return expand_expr (result, target, mode, EXPAND_NORMAL); - /* Now, we must be passed a constant src ptr parameter. */ - if (slen == 0 || TREE_CODE (slen) != INTEGER_CST) + /* We must be passed a constant len and src parameter. */ + if (!host_integerp (len, 1) || !slen || !host_integerp (slen, 1)) return 0; slen = size_binop (PLUS_EXPR, slen, ssize_int (1)); @@ -3105,14 +3241,8 @@ expand_builtin_strncpy (tree arglist, rtx target, enum machine_mode mode) dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } - - /* OK transform into builtin memcpy. */ - 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); } + return 0; } /* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) @@ -3163,7 +3293,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)) @@ -3249,7 +3380,9 @@ expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode) 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) { @@ -3265,8 +3398,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)) @@ -3284,7 +3418,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. @@ -3423,7 +3557,7 @@ expand_builtin_strcmp (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; @@ -3507,8 +3641,10 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode) 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); + 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; @@ -3545,7 +3681,7 @@ 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; @@ -3635,8 +3771,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; @@ -3647,7 +3785,7 @@ 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, rtx target, enum machine_mode mode) +expand_builtin_strcat (tree arglist, tree type, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; @@ -3685,7 +3823,8 @@ expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode) fold (build_function_call_expr (strlen_fn, build_tree_list (NULL_TREE, dst))); - /* Create (dst + strlen (dst)). */ + /* Create (dst + (cast) strlen (dst)). */ + newdst = fold_convert (TREE_TYPE (dst), newdst); newdst = fold (build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst)); /* Prepend the new dst argument. */ @@ -3697,7 +3836,7 @@ expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode) 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, /*target=*/0, mode, /*endp=*/0)) + if (expand_builtin_mempcpy (arglist, type, /*target=*/0, mode, /*endp=*/0)) return expand_expr (dst, target, mode, EXPAND_NORMAL); else return 0; @@ -3826,48 +3965,18 @@ expand_builtin_args_info (tree arglist) return const0_rtx; } -/* Expand ARGLIST, from a call to __builtin_next_arg. */ +/* Expand a call to __builtin_next_arg. */ static rtx -expand_builtin_next_arg (tree arglist) +expand_builtin_next_arg (void) { - tree fntype = TREE_TYPE (current_function_decl); - - if (TYPE_ARG_TYPES (fntype) == 0 - || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - == void_type_node)) - { - error ("% used in function with fixed args"); - return const0_rtx; - } - - if (arglist) - { - tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl)); - tree arg = TREE_VALUE (arglist); - - /* Strip off all nops for the sake of the comparison. This - is not quite the same as STRIP_NOPS. It does more. - We must also strip off INDIRECT_EXPR for C++ reference - parameters. */ - while (TREE_CODE (arg) == NOP_EXPR - || TREE_CODE (arg) == CONVERT_EXPR - || TREE_CODE (arg) == NON_LVALUE_EXPR - || TREE_CODE (arg) == INDIRECT_REF) - arg = TREE_OPERAND (arg, 0); - if (arg != last_parm) - warning ("second parameter of % not last named argument"); - } - else - /* 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"); - - return expand_binop (Pmode, add_optab, - current_function_internal_arg_pointer, - current_function_arg_offset_rtx, - NULL_RTX, 0, OPTAB_LIB_WIDEN); -} + /* Checking arguments is already done in fold_builtin_next_arg + that must be called before this function. */ + return expand_binop (Pmode, add_optab, + current_function_internal_arg_pointer, + current_function_arg_offset_rtx, + NULL_RTX, 0, OPTAB_LIB_WIDEN); +} /* Make it easier for the backends by protecting the valist argument from multiple evaluations. */ @@ -3945,12 +4054,16 @@ expand_builtin_va_start (tree arglist) chain = TREE_CHAIN (arglist); - if (TREE_CHAIN (chain)) - error ("too many arguments to function %"); + if (!chain) + { + error ("too few arguments to function %"); + return const0_rtx; + } - fold_builtin_next_arg (chain); + if (fold_builtin_next_arg (chain)) + return const0_rtx; - nextarg = expand_builtin_next_arg (chain); + nextarg = expand_builtin_next_arg (); valist = stabilize_va_list (TREE_VALUE (arglist), 1); #ifdef EXPAND_BUILTIN_VA_START @@ -4003,6 +4116,18 @@ std_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) build2 (BIT_AND_EXPR, TREE_TYPE (valist), valist_tmp, t)); gimplify_and_add (t, pre_p); } + else + boundary = align; + + /* If the actual alignment is less than the alignment of the type, + adjust the type accordingly so that we don't assume strict alignment + when deferencing the pointer. */ + boundary *= BITS_PER_UNIT; + if (boundary < TYPE_ALIGN (type)) + { + type = build_variant_type_copy (type); + TYPE_ALIGN (type) = boundary; + } /* Compute the rounded size of the type. */ type_size = size_in_bytes (type); @@ -4085,7 +4210,7 @@ gimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p) In that case, unwrap both types so that we can compare the underlying records. */ if (TREE_CODE (have_va_type) == ARRAY_TYPE - || TREE_CODE (have_va_type) == POINTER_TYPE) + || POINTER_TYPE_P (have_va_type)) { want_va_type = TREE_TYPE (want_va_type); have_va_type = TREE_TYPE (have_va_type); @@ -4108,12 +4233,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); } @@ -4150,7 +4275,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); @@ -4237,25 +4363,24 @@ expand_builtin_frame_address (tree fndecl, tree arglist) else if (! host_integerp (TREE_VALUE (arglist), 1)) { if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) - error ("invalid arg to %<__builtin_frame_address%>"); + error ("invalid argument to %<__builtin_frame_address%>"); else - error ("invalid arg to %<__builtin_return_address%>"); + error ("invalid argument to %<__builtin_return_address%>"); return const0_rtx; } else { rtx tem = expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl), - tree_low_cst (TREE_VALUE (arglist), 1), - hard_frame_pointer_rtx); + tree_low_cst (TREE_VALUE (arglist), 1)); /* Some ports cannot access arbitrary stack frames. */ if (tem == NULL) { if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) - warning ("unsupported arg to %<__builtin_frame_address%>"); + warning (0, "unsupported argument to %<__builtin_frame_address%>"); else - warning ("unsupported arg to %<__builtin_return_address%>"); + warning (0, "unsupported argument to %<__builtin_return_address%>"); return const0_rtx; } @@ -4358,7 +4483,7 @@ expand_builtin_expect (tree arglist, rtx target) if (TREE_CODE (c) != INTEGER_CST) { - error ("second arg to %<__builtin_expect%> must be a constant"); + error ("second argument to %<__builtin_expect%> must be a constant"); c = integer_zero_node; } @@ -4499,7 +4624,7 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) return ret; } -void +static void expand_builtin_trap (void) { #ifdef HAVE_trap @@ -4533,6 +4658,29 @@ expand_builtin_fabs (tree arglist, rtx target, rtx subtarget) return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1)); } +/* Expand a call to copysign, copysignf, or copysignl with arguments ARGLIST. + Return NULL is a normal call should be emitted rather than expanding the + function inline. If convenient, the result should be placed in TARGET. + SUBTARGET may be used as the target for computing the operand. */ + +static rtx +expand_builtin_copysign (tree arglist, rtx target, rtx subtarget) +{ + rtx op0, op1; + tree arg; + + if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + return 0; + + arg = TREE_VALUE (arglist); + op0 = expand_expr (arg, subtarget, VOIDmode, 0); + + arg = TREE_VALUE (TREE_CHAIN (arglist)); + op1 = expand_expr (arg, NULL, VOIDmode, 0); + + 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 @@ -4558,15 +4706,16 @@ 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 arglist = TREE_OPERAND (exp, 1); tree fn_putchar = unlocked ? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED] : implicit_built_in_decls[BUILT_IN_PUTCHAR]; @@ -4583,7 +4732,7 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, if (! arglist) return 0; fmt = TREE_VALUE (arglist); - if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) return 0; arglist = TREE_CHAIN (arglist); @@ -4596,7 +4745,7 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, if (strcmp (fmt_str, "%s\n") == 0) { if (! arglist - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) || TREE_CHAIN (arglist)) return 0; fn = fn_puts; @@ -4657,19 +4806,22 @@ 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 arglist = TREE_OPERAND (exp, 1); 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] @@ -4685,13 +4837,13 @@ expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode, if (! arglist) return 0; fp = TREE_VALUE (arglist); - if (TREE_CODE (TREE_TYPE (fp)) != POINTER_TYPE) + if (! POINTER_TYPE_P (TREE_TYPE (fp))) return 0; arglist = TREE_CHAIN (arglist); if (! arglist) return 0; fmt = TREE_VALUE (arglist); - if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) return 0; arglist = TREE_CHAIN (arglist); @@ -4704,7 +4856,7 @@ expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode, if (strcmp (fmt_str, "%s") == 0) { if (! arglist - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) || TREE_CHAIN (arglist)) return 0; arg = TREE_VALUE (arglist); @@ -4751,8 +4903,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 @@ -4772,13 +4926,13 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) if (! arglist) return 0; dest = TREE_VALUE (arglist); - if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE) + if (! POINTER_TYPE_P (TREE_TYPE (dest))) return 0; arglist = TREE_CHAIN (arglist); if (! arglist) return 0; fmt = TREE_VALUE (arglist); - if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) return 0; arglist = TREE_CHAIN (arglist); @@ -4814,7 +4968,7 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) if (! arglist || TREE_CHAIN (arglist)) return 0; arg = TREE_VALUE (arglist); - if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) + if (! POINTER_TYPE_P (TREE_TYPE (arg))) return 0; if (target != const0_rtx) @@ -4857,7 +5011,7 @@ expand_builtin_profile_func (bool exitp) emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this, Pmode, expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, - 0, hard_frame_pointer_rtx), + 0), Pmode); return const0_rtx; @@ -4954,7 +5108,7 @@ expand_builtin_signbit (tree exp, rtx target) enum machine_mode fmode, imode, rmode; HOST_WIDE_INT hi, lo; tree arg, arglist; - int bitpos; + int word, bitpos; rtx temp; arglist = TREE_OPERAND (exp, 1); @@ -4968,7 +5122,8 @@ expand_builtin_signbit (tree exp, rtx target) /* For floating point formats without a sign bit, implement signbit as "ARG < 0.0". */ - if (fmt->signbit < 0) + 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)) @@ -4979,41 +5134,37 @@ expand_builtin_signbit (tree exp, rtx target) return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL); } - imode = int_mode_for_mode (fmode); - if (imode == BLKmode) - return 0; - - bitpos = fmt->signbit; - /* Handle targets with different FP word orders. */ - if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN) + temp = expand_expr (arg, NULL_RTX, VOIDmode, 0); + if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD) { - int nwords = GET_MODE_BITSIZE (fmode) / BITS_PER_WORD; - int word = nwords - (bitpos / BITS_PER_WORD) - 1; - bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD; + imode = int_mode_for_mode (fmode); + if (imode == BLKmode) + return 0; + temp = gen_lowpart (imode, temp); + } + else + { + imode = word_mode; + /* Handle targets with different FP word orders. */ + if (FLOAT_WORDS_BIG_ENDIAN) + word = (GET_MODE_BITSIZE (fmode) - bitpos) / BITS_PER_WORD; + else + word = bitpos / BITS_PER_WORD; + temp = operand_subword_force (temp, word, fmode); + bitpos = bitpos % BITS_PER_WORD; } - /* If the sign bit is not in the lowpart and the floating point format - is wider than an integer, check that is twice the size of an integer - so that we can use gen_highpart below. */ - if (bitpos >= GET_MODE_BITSIZE (rmode) - && GET_MODE_BITSIZE (imode) != 2 * GET_MODE_BITSIZE (rmode)) - return 0; + /* Force the intermediate word_mode (or narrower) result into a + register. This avoids attempting to create paradoxical SUBREGs + of floating point modes below. */ + temp = force_reg (imode, temp); - temp = expand_expr (arg, NULL_RTX, VOIDmode, 0); - temp = gen_lowpart (imode, temp); + /* If the bitpos is within the "result mode" lowpart, the operation + can be implement with a single bitwise AND. Otherwise, we need + a right shift and an AND. */ - if (GET_MODE_BITSIZE (imode) > GET_MODE_BITSIZE (rmode)) - { - if (BYTES_BIG_ENDIAN) - bitpos = GET_MODE_BITSIZE (imode) - 1 - bitpos; - temp = copy_to_mode_reg (imode, temp); - temp = extract_bit_field (temp, 1, bitpos, 1, - NULL_RTX, rmode, rmode); - } - else + if (bitpos < GET_MODE_BITSIZE (rmode)) { - if (GET_MODE_BITSIZE (imode) < GET_MODE_BITSIZE (rmode)) - temp = gen_lowpart (rmode, temp); if (bitpos < HOST_BITS_PER_WIDE_INT) { hi = 0; @@ -5025,11 +5176,24 @@ expand_builtin_signbit (tree exp, rtx target) lo = 0; } - temp = force_reg (rmode, temp); + if (imode != rmode) + temp = gen_lowpart (rmode, temp); temp = expand_binop (rmode, and_optab, temp, immed_double_const (lo, hi, rmode), - target, 1, OPTAB_LIB_WIDEN); + NULL_RTX, 1, OPTAB_LIB_WIDEN); } + else + { + /* Perform a logical right shift to place the signbit in the least + significant bit, then truncate the result to the desired mode + and mask just this bit. */ + temp = expand_shift (RSHIFT_EXPR, imode, temp, + build_int_cst (NULL_TREE, bitpos), NULL_RTX, 1); + temp = gen_lowpart (rmode, temp); + temp = expand_binop (rmode, and_optab, temp, const1_rtx, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + } + return temp; } @@ -5095,6 +5259,166 @@ expand_builtin_fork_or_exec (tree fn, tree arglist, rtx target, int ignore) return expand_call (call, target, ignore); } + + +/* 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 (tree arglist, enum rtx_code code, bool after, + rtx target, bool ignore) +{ + enum machine_mode mode; + rtx addr, val, mem; + + /* Expand the operands. */ + addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_SUM); + mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist)))); + + arglist = TREE_CHAIN (arglist); + val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); + + /* 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)); + MEM_VOLATILE_P (mem) = 1; + + 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 (tree arglist, bool is_bool, rtx target) +{ + enum machine_mode mode; + rtx addr, old_val, new_val, mem; + + /* Expand the operands. */ + addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_SUM); + mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist)))); + + 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); + + /* 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)); + MEM_VOLATILE_P (mem) = 1; + + 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 (tree arglist, rtx target) +{ + enum machine_mode mode; + rtx addr, val, mem; + + /* Expand the operands. */ + addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_NORMAL); + mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist)))); + + arglist = TREE_CHAIN (arglist); + val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL); + + /* 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 barrier semantics of the intrinsic. */ + mem = validize_mem (gen_rtx_MEM (mode, addr)); + MEM_VOLATILE_P (mem) = 1; + + return expand_sync_lock_test_and_set (mem, val, target); +} + +/* Expand the __sync_synchronize intrinsic. */ + +static void +expand_builtin_synchronize (void) +{ + rtx body; + +#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 that will prevent compiler movement across the barrier. */ + body = gen_rtx_ASM_INPUT (VOIDmode, ""); + MEM_VOLATILE_P (body) = 1; + emit_insn (body); +} + +/* Expand the __sync_lock_release intrinsic. ARGLIST is the operands list + to the function. */ + +static void +expand_builtin_lock_release (tree arglist) +{ + enum machine_mode mode; + enum insn_code icode; + rtx addr, val, mem, insn; + + /* Expand the operands. */ + addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_NORMAL); + mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist)))); + val = const0_rtx; + + /* 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 barrier semantics of the intrinsic. */ + mem = validize_mem (gen_rtx_MEM (mode, addr)); + MEM_VOLATILE_P (mem) = 1; + + /* 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 @@ -5117,7 +5441,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, /* 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); @@ -5162,6 +5486,14 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; + case BUILT_IN_COPYSIGN: + case BUILT_IN_COPYSIGNF: + case BUILT_IN_COPYSIGNL: + target = expand_builtin_copysign (arglist, target, subtarget); + if (target) + return target; + break; + /* Just do a normal library call if we were unable to fold the values. */ case BUILT_IN_CABS: @@ -5239,11 +5571,34 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, case BUILT_IN_RINT: case BUILT_IN_RINTF: case BUILT_IN_RINTL: + case BUILT_IN_LRINT: + case BUILT_IN_LRINTF: + case BUILT_IN_LRINTL: + case BUILT_IN_LLRINT: + case BUILT_IN_LLRINTF: + case BUILT_IN_LLRINTL: target = expand_builtin_mathfn (exp, target, subtarget); if (target) return target; break; + case BUILT_IN_LCEIL: + case BUILT_IN_LCEILF: + case BUILT_IN_LCEILL: + case BUILT_IN_LLCEIL: + case BUILT_IN_LLCEILF: + case BUILT_IN_LLCEILL: + case BUILT_IN_LFLOOR: + case BUILT_IN_LFLOORF: + case BUILT_IN_LFLOORL: + case BUILT_IN_LLFLOOR: + case BUILT_IN_LLFLOORF: + case BUILT_IN_LLFLOORL: + target = expand_builtin_int_roundingfn (exp, target, subtarget); + if (target) + return target; + break; + case BUILT_IN_POW: case BUILT_IN_POWF: case BUILT_IN_POWL: @@ -5252,9 +5607,20 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; + case BUILT_IN_POWI: + case BUILT_IN_POWIF: + case BUILT_IN_POWIL: + 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: @@ -5329,8 +5695,9 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, /* Return the address of the first anonymous stack arg. */ case BUILT_IN_NEXT_ARG: - fold_builtin_next_arg (arglist); - return expand_builtin_next_arg (arglist); + if (fold_builtin_next_arg (arglist)) + return const0_rtx; + return expand_builtin_next_arg (); case BUILT_IN_CLASSIFY_TYPE: return expand_builtin_classify_type (arglist); @@ -5422,25 +5789,25 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; case BUILT_IN_STRCPY: - target = expand_builtin_strcpy (arglist, target, mode); + target = expand_builtin_strcpy (exp, target, mode); if (target) return target; break; case BUILT_IN_STRNCPY: - target = expand_builtin_strncpy (arglist, target, mode); + target = expand_builtin_strncpy (exp, target, mode); if (target) return target; break; case BUILT_IN_STPCPY: - target = expand_builtin_stpcpy (arglist, target, mode); + target = expand_builtin_stpcpy (exp, target, mode); if (target) return target; break; case BUILT_IN_STRCAT: - target = expand_builtin_strcat (arglist, target, mode); + target = expand_builtin_strcat (arglist, TREE_TYPE (exp), target, mode); if (target) return target; break; @@ -5464,63 +5831,64 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; case BUILT_IN_STRSTR: - target = expand_builtin_strstr (arglist, target, mode); + target = expand_builtin_strstr (arglist, TREE_TYPE (exp), target, mode); if (target) return target; break; case BUILT_IN_STRPBRK: - target = expand_builtin_strpbrk (arglist, target, mode); + target = expand_builtin_strpbrk (arglist, TREE_TYPE (exp), target, mode); if (target) return target; break; case BUILT_IN_INDEX: case BUILT_IN_STRCHR: - target = expand_builtin_strchr (arglist, target, mode); + target = expand_builtin_strchr (arglist, TREE_TYPE (exp), target, mode); if (target) return target; break; case BUILT_IN_RINDEX: case BUILT_IN_STRRCHR: - target = expand_builtin_strrchr (arglist, target, mode); + target = expand_builtin_strrchr (arglist, TREE_TYPE (exp), target, mode); if (target) return target; break; case BUILT_IN_MEMCPY: - target = expand_builtin_memcpy (arglist, target, mode); + target = expand_builtin_memcpy (exp, target, mode); if (target) return target; break; case BUILT_IN_MEMPCPY: - target = expand_builtin_mempcpy (arglist, target, mode, /*endp=*/ 1); + target = expand_builtin_mempcpy (arglist, TREE_TYPE (exp), target, mode, /*endp=*/ 1); if (target) return target; break; case BUILT_IN_MEMMOVE: - target = expand_builtin_memmove (arglist, 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); + 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; @@ -5565,7 +5933,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, if (value != const1_rtx) { - error ("__builtin_longjmp second argument must be 1"); + error ("%<__builtin_longjmp%> second argument must be 1"); return const0_rtx; } @@ -5597,13 +5965,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; @@ -5620,13 +5988,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; @@ -5708,6 +6076,168 @@ 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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + target = expand_builtin_sync_operation (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: + if (mode == VOIDmode) + mode = TYPE_MODE (boolean_type_node); + if (!target || !register_operand (target, mode)) + target = gen_reg_rtx (mode); + target = expand_builtin_compare_and_swap (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: + target = expand_builtin_compare_and_swap (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: + target = expand_builtin_lock_test_and_set (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: + expand_builtin_lock_release (arglist); + return const0_rtx; + + case BUILT_IN_SYNCHRONIZE: + expand_builtin_synchronize (); + return const0_rtx; + default: /* just do library call, if unknown builtin */ break; } @@ -5808,10 +6338,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 @@ -5909,8 +6446,15 @@ fold_builtin_inf (tree type, int warn) { REAL_VALUE_TYPE real; + /* __builtin_inff is intended to be usable to define INFINITY on all + targets. If an infinity is not available, INFINITY expands "to a + positive constant of type float that overflows at translation + time", footnote "In this case, using INFINITY will violate the + constraint in 6.4.4 and thus require a diagnostic." (C99 7.12#4). + Thus we pedwarn to ensure this constraint violation is + diagnosed. */ if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn) - warning ("target format does not support infinity"); + pedwarn ("target format does not support infinity"); real_inf (&real); return build_real (type, real); @@ -6028,10 +6572,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; @@ -6051,7 +6593,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; @@ -6071,10 +6613,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; @@ -6086,7 +6626,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) { @@ -6244,7 +6784,7 @@ fold_builtin_sqrt (tree arglist, tree type) } } - /* Optimize sqrt(pow(x,y)) = pow(x,y*0.5). */ + /* Optimize sqrt(pow(x,y)) = pow(|x|,y*0.5). */ if (flag_unsafe_math_optimizations && (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF @@ -6253,8 +6793,11 @@ fold_builtin_sqrt (tree arglist, tree type) tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); tree arg1 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); - tree narg1 = fold (build2 (MULT_EXPR, type, arg1, - build_real (type, dconsthalf))); + 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))); arglist = tree_cons (NULL_TREE, arg0, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (powfn, arglist); @@ -6278,40 +6821,82 @@ fold_builtin_cbrt (tree arglist, tree type) if (real_zerop (arg) || real_onep (arg) || real_minus_onep (arg)) return arg; - /* Optimize cbrt(expN(x)) -> expN(x/3). */ - if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode)) - { - 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))); - arglist = build_tree_list (NULL_TREE, arg); - return build_function_call_expr (expfn, arglist); - } - - /* Optimize cbrt(sqrt(x)) -> pow(x,1/6). */ - /* We don't optimize cbrt(cbrt(x)) -> pow(x,1/9) because if - x is negative pow will error but cbrt won't. */ - if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode)) + if (flag_unsafe_math_optimizations) { - tree powfn = mathfn_built_in (type, BUILT_IN_POW); + /* Optimize cbrt(expN(x)) -> expN(x/3). */ + if (BUILTIN_EXPONENT_P (fcode)) + { + 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))); + arglist = build_tree_list (NULL_TREE, arg); + return build_function_call_expr (expfn, arglist); + } - if (powfn) - { - tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); - tree tree_root; - REAL_VALUE_TYPE dconstroot = dconstthird; + /* Optimize cbrt(sqrt(x)) -> pow(x,1/6). */ + if (BUILTIN_SQRT_P (fcode)) + { + tree powfn = mathfn_built_in (type, BUILT_IN_POW); - SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1); - dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot); - tree_root = build_real (type, dconstroot); - arglist = tree_cons (NULL_TREE, arg0, - build_tree_list (NULL_TREE, tree_root)); - return build_function_call_expr (powfn, arglist); + if (powfn) + { + tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); + tree tree_root; + REAL_VALUE_TYPE dconstroot = dconstthird; + + SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1); + dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot); + tree_root = build_real (type, dconstroot); + arglist = tree_cons (NULL_TREE, arg0, + build_tree_list (NULL_TREE, tree_root)); + return build_function_call_expr (powfn, arglist); + } } + /* Optimize cbrt(cbrt(x)) -> pow(x,1/9) iff x is nonnegative. */ + if (BUILTIN_CBRT_P (fcode)) + { + tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); + if (tree_expr_nonnegative_p (arg0)) + { + tree powfn = mathfn_built_in (type, BUILT_IN_POW); + + if (powfn) + { + tree tree_root; + REAL_VALUE_TYPE dconstroot; + + real_arithmetic (&dconstroot, MULT_EXPR, &dconstthird, &dconstthird); + dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot); + tree_root = build_real (type, dconstroot); + arglist = tree_cons (NULL_TREE, arg0, + build_tree_list (NULL_TREE, tree_root)); + return build_function_call_expr (powfn, arglist); + } + } + } + + /* Optimize cbrt(pow(x,y)) -> pow(x,y/3) iff x is nonnegative. */ + if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF + || fcode == BUILT_IN_POWL) + { + tree arg00 = TREE_VALUE (TREE_OPERAND (arg, 1)); + tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); + if (tree_expr_nonnegative_p (arg00)) + { + 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))); + arglist = tree_cons (NULL_TREE, arg00, + build_tree_list (NULL_TREE, narg01)); + return build_function_call_expr (powfn, arglist); + } + } } return NULL_TREE; } @@ -6417,9 +7002,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)) @@ -6430,23 +7014,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)) @@ -6461,7 +7044,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); @@ -6469,16 +7052,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)) @@ -6493,7 +7075,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); @@ -6501,16 +7083,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)) @@ -6525,7 +7106,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); @@ -6533,17 +7114,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)) @@ -6557,11 +7137,44 @@ 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 BUILT_IN_LFLOOR: + case BUILT_IN_LFLOORF: + case BUILT_IN_LFLOORL: + case BUILT_IN_LLFLOOR: + case BUILT_IN_LLFLOORF: + case BUILT_IN_LLFLOORL: + real_floor (&r, TYPE_MODE (ftype), &x); + break; + + case BUILT_IN_LCEIL: + case BUILT_IN_LCEILF: + case BUILT_IN_LCEILL: + case BUILT_IN_LLCEIL: + case BUILT_IN_LLCEILF: + case BUILT_IN_LLCEILL: + real_ceil (&r, TYPE_MODE (ftype), &x); + break; + + case BUILT_IN_LROUND: + case BUILT_IN_LROUNDF: + case BUILT_IN_LROUNDL: + case BUILT_IN_LLROUND: + case BUILT_IN_LLROUNDF: + case BUILT_IN_LLROUNDL: + 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)) @@ -6569,7 +7182,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 @@ -6577,10 +7190,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)) @@ -6672,7 +7283,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; @@ -6698,13 +7309,11 @@ real_dconstp (tree expr, const REAL_VALUE_TYPE *value) function. VALUE is the base of the logN function. */ static tree -fold_builtin_logarithm (tree exp, const REAL_VALUE_TYPE *value) +fold_builtin_logarithm (tree fndecl, tree arglist, + const REAL_VALUE_TYPE *value) { - tree arglist = TREE_OPERAND (exp, 1); - if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { - tree fndecl = get_callee_fndecl (exp); tree type = TREE_TYPE (TREE_TYPE (fndecl)); tree arg = TREE_VALUE (arglist); const enum built_in_function fcode = builtin_mathfn_code (arg); @@ -6816,7 +7425,6 @@ fold_builtin_logarithm (tree exp, const REAL_VALUE_TYPE *value) static tree fold_builtin_pow (tree fndecl, tree arglist, tree type) { - enum built_in_function fcode; tree arg0 = TREE_VALUE (arglist); tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); @@ -6830,7 +7438,10 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) if (TREE_CODE (arg1) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg1)) { + REAL_VALUE_TYPE cint; REAL_VALUE_TYPE c; + HOST_WIDE_INT n; + c = TREE_REAL_CST (arg1); /* Optimize pow(x,0.0) = 1.0. */ @@ -6860,17 +7471,14 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) } } - /* Attempt to evaluate pow at compile-time. */ - if (TREE_CODE (arg0) == REAL_CST - && ! TREE_CONSTANT_OVERFLOW (arg0)) + /* Check for an integer exponent. */ + n = real_to_integer (&c); + real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); + if (real_identical (&c, &cint)) { - REAL_VALUE_TYPE cint; - HOST_WIDE_INT n; - - n = real_to_integer (&c); - real_from_integer (&cint, VOIDmode, n, - n < 0 ? -1 : 0, 0); - if (real_identical (&c, &cint)) + /* Attempt to evaluate pow at compile-time. */ + if (TREE_CODE (arg0) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg0)) { REAL_VALUE_TYPE x; bool inexact; @@ -6880,45 +7488,123 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) if (flag_unsafe_math_optimizations || !inexact) return build_real (type, x); } + + /* Strip sign ops from even integer powers. */ + if ((n & 1) == 0 && flag_unsafe_math_optimizations) + { + tree narg0 = fold_strip_sign_ops (arg0); + if (narg0) + { + arglist = build_tree_list (NULL_TREE, arg1); + arglist = tree_cons (NULL_TREE, narg0, arglist); + return build_function_call_expr (fndecl, arglist); + } + } } } - /* Optimize pow(expN(x),y) = expN(x*y). */ - fcode = builtin_mathfn_code (arg0); - if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode)) + if (flag_unsafe_math_optimizations) { - 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)); - arglist = build_tree_list (NULL_TREE, arg); - return build_function_call_expr (expfn, arglist); - } + const enum built_in_function fcode = builtin_mathfn_code (arg0); - /* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */ - if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode)) - { - tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1)); - tree narg1 = fold (build2 (MULT_EXPR, type, arg1, - build_real (type, dconsthalf))); + /* Optimize pow(expN(x),y) = expN(x*y). */ + if (BUILTIN_EXPONENT_P (fcode)) + { + 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)); + arglist = build_tree_list (NULL_TREE, arg); + return build_function_call_expr (expfn, arglist); + } - arglist = tree_cons (NULL_TREE, narg0, - build_tree_list (NULL_TREE, narg1)); - return build_function_call_expr (fndecl, arglist); + /* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */ + 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))); + + arglist = tree_cons (NULL_TREE, narg0, + build_tree_list (NULL_TREE, narg1)); + return build_function_call_expr (fndecl, arglist); + } + + /* Optimize pow(cbrt(x),y) = pow(x,y/3) iff x is nonnegative. */ + if (BUILTIN_CBRT_P (fcode)) + { + tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1)); + if (tree_expr_nonnegative_p (arg)) + { + const REAL_VALUE_TYPE dconstroot + = real_value_truncate (TYPE_MODE (type), dconstthird); + 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); + } + } + + /* Optimize pow(pow(x,y),z) = pow(x,y*z). */ + if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF + || fcode == BUILT_IN_POWL) + { + 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)); + arglist = tree_cons (NULL_TREE, arg00, + build_tree_list (NULL_TREE, narg1)); + return build_function_call_expr (fndecl, arglist); + } } - /* Optimize pow(pow(x,y),z) = pow(x,y*z). */ - if (flag_unsafe_math_optimizations - && (fcode == BUILT_IN_POW - || fcode == BUILT_IN_POWF - || fcode == BUILT_IN_POWL)) + return NULL_TREE; +} + +/* Fold a builtin function call to powi, powif, or powil. Return + NULL_TREE if no simplification can be made. */ +static tree +fold_builtin_powi (tree fndecl ATTRIBUTE_UNUSED, tree arglist, tree type) +{ + tree arg0 = TREE_VALUE (arglist); + tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + + if (!validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE)) + return NULL_TREE; + + /* Optimize pow(1.0,y) = 1.0. */ + if (real_onep (arg0)) + return omit_one_operand (type, build_real (type, dconst1), arg1); + + if (host_integerp (arg1, 0)) { - 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)); - arglist = tree_cons (NULL_TREE, arg00, - build_tree_list (NULL_TREE, narg1)); - return build_function_call_expr (fndecl, arglist); + HOST_WIDE_INT c = TREE_INT_CST_LOW (arg1); + + /* Evaluate powi at compile-time. */ + if (TREE_CODE (arg0) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg0)) + { + REAL_VALUE_TYPE x; + x = TREE_REAL_CST (arg0); + real_powi (&x, TYPE_MODE (type), &x, c); + return build_real (type, x); + } + + /* Optimize pow(x,0) = 1.0. */ + if (c == 0) + return omit_one_operand (type, build_real (type, dconst1), + arg0); + + /* Optimize pow(x,1) = x. */ + if (c == 1) + return arg0; + + /* Optimize pow(x,-1) = 1.0/x. */ + if (c == -1) + return fold (build2 (RDIV_EXPR, type, + build_real (type, dconst1), arg0)); } + return NULL_TREE; } @@ -6927,13 +7613,11 @@ fold_builtin_pow (tree fndecl, tree arglist, tree type) VALUE is the value which will be raised to a power. */ static tree -fold_builtin_exponent (tree exp, const REAL_VALUE_TYPE *value) +fold_builtin_exponent (tree fndecl, tree arglist, + const REAL_VALUE_TYPE *value) { - tree arglist = TREE_OPERAND (exp, 1); - if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { - tree fndecl = get_callee_fndecl (exp); tree type = TREE_TYPE (TREE_TYPE (fndecl)); tree arg = TREE_VALUE (arglist); @@ -7000,9 +7684,8 @@ fold_builtin_exponent (tree exp, const REAL_VALUE_TYPE *value) 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, @@ -7015,11 +7698,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; } @@ -7028,31 +7711,34 @@ fold_builtin_memcpy (tree exp) NULL_TREE if no simplification can be made. */ static tree -fold_builtin_mempcpy (tree exp) +fold_builtin_mempcpy (tree arglist, tree type, int endp) { - tree arglist = TREE_OPERAND (exp, 1); - tree dest, src, len; - - if (!validate_arglist (arglist, - POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) - return 0; + if (validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + { + tree dest = TREE_VALUE (arglist); + tree src = TREE_VALUE (TREE_CHAIN (arglist)); + tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); - dest = TREE_VALUE (arglist); - src = TREE_VALUE (TREE_CHAIN (arglist)); - len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + /* If the LEN parameter is zero, return DEST. */ + if (integer_zerop (len)) + return omit_one_operand (type, dest, src); - /* If the LEN parameter is zero, return DEST. */ - if (integer_zerop (len)) - return omit_one_operand (TREE_TYPE (exp), dest, src); + /* If SRC and DEST are the same (and not volatile), return DEST+LEN. */ + if (operand_equal_p (src, dest, 0)) + { + if (endp == 0) + return omit_one_operand (type, dest, len); - /* If SRC and DEST are the same (and not volatile), return DEST+LEN. */ - if (operand_equal_p (src, dest, 0)) - { - tree temp = fold_convert (TREE_TYPE (dest), len); - temp = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp)); - return fold_convert (TREE_TYPE (exp), temp); + if (endp == 2) + 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)); + return fold_convert (type, len); + } } - return 0; } @@ -7060,9 +7746,8 @@ fold_builtin_mempcpy (tree exp) NULL_TREE if no simplification can be made. */ static tree -fold_builtin_memmove (tree exp) +fold_builtin_memmove (tree arglist, tree type) { - tree arglist = TREE_OPERAND (exp, 1); tree dest, src, len; if (!validate_arglist (arglist, @@ -7075,11 +7760,11 @@ fold_builtin_memmove (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 (type, 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 (type, dest, len); return 0; } @@ -7089,9 +7774,8 @@ fold_builtin_memmove (tree exp) 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, @@ -7103,7 +7787,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; @@ -7123,7 +7807,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)); } @@ -7132,9 +7816,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, @@ -7147,7 +7830,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. */ @@ -7173,7 +7856,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)); } @@ -7394,9 +8077,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)) @@ -7412,16 +8095,16 @@ 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, + return fold (build2 (LT_EXPR, type, arg, build_real (TREE_TYPE (arg), dconst0))); return NULL_TREE; @@ -7431,9 +8114,9 @@ fold_builtin_signbit (tree exp) Return NULL_TREE if no simplification can be made. */ static tree -fold_builtin_copysign (tree arglist, tree type) +fold_builtin_copysign (tree fndecl, tree arglist, tree type) { - tree arg1, arg2; + tree arg1, arg2, tem; if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) return NULL_TREE; @@ -7467,6 +8150,14 @@ fold_builtin_copysign (tree arglist, tree type) fold (build1 (ABS_EXPR, type, arg1)), arg2); + /* Strip sign changing operations for the first argument. */ + tem = fold_strip_sign_ops (arg1); + if (tem) + { + arglist = tree_cons (NULL_TREE, tem, TREE_CHAIN (arglist)); + return build_function_call_expr (fndecl, arglist); + } + return NULL_TREE; } @@ -7522,11 +8213,18 @@ fold_builtin_isdigit (tree arglist) else { /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9. */ - /* According to the C standard, isdigit is unaffected by locale. */ - tree arg = TREE_VALUE (arglist); - arg = fold_convert (unsigned_type_node, arg); + /* According to the C standard, isdigit is unaffected by locale. + However, it definitely is affected by the target character set. */ + tree arg; + unsigned HOST_WIDE_INT target_digit0 + = lang_hooks.to_target_charset ('0'); + + if (target_digit0 == 0) + return NULL_TREE; + + 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)); + 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); @@ -7548,6 +8246,7 @@ fold_builtin_fabs (tree arglist, tree type) return 0; arg = TREE_VALUE (arglist); + arg = fold_convert (type, arg); if (TREE_CODE (arg) == REAL_CST) return fold_abs_const (arg, type); return fold (build1 (ABS_EXPR, type, arg)); @@ -7564,6 +8263,7 @@ fold_builtin_abs (tree arglist, tree type) return 0; arg = TREE_VALUE (arglist); + arg = fold_convert (type, arg); if (TREE_CODE (arg) == INTEGER_CST) return fold_abs_const (arg, type); return fold (build1 (ABS_EXPR, type, arg)); @@ -7573,10 +8273,8 @@ fold_builtin_abs (tree arglist, tree type) 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; @@ -7656,29 +8354,27 @@ fold_builtin_classify (tree exp, int builtin_index) } /* Fold a call to an unordered comparison function such as - __builtin_isgreater(). EXP is the CALL_EXPR for the call. + __builtin_isgreater(). FNDECL is the FUNCTION_DECL for the function + being called and ARGLIST is the argument list for the call. UNORDERED_CODE and ORDERED_CODE are comparison codes that give the opposite of the desired result. UNORDERED_CODE is used for modes that can hold NaNs and ORDERED_CODE is used for the rest. */ static tree -fold_builtin_unordered_cmp (tree exp, +fold_builtin_unordered_cmp (tree fndecl, tree arglist, enum tree_code unordered_code, enum tree_code ordered_code) { - tree fndecl = get_callee_fndecl (exp); - tree arglist = TREE_OPERAND (exp, 1); tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum tree_code code; tree arg0, arg1; + tree type0, type1; + enum tree_code code0, code1; + tree cmp_type = NULL_TREE; if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) { - enum tree_code code0, code1; - tree type0, type1; - tree cmp_type = 0; - /* Check that we have exactly two arguments. */ if (arglist == 0 || TREE_CHAIN (arglist) == 0) { @@ -7692,39 +8388,34 @@ fold_builtin_unordered_cmp (tree exp, IDENTIFIER_POINTER (DECL_NAME (fndecl))); return error_mark_node; } - - arg0 = TREE_VALUE (arglist); - arg1 = TREE_VALUE (TREE_CHAIN (arglist)); - - type0 = TREE_TYPE (arg0); - type1 = TREE_TYPE (arg1); - - code0 = TREE_CODE (type0); - code1 = TREE_CODE (type1); - - if (code0 == REAL_TYPE && code1 == REAL_TYPE) - /* Choose the wider of two real types. */ - cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1) - ? type0 : type1; - else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE) - cmp_type = type0; - else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE) - cmp_type = type1; - else - { - error ("non-floating-point argument to function %qs", - IDENTIFIER_POINTER (DECL_NAME (fndecl))); - return error_mark_node; - } - - arg0 = fold_convert (cmp_type, arg0); - arg1 = fold_convert (cmp_type, arg1); } + + arg0 = TREE_VALUE (arglist); + arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + + type0 = TREE_TYPE (arg0); + type1 = TREE_TYPE (arg1); + + code0 = TREE_CODE (type0); + code1 = TREE_CODE (type1); + + if (code0 == REAL_TYPE && code1 == REAL_TYPE) + /* Choose the wider of two real types. */ + cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1) + ? type0 : type1; + else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE) + cmp_type = type0; + else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE) + cmp_type = type1; else { - arg0 = TREE_VALUE (arglist); - arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + error ("non-floating-point argument to function %qs", + IDENTIFIER_POINTER (DECL_NAME (fndecl))); + return error_mark_node; } + + arg0 = fold_convert (cmp_type, arg0); + arg1 = fold_convert (cmp_type, arg1); if (unordered_code == UNORDERED_EXPR) { @@ -7739,22 +8430,60 @@ fold_builtin_unordered_cmp (tree exp, 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); +} + /* Used by constant folding to simplify calls to builtin functions. EXP is the CALL_EXPR of a call to a builtin function. IGNORE is true if the result of the function call is ignored. This function returns NULL_TREE 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); - switch (DECL_FUNCTION_CODE (fndecl)) + fcode = DECL_FUNCTION_CODE (fndecl); + switch (fcode) { case BUILT_IN_FPUTS: return fold_builtin_fputs (arglist, ignore, false, NULL_TREE); @@ -7763,7 +8492,7 @@ fold_builtin_1 (tree exp, bool ignore) return fold_builtin_fputs (arglist, ignore, true, NULL_TREE); case BUILT_IN_STRSTR: - return fold_builtin_strstr (arglist); + return fold_builtin_strstr (arglist, type); case BUILT_IN_STRCAT: return fold_builtin_strcat (arglist); @@ -7779,17 +8508,17 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_STRCHR: case BUILT_IN_INDEX: - return fold_builtin_strchr (arglist); + return fold_builtin_strchr (arglist, type); case BUILT_IN_STRRCHR: case BUILT_IN_RINDEX: - return fold_builtin_strrchr (arglist); + 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); @@ -7798,7 +8527,7 @@ fold_builtin_1 (tree exp, bool ignore) return fold_builtin_strncmp (arglist); case BUILT_IN_STRPBRK: - return fold_builtin_strpbrk (arglist); + return fold_builtin_strpbrk (arglist, type); case BUILT_IN_BCMP: case BUILT_IN_MEMCMP: @@ -7892,12 +8621,12 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_EXP: case BUILT_IN_EXPF: case BUILT_IN_EXPL: - return fold_builtin_exponent (exp, &dconste); + return fold_builtin_exponent (fndecl, arglist, &dconste); case BUILT_IN_EXP2: case BUILT_IN_EXP2F: case BUILT_IN_EXP2L: - return fold_builtin_exponent (exp, &dconst2); + return fold_builtin_exponent (fndecl, arglist, &dconst2); case BUILT_IN_EXP10: case BUILT_IN_EXP10F: @@ -7905,22 +8634,22 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_POW10: case BUILT_IN_POW10F: case BUILT_IN_POW10L: - return fold_builtin_exponent (exp, &dconst10); + return fold_builtin_exponent (fndecl, arglist, &dconst10); case BUILT_IN_LOG: case BUILT_IN_LOGF: case BUILT_IN_LOGL: - return fold_builtin_logarithm (exp, &dconste); + return fold_builtin_logarithm (fndecl, arglist, &dconste); case BUILT_IN_LOG2: case BUILT_IN_LOG2F: case BUILT_IN_LOG2L: - return fold_builtin_logarithm (exp, &dconst2); + return fold_builtin_logarithm (fndecl, arglist, &dconst2); case BUILT_IN_LOG10: case BUILT_IN_LOG10F: case BUILT_IN_LOG10L: - return fold_builtin_logarithm (exp, &dconst10); + return fold_builtin_logarithm (fndecl, arglist, &dconst10); case BUILT_IN_TAN: case BUILT_IN_TANF: @@ -7937,6 +8666,11 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_POWL: return fold_builtin_pow (fndecl, arglist, type); + case BUILT_IN_POWI: + case BUILT_IN_POWIF: + case BUILT_IN_POWIL: + return fold_builtin_powi (fndecl, arglist, type); + case BUILT_IN_INF: case BUILT_IN_INFF: case BUILT_IN_INFL: @@ -7960,22 +8694,22 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_FLOOR: case BUILT_IN_FLOORF: case BUILT_IN_FLOORL: - return fold_builtin_floor (exp); + return fold_builtin_floor (fndecl, arglist); case BUILT_IN_CEIL: case BUILT_IN_CEILF: case BUILT_IN_CEILL: - return fold_builtin_ceil (exp); + return fold_builtin_ceil (fndecl, arglist); case BUILT_IN_TRUNC: case BUILT_IN_TRUNCF: case BUILT_IN_TRUNCL: - return fold_builtin_trunc (exp); + return fold_builtin_trunc (fndecl, arglist); case BUILT_IN_ROUND: case BUILT_IN_ROUNDF: case BUILT_IN_ROUNDL: - return fold_builtin_round (exp); + return fold_builtin_round (fndecl, arglist); case BUILT_IN_NEARBYINT: case BUILT_IN_NEARBYINTF: @@ -7983,15 +8717,27 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_RINT: case BUILT_IN_RINTF: case BUILT_IN_RINTL: - return fold_trunc_transparent_mathfn (exp); - + return fold_trunc_transparent_mathfn (fndecl, arglist); + + case BUILT_IN_LCEIL: + case BUILT_IN_LCEILF: + case BUILT_IN_LCEILL: + case BUILT_IN_LLCEIL: + case BUILT_IN_LLCEILF: + case BUILT_IN_LLCEILL: + case BUILT_IN_LFLOOR: + case BUILT_IN_LFLOORF: + case BUILT_IN_LFLOORL: + case BUILT_IN_LLFLOOR: + case BUILT_IN_LLFLOORF: + case BUILT_IN_LLFLOORL: 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); + return fold_builtin_int_roundingfn (fndecl, arglist); case BUILT_IN_LRINT: case BUILT_IN_LRINTF: @@ -7999,7 +8745,7 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_LLRINT: case BUILT_IN_LLRINTF: case BUILT_IN_LLRINTL: - return fold_fixed_mathfn (exp); + return fold_fixed_mathfn (fndecl, arglist); case BUILT_IN_FFS: case BUILT_IN_FFSL: @@ -8016,21 +8762,21 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_PARITY: case BUILT_IN_PARITYL: case BUILT_IN_PARITYLL: - return fold_builtin_bitop (exp); + 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 (exp); + return fold_builtin_mempcpy (arglist, type, /*endp=*/1); case BUILT_IN_MEMMOVE: - return fold_builtin_memmove (exp); + return fold_builtin_memmove (arglist, type); case BUILT_IN_SIGNBIT: case BUILT_IN_SIGNBITF: case BUILT_IN_SIGNBITL: - return fold_builtin_signbit (exp); + return fold_builtin_signbit (fndecl, arglist); case BUILT_IN_ISASCII: return fold_builtin_isascii (arglist); @@ -8044,41 +8790,48 @@ fold_builtin_1 (tree exp, bool ignore) case BUILT_IN_COPYSIGN: case BUILT_IN_COPYSIGNF: case BUILT_IN_COPYSIGNL: - return fold_builtin_copysign (arglist, type); + 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); + 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); + 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); + return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISNAN); case BUILT_IN_ISGREATER: - return fold_builtin_unordered_cmp (exp, UNLE_EXPR, LE_EXPR); + return fold_builtin_unordered_cmp (fndecl, arglist, UNLE_EXPR, LE_EXPR); case BUILT_IN_ISGREATEREQUAL: - return fold_builtin_unordered_cmp (exp, UNLT_EXPR, LT_EXPR); + return fold_builtin_unordered_cmp (fndecl, arglist, UNLT_EXPR, LT_EXPR); case BUILT_IN_ISLESS: - return fold_builtin_unordered_cmp (exp, UNGE_EXPR, GE_EXPR); + return fold_builtin_unordered_cmp (fndecl, arglist, UNGE_EXPR, GE_EXPR); case BUILT_IN_ISLESSEQUAL: - return fold_builtin_unordered_cmp (exp, UNGT_EXPR, GT_EXPR); + return fold_builtin_unordered_cmp (fndecl, arglist, UNGT_EXPR, GT_EXPR); case BUILT_IN_ISLESSGREATER: - return fold_builtin_unordered_cmp (exp, UNEQ_EXPR, EQ_EXPR); + return fold_builtin_unordered_cmp (fndecl, arglist, UNEQ_EXPR, EQ_EXPR); case BUILT_IN_ISUNORDERED: - return fold_builtin_unordered_cmp (exp, UNORDERED_EXPR, NOP_EXPR); + return fold_builtin_unordered_cmp (fndecl, arglist, UNORDERED_EXPR, + NOP_EXPR); /* We do the folding for va_start in the expander. */ 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; } @@ -8090,9 +8843,9 @@ 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. */ @@ -8149,8 +8902,14 @@ validate_arglist (tree arglist, ...) /* If no parameters remain or the parameter's code does not match the specified code, return false. Otherwise continue checking any remaining arguments. */ - if (arglist == 0 - || code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist)))) + if (arglist == 0) + goto end; + if (code == POINTER_TYPE) + { + if (! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))) + goto end; + } + else if (code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist)))) goto end; break; } @@ -8222,7 +8981,7 @@ readonly_data_expr (tree exp) form of the builtin function call. */ static tree -fold_builtin_strstr (tree arglist) +fold_builtin_strstr (tree arglist, tree type) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; @@ -8240,13 +8999,15 @@ fold_builtin_strstr (tree arglist) if (p1 != NULL) { const char *r = strstr (p1, p2); + tree tem; if (r == NULL) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - return 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); } if (p2[0] == '\0') @@ -8286,7 +9047,7 @@ fold_builtin_strstr (tree arglist) form of the builtin function call. */ static tree -fold_builtin_strchr (tree arglist) +fold_builtin_strchr (tree arglist, tree type) { if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; @@ -8303,6 +9064,7 @@ fold_builtin_strchr (tree arglist) { char c; const char *r; + tree tem; if (target_char_cast (s2, &c)) return 0; @@ -8313,8 +9075,9 @@ fold_builtin_strchr (tree arglist) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - return 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; } @@ -8338,7 +9101,7 @@ fold_builtin_strchr (tree arglist) form of the builtin function call. */ static tree -fold_builtin_strrchr (tree arglist) +fold_builtin_strrchr (tree arglist, tree type) { if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; @@ -8356,6 +9119,7 @@ fold_builtin_strrchr (tree arglist) { char c; const char *r; + tree tem; if (target_char_cast (s2, &c)) return 0; @@ -8366,8 +9130,9 @@ fold_builtin_strrchr (tree arglist) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - return 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); } if (! integer_zerop (s2)) @@ -8400,7 +9165,7 @@ fold_builtin_strrchr (tree arglist) form of the builtin function call. */ static tree -fold_builtin_strpbrk (tree arglist) +fold_builtin_strpbrk (tree arglist, tree type) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; @@ -8418,13 +9183,15 @@ fold_builtin_strpbrk (tree arglist) if (p1 != NULL) { const char *r = strpbrk (p1, p2); + tree tem; if (r == NULL) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - return 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); } if (p2[0] == '\0') @@ -8727,7 +9494,10 @@ fold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len) return build_function_call_expr (fn, arglist); } -static void +/* Fold the new_arg's arguments (ARGLIST). Returns true if there was an error + produced. False otherwise. This is done so that we don't output the error + or warning twice or three times. */ +bool fold_builtin_next_arg (tree arglist) { tree fntype = TREE_TYPE (current_function_decl); @@ -8735,12 +9505,33 @@ fold_builtin_next_arg (tree arglist) if (TYPE_ARG_TYPES (fntype) == 0 || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) == void_type_node)) - error ("% used in function with fixed args"); - else if (arglist) + { + error ("% used in function with fixed args"); + return true; + } + else if (!arglist) + { + /* Evidently an out of date version of ; can't validate + va_start's second argument, but can still work as intended. */ + 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) + when we checked the arguments and if needed issued a warning. */ + else if (!TREE_CHAIN (arglist) + || !integer_zerop (TREE_VALUE (arglist)) + || !integer_zerop (TREE_VALUE (TREE_CHAIN (arglist))) + || TREE_CHAIN (TREE_CHAIN (arglist))) { tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl)); tree arg = TREE_VALUE (arglist); + if (TREE_CHAIN (arglist)) + { + error ("% used with too many arguments"); + return true; + } + /* Strip off all nops for the sake of the comparison. This is not quite the same as STRIP_NOPS. It does more. We must also strip off INDIRECT_EXPR for C++ reference @@ -8751,13 +9542,23 @@ fold_builtin_next_arg (tree arglist) || TREE_CODE (arg) == INDIRECT_REF) arg = TREE_OPERAND (arg, 0); if (arg != last_parm) - warning ("second parameter of % not last named argument"); - TREE_VALUE (arglist) = arg; + { + /* FIXME: Sometimes with the tree optimizers we can get the + not the last argument even though the user used the last + argument. We just warn and set the arg to be the last + argument so that we will get wrong-code because of + it. */ + 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, + as otherwise we could warn even for correct code like: + void foo (int i, ...) + { va_list ap; i++; va_start (ap, i); va_end (ap); } */ + TREE_VALUE (arglist) = integer_zero_node; + TREE_CHAIN (arglist) = build_tree_list (NULL, integer_zero_node); } - else - /* 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"); + return false; }