diff options
Diffstat (limited to 'contrib/gcc/builtins.c')
-rw-r--r-- | contrib/gcc/builtins.c | 4429 |
1 files changed, 3521 insertions, 908 deletions
diff --git a/contrib/gcc/builtins.c b/contrib/gcc/builtins.c index d088274..dadb6cd 100644 --- a/contrib/gcc/builtins.c +++ b/contrib/gcc/builtins.c @@ -1,6 +1,6 @@ /* Expand builtin functions. - Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GCC. @@ -21,6 +21,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "machmode.h" #include "real.h" #include "rtl.h" @@ -62,7 +64,7 @@ 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) STRINGX(X), +#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM) #X, const char *const built_in_names[(int) END_BUILTINS] = { #include "builtins.def" @@ -72,86 +74,97 @@ const char *const built_in_names[(int) END_BUILTINS] = /* Setup an array of _DECL trees, make sure each element is initialized to NULL_TREE. */ tree built_in_decls[(int) END_BUILTINS]; - -static int get_pointer_alignment PARAMS ((tree, unsigned int)); -static tree c_strlen PARAMS ((tree)); -static const char *c_getstr PARAMS ((tree)); -static rtx c_readstr PARAMS ((const char *, - enum machine_mode)); -static int target_char_cast PARAMS ((tree, char *)); -static rtx get_memory_rtx PARAMS ((tree)); -static int apply_args_size PARAMS ((void)); -static int apply_result_size PARAMS ((void)); +/* Declarations used when constructing the builtin implicitly in the compiler. + It may be NULL_TREE when this is invalid (for instance runtime is not + required to implement the function call in all cases. */ +tree implicit_built_in_decls[(int) END_BUILTINS]; + +static int get_pointer_alignment (tree, unsigned int); +static tree c_strlen (tree, int); +static const char *c_getstr (tree); +static rtx c_readstr (const char *, enum machine_mode); +static int target_char_cast (tree, char *); +static rtx get_memory_rtx (tree); +static tree build_string_literal (int, const char *); +static int apply_args_size (void); +static int apply_result_size (void); #if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) -static rtx result_vector PARAMS ((int, rtx)); +static rtx result_vector (int, rtx); #endif -static rtx expand_builtin_setjmp PARAMS ((tree, rtx)); -static void expand_builtin_prefetch PARAMS ((tree)); -static rtx expand_builtin_apply_args PARAMS ((void)); -static rtx expand_builtin_apply_args_1 PARAMS ((void)); -static rtx expand_builtin_apply PARAMS ((rtx, rtx, rtx)); -static void expand_builtin_return PARAMS ((rtx)); -static enum type_class type_to_class PARAMS ((tree)); -static rtx expand_builtin_classify_type PARAMS ((tree)); -static rtx expand_builtin_mathfn PARAMS ((tree, rtx, rtx)); -static rtx expand_builtin_constant_p PARAMS ((tree)); -static rtx expand_builtin_args_info PARAMS ((tree)); -static rtx expand_builtin_next_arg PARAMS ((tree)); -static rtx expand_builtin_va_start PARAMS ((tree)); -static rtx expand_builtin_va_end PARAMS ((tree)); -static rtx expand_builtin_va_copy PARAMS ((tree)); -static rtx expand_builtin_memcmp PARAMS ((tree, tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strcmp PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strncmp PARAMS ((tree, rtx, - enum machine_mode)); -static rtx builtin_memcpy_read_str PARAMS ((PTR, HOST_WIDE_INT, - enum machine_mode)); -static rtx expand_builtin_strcat PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strncat PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strspn PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strcspn PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_memcpy PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strcpy PARAMS ((tree, rtx, - enum machine_mode)); -static rtx builtin_strncpy_read_str PARAMS ((PTR, HOST_WIDE_INT, - enum machine_mode)); -static rtx expand_builtin_strncpy PARAMS ((tree, rtx, - enum machine_mode)); -static rtx builtin_memset_read_str PARAMS ((PTR, HOST_WIDE_INT, - enum machine_mode)); -static rtx builtin_memset_gen_str PARAMS ((PTR, HOST_WIDE_INT, - enum machine_mode)); -static rtx expand_builtin_memset PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_bzero PARAMS ((tree)); -static rtx expand_builtin_strlen PARAMS ((tree, rtx)); -static rtx expand_builtin_strstr PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strpbrk PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strchr PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_strrchr PARAMS ((tree, rtx, - enum machine_mode)); -static rtx expand_builtin_alloca PARAMS ((tree, rtx)); -static rtx expand_builtin_ffs PARAMS ((tree, rtx, rtx)); -static rtx expand_builtin_frame_address PARAMS ((tree)); -static rtx expand_builtin_fputs PARAMS ((tree, int, int)); -static tree stabilize_va_list PARAMS ((tree, int)); -static rtx expand_builtin_expect PARAMS ((tree, rtx)); -static tree fold_builtin_constant_p PARAMS ((tree)); -static tree fold_builtin_classify_type PARAMS ((tree)); -static tree fold_builtin_inf PARAMS ((tree, int)); -static tree fold_builtin_nan PARAMS ((tree, tree, int)); -static tree build_function_call_expr PARAMS ((tree, tree)); -static int validate_arglist PARAMS ((tree, ...)); +static rtx expand_builtin_setjmp (tree, rtx); +static void expand_builtin_prefetch (tree); +static rtx expand_builtin_apply_args (void); +static rtx expand_builtin_apply_args_1 (void); +static rtx expand_builtin_apply (rtx, rtx, rtx); +static void expand_builtin_return (rtx); +static enum type_class type_to_class (tree); +static rtx expand_builtin_classify_type (tree); +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_constant_p (tree, enum machine_mode); +static rtx expand_builtin_args_info (tree); +static rtx expand_builtin_next_arg (tree); +static rtx expand_builtin_va_start (tree); +static rtx expand_builtin_va_end (tree); +static rtx expand_builtin_va_copy (tree); +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_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_bcopy (tree); +static rtx expand_builtin_strcpy (tree, rtx, enum machine_mode); +static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode); +static rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode); +static rtx expand_builtin_strncpy (tree, rtx, enum machine_mode); +static rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode); +static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode); +static rtx expand_builtin_memset (tree, rtx, enum machine_mode); +static rtx expand_builtin_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_alloca (tree, rtx); +static rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab); +static rtx expand_builtin_frame_address (tree, tree); +static rtx expand_builtin_fputs (tree, rtx, bool); +static rtx expand_builtin_printf (tree, rtx, enum machine_mode, bool); +static rtx expand_builtin_fprintf (tree, rtx, enum machine_mode, bool); +static rtx expand_builtin_sprintf (tree, rtx, enum machine_mode); +static tree stabilize_va_list (tree, int); +static rtx expand_builtin_expect (tree, rtx); +static tree fold_builtin_constant_p (tree); +static tree fold_builtin_classify_type (tree); +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 bool readonly_data_expr (tree); +static rtx expand_builtin_fabs (tree, rtx, rtx); +static rtx expand_builtin_cabs (tree, rtx); +static tree fold_builtin_cabs (tree, tree, tree); +static tree fold_builtin_trunc (tree); +static tree fold_builtin_floor (tree); +static tree fold_builtin_ceil (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_strcpy (tree); +static tree fold_builtin_strncpy (tree); +static tree fold_builtin_memcmp (tree); +static tree fold_builtin_strcmp (tree); +static tree fold_builtin_strncmp (tree); /* Return the alignment in bits of EXP, a pointer valued expression. But don't return more than MAX_ALIGN no matter what. @@ -162,9 +175,7 @@ static int validate_arglist PARAMS ((tree, ...)); expression is actually pointing at an object whose alignment is tighter. */ static int -get_pointer_alignment (exp, max_align) - tree exp; - unsigned int max_align; +get_pointer_alignment (tree exp, unsigned int max_align) { unsigned int align, inner; @@ -227,20 +238,42 @@ get_pointer_alignment (exp, max_align) way, because it could contain a zero byte in the middle. TREE_STRING_LENGTH is the size of the character array, not the string. + ONLY_VALUE should be nonzero if the result is not going to be emitted + into the instruction stream and zero if it is going to be expanded. + E.g. with i++ ? "foo" : "bar", if ONLY_VALUE is nonzero, constant 3 + is returned, otherwise NULL, since + len = c_strlen (src, 1); if (len) expand_expr (len, ...); would not + evaluate the side-effects. + The value returned is of type `ssizetype'. Unfortunately, string_constant can't access the values of const char arrays with initializers, so neither can we do so here. */ static tree -c_strlen (src) - tree src; +c_strlen (tree src, int only_value) { tree offset_node; HOST_WIDE_INT offset; int max; const char *ptr; + STRIP_NOPS (src); + if (TREE_CODE (src) == COND_EXPR + && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) + { + tree len1, len2; + + len1 = c_strlen (TREE_OPERAND (src, 1), only_value); + len2 = c_strlen (TREE_OPERAND (src, 2), only_value); + if (tree_int_cst_equal (len1, len2)) + return len1; + } + + if (TREE_CODE (src) == COMPOUND_EXPR + && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) + return c_strlen (TREE_OPERAND (src, 1), only_value); + src = string_constant (src, &offset_node); if (src == 0) return 0; @@ -299,8 +332,7 @@ c_strlen (src) or sum of string constant and integer constant. */ static const char * -c_getstr (src) - tree src; +c_getstr (tree src) { tree offset_node; @@ -321,9 +353,7 @@ c_getstr (src) GET_MODE_BITSIZE (MODE) bits from string constant STR. */ static rtx -c_readstr (str, mode) - const char *str; - enum machine_mode mode; +c_readstr (const char *str, enum machine_mode mode) { HOST_WIDE_INT c[2]; HOST_WIDE_INT ch; @@ -357,9 +387,7 @@ c_readstr (str, mode) P. */ static int -target_char_cast (cst, p) - tree cst; - char *p; +target_char_cast (tree cst, char *p) { unsigned HOST_WIDE_INT val, hostval; @@ -387,10 +415,8 @@ target_char_cast (cst, p) address located within it (depending on FNDECL_CODE). */ rtx -expand_builtin_return_addr (fndecl_code, count, tem) - enum built_in_function fndecl_code; - int count; - rtx tem; +expand_builtin_return_addr (enum built_in_function fndecl_code, int count, + rtx tem) { int i; @@ -450,9 +476,7 @@ static HOST_WIDE_INT setjmp_alias_set = -1; handling code. */ void -expand_builtin_setjmp_setup (buf_addr, receiver_label) - rtx buf_addr; - rtx receiver_label; +expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) { enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); rtx stack_save; @@ -461,10 +485,7 @@ expand_builtin_setjmp_setup (buf_addr, receiver_label) if (setjmp_alias_set == -1) setjmp_alias_set = new_alias_set (); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (buf_addr) != Pmode) - buf_addr = convert_memory_address (Pmode, buf_addr); -#endif + buf_addr = convert_memory_address (Pmode, buf_addr); buf_addr = force_reg (Pmode, force_operand (buf_addr, NULL_RTX)); @@ -513,8 +534,7 @@ expand_builtin_setjmp_setup (buf_addr, receiver_label) This is used directly by sjlj exception handling code. */ void -expand_builtin_setjmp_receiver (receiver_label) - rtx receiver_label ATTRIBUTE_UNUSED; +expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED) { /* Clobber the FP when we get here, so we have to make sure it's marked as used by this function. */ @@ -586,9 +606,7 @@ expand_builtin_setjmp_receiver (receiver_label) them. */ static rtx -expand_builtin_setjmp (arglist, target) - tree arglist; - rtx target; +expand_builtin_setjmp (tree arglist, rtx target) { rtx buf_addr, next_lab, cont_lab; @@ -606,10 +624,11 @@ expand_builtin_setjmp (arglist, target) expand_builtin_setjmp_setup (buf_addr, next_lab); - /* Set TARGET to zero and branch to the continue label. */ + /* Set TARGET to zero and branch to the continue label. Use emit_jump to + ensure that pending stack adjustments are flushed. */ emit_move_insn (target, const0_rtx); - emit_jump_insn (gen_jump (cont_lab)); - emit_barrier (); + emit_jump (cont_lab); + emit_label (next_lab); expand_builtin_setjmp_receiver (next_lab); @@ -639,8 +658,7 @@ expand_builtin_setjmp (arglist, target) them. */ void -expand_builtin_longjmp (buf_addr, value) - rtx buf_addr, value; +expand_builtin_longjmp (rtx buf_addr, rtx value) { rtx fp, lab, stack, insn, last; enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); @@ -648,10 +666,7 @@ expand_builtin_longjmp (buf_addr, value) if (setjmp_alias_set == -1) setjmp_alias_set = new_alias_set (); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (buf_addr) != Pmode) - buf_addr = convert_memory_address (Pmode, buf_addr); -#endif + buf_addr = convert_memory_address (Pmode, buf_addr); buf_addr = force_reg (Pmode, buf_addr); @@ -695,6 +710,13 @@ expand_builtin_longjmp (buf_addr, value) { lab = copy_to_reg (lab); + emit_insn (gen_rtx_CLOBBER (VOIDmode, + gen_rtx_MEM (BLKmode, + gen_rtx_SCRATCH (VOIDmode)))); + emit_insn (gen_rtx_CLOBBER (VOIDmode, + gen_rtx_MEM (BLKmode, + hard_frame_pointer_rtx))); + emit_move_insn (hard_frame_pointer_rtx, fp); emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX); @@ -729,8 +751,7 @@ expand_builtin_longjmp (buf_addr, value) effects. */ static void -expand_builtin_prefetch (arglist) - tree arglist; +expand_builtin_prefetch (tree arglist) { tree arg0, arg1, arg2; rtx op0, op1, op2; @@ -793,12 +814,9 @@ expand_builtin_prefetch (arglist) if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate) (op0, insn_data[(int) CODE_FOR_prefetch].operand[0].mode)) - || (GET_MODE(op0) != Pmode)) + || (GET_MODE (op0) != Pmode)) { -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE(op0) != Pmode) - op0 = convert_memory_address (Pmode, op0); -#endif + op0 = convert_memory_address (Pmode, op0); op0 = force_reg (Pmode, op0); } emit_insn (gen_prefetch (op0, op1, op2)); @@ -816,16 +834,12 @@ expand_builtin_prefetch (arglist) to be used to be used in a string instruction (cmpstrsi, movstrsi, ..). */ static rtx -get_memory_rtx (exp) - tree exp; +get_memory_rtx (tree exp) { rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_SUM); rtx mem; -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (addr) != Pmode) - addr = convert_memory_address (Pmode, addr); -#endif + addr = convert_memory_address (Pmode, addr); mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr)); @@ -879,8 +893,7 @@ static int apply_args_reg_offset[FIRST_PSEUDO_REGISTER]; needed in objc-act.c. */ int -apply_args_register_offset (regno) - int regno; +apply_args_register_offset (int regno) { apply_args_size (); @@ -896,7 +909,7 @@ apply_args_register_offset (regno) and initialize apply_args_mode. */ static int -apply_args_size () +apply_args_size (void) { static int size = -1; int align; @@ -911,7 +924,7 @@ apply_args_size () /* The second value is the structure value address unless this is passed as an "invisible" first argument. */ - if (struct_value_rtx) + if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0)) size += GET_MODE_SIZE (Pmode); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) @@ -976,7 +989,7 @@ apply_args_size () and initialize apply_result_mode. */ static int -apply_result_size () +apply_result_size (void) { static int size = -1; int align, regno; @@ -1052,14 +1065,12 @@ apply_result_size () restore the values. */ static rtx -result_vector (savep, result) - int savep; - rtx result; +result_vector (int savep, rtx result) { int regno, size, align, nelts; enum machine_mode mode; rtx reg, mem; - rtx *savevec = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx)); + rtx *savevec = alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx)); size = nelts = 0; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) @@ -1083,11 +1094,12 @@ result_vector (savep, result) arguments as were passed to the current function. */ static rtx -expand_builtin_apply_args_1 () +expand_builtin_apply_args_1 (void) { - rtx registers; + rtx registers, tem; int size, align, regno; enum machine_mode mode; + rtx struct_incoming_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 1); /* Create a block where the arg-pointer, structure value address, and argument registers can be saved. */ @@ -1095,15 +1107,13 @@ expand_builtin_apply_args_1 () /* Walk past the arg-pointer and structure value address. */ size = GET_MODE_SIZE (Pmode); - if (struct_value_rtx) + if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0)) size += GET_MODE_SIZE (Pmode); /* Save each register used in calling a function to the block. */ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if ((mode = apply_args_mode[regno]) != VOIDmode) { - rtx tem; - align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; if (size % align != 0) size = CEIL (size, align) * align; @@ -1115,16 +1125,25 @@ expand_builtin_apply_args_1 () } /* Save the arg pointer to the block. */ - emit_move_insn (adjust_address (registers, Pmode, 0), - copy_to_reg (virtual_incoming_args_rtx)); + tem = copy_to_reg (virtual_incoming_args_rtx); +#ifdef STACK_GROWS_DOWNWARD + /* We need the pointer as the caller actually passed them to us, not + as we might have pretended they were passed. Make sure it's a valid + operand, as emit_move_insn isn't expected to handle a PLUS. */ + tem + = force_operand (plus_constant (tem, current_function_pretend_args_size), + NULL_RTX); +#endif + emit_move_insn (adjust_address (registers, Pmode, 0), tem); + size = GET_MODE_SIZE (Pmode); /* Save the structure value address unless this is passed as an "invisible" first argument. */ - if (struct_value_incoming_rtx) + if (struct_incoming_value) { emit_move_insn (adjust_address (registers, Pmode, size), - copy_to_reg (struct_value_incoming_rtx)); + copy_to_reg (struct_incoming_value)); size += GET_MODE_SIZE (Pmode); } @@ -1140,7 +1159,7 @@ expand_builtin_apply_args_1 () saved. */ static rtx -expand_builtin_apply_args () +expand_builtin_apply_args (void) { /* Don't do __builtin_apply_args more than once in a function. Save the result of the first call and reuse it. */ @@ -1175,19 +1194,16 @@ expand_builtin_apply_args () untyped return of whatever value was returned by the given function. */ static rtx -expand_builtin_apply (function, arguments, argsize) - rtx function, arguments, argsize; +expand_builtin_apply (rtx function, rtx arguments, rtx argsize) { int size, align, regno; enum machine_mode mode; rtx incoming_args, result, reg, dest, src, call_insn; rtx old_stack_level = 0; rtx call_fusage = 0; + rtx struct_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (arguments) != Pmode) - arguments = convert_memory_address (Pmode, arguments); -#endif + arguments = convert_memory_address (Pmode, arguments); /* Create a block where the return registers can be saved. */ result = assign_stack_local (BLKmode, apply_result_size (), -1); @@ -1209,7 +1225,7 @@ expand_builtin_apply (function, arguments, argsize) do_pending_stack_adjust (); NO_DEFER_POP; - /* Save the stack with nonlocal if available */ + /* Save the stack with nonlocal if available. */ #ifdef HAVE_save_stack_nonlocal if (HAVE_save_stack_nonlocal) emit_stack_save (SAVE_NONLOCAL, &old_stack_level, NULL_RTX); @@ -1217,12 +1233,16 @@ expand_builtin_apply (function, arguments, argsize) #endif emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); - /* Push a block of memory onto the stack to store the memory arguments. - Save the address in a register, and copy the memory arguments. ??? I - haven't figured out how the calling convention macros effect this, - but it's likely that the source and/or destination addresses in - the block copy will need updating in machine specific ways. */ - dest = allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT); + /* Allocate a block of memory onto the stack and copy the memory + arguments to the outgoing arguments address. */ + allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT); + dest = virtual_outgoing_args_rtx; +#ifndef STACK_GROWS_DOWNWARD + if (GET_CODE (argsize) == CONST_INT) + dest = plus_constant (dest, -INTVAL (argsize)); + else + dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize)); +#endif dest = gen_rtx_MEM (BLKmode, dest); set_mem_align (dest, PARM_BOUNDARY); src = gen_rtx_MEM (BLKmode, incoming_args); @@ -1236,7 +1256,7 @@ expand_builtin_apply (function, arguments, argsize) /* Walk past the arg-pointer and structure value address. */ size = GET_MODE_SIZE (Pmode); - if (struct_value_rtx) + if (struct_value) size += GET_MODE_SIZE (Pmode); /* Restore each of the registers previously saved. Make USE insns @@ -1256,13 +1276,13 @@ expand_builtin_apply (function, arguments, argsize) /* Restore the structure value address unless this is passed as an "invisible" first argument. */ size = GET_MODE_SIZE (Pmode); - if (struct_value_rtx) + if (struct_value) { rtx value = gen_reg_rtx (Pmode); emit_move_insn (value, adjust_address (arguments, Pmode, size)); - emit_move_insn (struct_value_rtx, value); - if (GET_CODE (struct_value_rtx) == REG) - use_reg (&call_fusage, struct_value_rtx); + emit_move_insn (struct_value, value); + if (GET_CODE (struct_value) == REG) + use_reg (&call_fusage, struct_value); size += GET_MODE_SIZE (Pmode); } @@ -1309,29 +1329,10 @@ expand_builtin_apply (function, arguments, argsize) #endif abort (); - /* Find the CALL insn we just emitted. */ - for (call_insn = get_last_insn (); - call_insn && GET_CODE (call_insn) != CALL_INSN; - call_insn = PREV_INSN (call_insn)) - ; - - if (! call_insn) - abort (); - - /* Put the register usage information on the CALL. If there is already - some usage information, put ours at the end. */ - if (CALL_INSN_FUNCTION_USAGE (call_insn)) - { - rtx link; - - for (link = CALL_INSN_FUNCTION_USAGE (call_insn); XEXP (link, 1) != 0; - link = XEXP (link, 1)) - ; - - XEXP (link, 1) = call_fusage; - } - else - CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage; + /* Find the CALL insn we just emitted, and attach the register usage + information. */ + call_insn = last_call_insn (); + add_function_usage_to (call_insn, call_fusage); /* Restore the stack. */ #ifdef HAVE_save_stack_nonlocal @@ -1345,28 +1346,20 @@ expand_builtin_apply (function, arguments, argsize) /* Return the address of the result block. */ result = copy_addr_to_reg (XEXP (result, 0)); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (result) != ptr_mode) - result = convert_memory_address (ptr_mode, result); -#endif - return result; + return convert_memory_address (ptr_mode, result); } /* Perform an untyped return. */ static void -expand_builtin_return (result) - rtx result; +expand_builtin_return (rtx result) { int size, align, regno; enum machine_mode mode; rtx reg; rtx call_fusage = 0; -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (result) != Pmode) - result = convert_memory_address (Pmode, result); -#endif + result = convert_memory_address (Pmode, result); apply_result_size (); result = gen_rtx_MEM (BLKmode, result); @@ -1403,14 +1396,13 @@ expand_builtin_return (result) /* Return whatever values was restored by jumping directly to the end of the function. */ - expand_null_return (); + expand_naked_return (); } /* Used by expand_builtin_classify_type and fold_builtin_classify_type. */ static enum type_class -type_to_class (type) - tree type; +type_to_class (tree type) { switch (TREE_CODE (type)) { @@ -1442,8 +1434,7 @@ type_to_class (type) ARGLIST. */ static rtx -expand_builtin_classify_type (arglist) - tree arglist; +expand_builtin_classify_type (tree arglist) { if (arglist != 0) return GEN_INT (type_to_class (TREE_TYPE (TREE_VALUE (arglist)))); @@ -1453,11 +1444,8 @@ expand_builtin_classify_type (arglist) /* Expand expression EXP, which is a call to __builtin_constant_p. */ static rtx -expand_builtin_constant_p (exp) - tree exp; +expand_builtin_constant_p (tree arglist, enum machine_mode target_mode) { - tree arglist = TREE_OPERAND (exp, 1); - enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp)); rtx tmp; if (arglist == 0) @@ -1465,14 +1453,166 @@ expand_builtin_constant_p (exp) arglist = TREE_VALUE (arglist); /* We have taken care of the easy cases during constant folding. This - case is not obvious, so emit (constant_p_rtx (ARGLIST)) and let CSE get a - chance to see if it can deduce whether ARGLIST is constant. */ + case is not obvious, so emit (constant_p_rtx (ARGLIST)) and let CSE + get a chance to see if it can deduce whether ARGLIST is constant. + If CSE isn't going to run, of course, don't bother waiting. */ + + if (cse_not_expected) + return const0_rtx; + + current_function_calls_constant_p = 1; tmp = expand_expr (arglist, NULL_RTX, VOIDmode, 0); - tmp = gen_rtx_CONSTANT_P_RTX (value_mode, tmp); + tmp = gen_rtx_CONSTANT_P_RTX (target_mode, tmp); return tmp; } +/* This helper macro, meant to be used in mathfn_built_in below, + determines which among a set of three builtin math functions is + appropriate for a given type mode. The `F' and `L' cases are + automatically generated from the `double' case. */ +#define CASE_MATHFN(BUILT_IN_MATHFN) \ + case BUILT_IN_MATHFN: case BUILT_IN_MATHFN##F: case BUILT_IN_MATHFN##L: \ + fcode = BUILT_IN_MATHFN; fcodef = BUILT_IN_MATHFN##F ; \ + fcodel = BUILT_IN_MATHFN##L ; break; + +/* Return mathematic function equivalent to FN but operating directly + on TYPE, if available. If we can't do the conversion, return zero. */ +tree +mathfn_built_in (tree type, enum built_in_function fn) +{ + const enum machine_mode type_mode = TYPE_MODE (type); + enum built_in_function fcode, fcodef, fcodel; + + switch (fn) + { + CASE_MATHFN (BUILT_IN_ACOS) + CASE_MATHFN (BUILT_IN_ACOSH) + CASE_MATHFN (BUILT_IN_ASIN) + CASE_MATHFN (BUILT_IN_ASINH) + CASE_MATHFN (BUILT_IN_ATAN) + CASE_MATHFN (BUILT_IN_ATAN2) + CASE_MATHFN (BUILT_IN_ATANH) + CASE_MATHFN (BUILT_IN_CBRT) + CASE_MATHFN (BUILT_IN_CEIL) + CASE_MATHFN (BUILT_IN_COPYSIGN) + CASE_MATHFN (BUILT_IN_COS) + CASE_MATHFN (BUILT_IN_COSH) + CASE_MATHFN (BUILT_IN_DREM) + CASE_MATHFN (BUILT_IN_ERF) + CASE_MATHFN (BUILT_IN_ERFC) + CASE_MATHFN (BUILT_IN_EXP) + CASE_MATHFN (BUILT_IN_EXP10) + CASE_MATHFN (BUILT_IN_EXP2) + CASE_MATHFN (BUILT_IN_EXPM1) + CASE_MATHFN (BUILT_IN_FABS) + CASE_MATHFN (BUILT_IN_FDIM) + CASE_MATHFN (BUILT_IN_FLOOR) + CASE_MATHFN (BUILT_IN_FMA) + CASE_MATHFN (BUILT_IN_FMAX) + CASE_MATHFN (BUILT_IN_FMIN) + CASE_MATHFN (BUILT_IN_FMOD) + CASE_MATHFN (BUILT_IN_FREXP) + CASE_MATHFN (BUILT_IN_GAMMA) + CASE_MATHFN (BUILT_IN_HUGE_VAL) + CASE_MATHFN (BUILT_IN_HYPOT) + CASE_MATHFN (BUILT_IN_ILOGB) + CASE_MATHFN (BUILT_IN_INF) + CASE_MATHFN (BUILT_IN_J0) + CASE_MATHFN (BUILT_IN_J1) + CASE_MATHFN (BUILT_IN_JN) + CASE_MATHFN (BUILT_IN_LDEXP) + CASE_MATHFN (BUILT_IN_LGAMMA) + CASE_MATHFN (BUILT_IN_LLRINT) + CASE_MATHFN (BUILT_IN_LLROUND) + CASE_MATHFN (BUILT_IN_LOG) + CASE_MATHFN (BUILT_IN_LOG10) + CASE_MATHFN (BUILT_IN_LOG1P) + CASE_MATHFN (BUILT_IN_LOG2) + CASE_MATHFN (BUILT_IN_LOGB) + CASE_MATHFN (BUILT_IN_LRINT) + CASE_MATHFN (BUILT_IN_LROUND) + CASE_MATHFN (BUILT_IN_MODF) + CASE_MATHFN (BUILT_IN_NAN) + CASE_MATHFN (BUILT_IN_NANS) + CASE_MATHFN (BUILT_IN_NEARBYINT) + CASE_MATHFN (BUILT_IN_NEXTAFTER) + CASE_MATHFN (BUILT_IN_NEXTTOWARD) + CASE_MATHFN (BUILT_IN_POW) + CASE_MATHFN (BUILT_IN_POW10) + CASE_MATHFN (BUILT_IN_REMAINDER) + CASE_MATHFN (BUILT_IN_REMQUO) + CASE_MATHFN (BUILT_IN_RINT) + CASE_MATHFN (BUILT_IN_ROUND) + CASE_MATHFN (BUILT_IN_SCALB) + CASE_MATHFN (BUILT_IN_SCALBLN) + CASE_MATHFN (BUILT_IN_SCALBN) + CASE_MATHFN (BUILT_IN_SIGNIFICAND) + CASE_MATHFN (BUILT_IN_SIN) + CASE_MATHFN (BUILT_IN_SINCOS) + CASE_MATHFN (BUILT_IN_SINH) + CASE_MATHFN (BUILT_IN_SQRT) + CASE_MATHFN (BUILT_IN_TAN) + CASE_MATHFN (BUILT_IN_TANH) + CASE_MATHFN (BUILT_IN_TGAMMA) + CASE_MATHFN (BUILT_IN_TRUNC) + CASE_MATHFN (BUILT_IN_Y0) + CASE_MATHFN (BUILT_IN_Y1) + CASE_MATHFN (BUILT_IN_YN) + + default: + return 0; + } + + if (type_mode == TYPE_MODE (double_type_node)) + return implicit_built_in_decls[fcode]; + else if (type_mode == TYPE_MODE (float_type_node)) + return implicit_built_in_decls[fcodef]; + else if (type_mode == TYPE_MODE (long_double_type_node)) + return implicit_built_in_decls[fcodel]; + else + return 0; +} + +/* If errno must be maintained, expand the RTL to check if the result, + TARGET, of a built-in function call, EXP, is NaN, and if so set + errno to EDOM. */ + +static void +expand_errno_check (tree exp, rtx target) +{ + rtx lab = gen_label_rtx (); + + /* Test the result; if it is NaN, set errno=EDOM because + the argument was not in the domain. */ + emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target), + 0, lab); + +#ifdef TARGET_EDOM + /* If this built-in doesn't throw an exception, set errno directly. */ + if (TREE_NOTHROW (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) + { +#ifdef GEN_ERRNO_RTX + rtx errno_rtx = GEN_ERRNO_RTX; +#else + rtx errno_rtx + = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno")); +#endif + emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM)); + emit_label (lab); + return; + } +#endif + + /* We can't set errno=EDOM directly; let the library call do it. + Pop the arguments right away in case the call gets deleted. */ + NO_DEFER_POP; + expand_call (exp, target, 0); + OK_DEFER_POP; + emit_label (lab); +} + + /* Expand a call to one of the builtin math functions (sin, cos, or sqrt). 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 @@ -1480,42 +1620,20 @@ expand_builtin_constant_p (exp) SUBTARGET may be used as the target for computing one of EXP's operands. */ static rtx -expand_builtin_mathfn (exp, target, subtarget) - tree exp; - rtx target, subtarget; +expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) { optab builtin_optab; - rtx op0, insns; - tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + rtx op0, insns, before_call; + tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); - enum machine_mode argmode; + enum machine_mode mode; + bool errno_set = false; + tree arg, narg; if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return 0; - /* Stabilize and compute the argument. */ - if (TREE_CODE (TREE_VALUE (arglist)) != VAR_DECL - && TREE_CODE (TREE_VALUE (arglist)) != PARM_DECL) - { - exp = copy_node (exp); - TREE_OPERAND (exp, 1) = arglist; - /* Wrap the computation of the argument in a SAVE_EXPR. That - way, if we need to expand the argument again (as in the - flag_errno_math case below where we cannot directly set - errno), we will not perform side-effects more than once. - Note that here we're mutating the original EXP as well as the - copy; that's the right thing to do in case the original EXP - is expanded later. */ - TREE_VALUE (arglist) = save_expr (TREE_VALUE (arglist)); - arglist = copy_node (arglist); - } - op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); - - /* Make a suitable register to place result in. */ - target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); - - emit_queue (); - start_sequence (); + arg = TREE_VALUE (arglist); switch (DECL_FUNCTION_CODE (fndecl)) { @@ -1530,68 +1648,239 @@ expand_builtin_mathfn (exp, target, subtarget) case BUILT_IN_SQRT: case BUILT_IN_SQRTF: case BUILT_IN_SQRTL: - builtin_optab = sqrt_optab; break; + errno_set = ! tree_expr_nonnegative_p (arg); + builtin_optab = sqrt_optab; + break; case BUILT_IN_EXP: case BUILT_IN_EXPF: case BUILT_IN_EXPL: - builtin_optab = exp_optab; break; + errno_set = true; builtin_optab = exp_optab; break; case BUILT_IN_LOG: case BUILT_IN_LOGF: case BUILT_IN_LOGL: - builtin_optab = log_optab; break; + errno_set = true; builtin_optab = log_optab; break; + case BUILT_IN_TAN: + case BUILT_IN_TANF: + case BUILT_IN_TANL: + builtin_optab = tan_optab; break; + case BUILT_IN_ATAN: + case BUILT_IN_ATANF: + case BUILT_IN_ATANL: + builtin_optab = atan_optab; break; + case BUILT_IN_FLOOR: + case BUILT_IN_FLOORF: + case BUILT_IN_FLOORL: + builtin_optab = floor_optab; break; + case BUILT_IN_CEIL: + case BUILT_IN_CEILF: + case BUILT_IN_CEILL: + builtin_optab = ceil_optab; break; + case BUILT_IN_TRUNC: + case BUILT_IN_TRUNCF: + case BUILT_IN_TRUNCL: + builtin_optab = btrunc_optab; break; + case BUILT_IN_ROUND: + case BUILT_IN_ROUNDF: + case BUILT_IN_ROUNDL: + builtin_optab = round_optab; break; + case BUILT_IN_NEARBYINT: + case BUILT_IN_NEARBYINTF: + case BUILT_IN_NEARBYINTL: + builtin_optab = nearbyint_optab; break; default: abort (); } - /* Compute into TARGET. - Set TARGET to wherever the result comes back. */ - argmode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))); - target = expand_unop (argmode, builtin_optab, op0, target, 0); + /* Make a suitable register to place result in. */ + mode = TYPE_MODE (TREE_TYPE (exp)); - /* If we were unable to expand via the builtin, stop the - sequence (without outputting the insns) and return 0, causing - a call to the library function. */ - if (target == 0) + if (! flag_errno_math || ! HONOR_NANS (mode)) + errno_set = false; + + /* 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 = save_expr (arg); + if (narg != arg) + { + arglist = build_tree_list (NULL_TREE, arg); + exp = build_function_call_expr (fndecl, arglist); + } + + op0 = expand_expr (arg, subtarget, VOIDmode, 0); + + emit_queue (); + 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) + { + if (errno_set) + expand_errno_check (exp, target); + + /* 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) and call to the library function + with the stabilized argument list. */ end_sequence (); - return 0; } - /* If errno must be maintained, we must set it to EDOM for NaN results. */ + before_call = get_last_insn (); - if (flag_errno_math && HONOR_NANS (argmode)) + target = expand_call (exp, target, target == const0_rtx); + + /* If this is a sqrt operation and we don't care about errno, try to + attach a REG_EQUAL note with a SQRT rtx to the emitted libcall. + This allows the semantics of the libcall to be visible to the RTL + optimizers. */ + if (builtin_optab == sqrt_optab && !errno_set) { - rtx lab1; + /* Search backwards through the insns emitted by expand_call looking + for the instruction with the REG_RETVAL note. */ + rtx last = get_last_insn (); + while (last != before_call) + { + if (find_reg_note (last, REG_RETVAL, NULL)) + { + rtx note = find_reg_note (last, REG_EQUAL, NULL); + /* Check that the REQ_EQUAL note is an EXPR_LIST with + two elements, i.e. symbol_ref(sqrt) and the operand. */ + if (note + && GET_CODE (note) == EXPR_LIST + && GET_CODE (XEXP (note, 0)) == EXPR_LIST + && XEXP (XEXP (note, 0), 1) != NULL_RTX + && XEXP (XEXP (XEXP (note, 0), 1), 1) == NULL_RTX) + { + rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0); + /* Check operand is a register with expected mode. */ + if (operand + && GET_CODE (operand) == REG + && GET_MODE (operand) == mode) + { + /* Replace the REG_EQUAL note with a SQRT rtx. */ + rtx equiv = gen_rtx_SQRT (mode, operand); + set_unique_reg_note (last, REG_EQUAL, equiv); + } + } + break; + } + last = PREV_INSN (last); + } + } + + return target; +} - lab1 = gen_label_rtx (); +/* Expand a call to the builtin binary math functions (pow and atan2). + 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. + SUBTARGET may be used as the target for computing one of EXP's + operands. */ - /* Test the result; if it is NaN, set errno=EDOM because - the argument was not in the domain. */ - emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target), - 0, lab1); +static rtx +expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) +{ + optab builtin_optab; + rtx op0, op1, insns; + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + tree arg0, arg1, temp, narg; + enum machine_mode mode; + bool errno_set = true; + bool stable = true; -#ifdef TARGET_EDOM - { -#ifdef GEN_ERRNO_RTX - rtx errno_rtx = GEN_ERRNO_RTX; -#else - rtx errno_rtx - = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno")); -#endif + if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + return 0; - emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM)); - } -#else - /* We can't set errno=EDOM directly; let the library call do it. - Pop the arguments right away in case the call gets deleted. */ - NO_DEFER_POP; - expand_call (exp, target, 0); - OK_DEFER_POP; -#endif + arg0 = TREE_VALUE (arglist); + arg1 = TREE_VALUE (TREE_CHAIN (arglist)); - emit_label (lab1); + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_POW: + case BUILT_IN_POWF: + case BUILT_IN_POWL: + builtin_optab = pow_optab; break; + case BUILT_IN_ATAN2: + case BUILT_IN_ATAN2F: + case BUILT_IN_ATAN2L: + builtin_optab = atan2_optab; break; + default: + abort (); } + /* 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) + return 0; + + target = gen_reg_rtx (mode); + + if (! flag_errno_math || ! HONOR_NANS (mode)) + errno_set = false; + + /* Alway stabilize the argument list. */ + narg = save_expr (arg1); + if (narg != arg1) + { + temp = build_tree_list (NULL_TREE, narg); + stable = false; + } + else + temp = TREE_CHAIN (arglist); + + narg = save_expr (arg0); + if (narg != arg0) + { + arglist = tree_cons (NULL_TREE, narg, temp); + stable = false; + } + else if (! stable) + arglist = tree_cons (NULL_TREE, arg0, temp); + + if (! stable) + exp = build_function_call_expr (fndecl, arglist); + + op0 = expand_expr (arg0, subtarget, VOIDmode, 0); + op1 = expand_expr (arg1, 0, VOIDmode, 0); + + emit_queue (); + start_sequence (); + + /* Compute into TARGET. + Set TARGET to wherever the result comes back. */ + target = expand_binop (mode, builtin_optab, op0, op1, + target, 0, OPTAB_DIRECT); + + /* If we were unable to expand via the builtin, stop the sequence + (without outputting the insns) and call to the library function + with the stabilized argument list. */ + if (target == 0) + { + end_sequence (); + return expand_call (exp, target, target == const0_rtx); + } + + if (errno_set) + expand_errno_check (exp, target); + /* Output the entire sequence. */ insns = get_insns (); end_sequence (); @@ -1600,31 +1889,295 @@ expand_builtin_mathfn (exp, target, subtarget) 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 + introduction to exponentiation algorithms and "addition chains", + see section 4.6.3, "Evaluation of Powers" of Donald E. Knuth, + "Seminumerical Algorithms", Vol. 2, "The Art of Computer Programming", + 3rd Edition, 1998, and Daniel M. Gordon, "A Survey of Fast Exponentiation + Methods", Journal of Algorithms, Vol. 27, pp. 129-146, 1998. */ + +/* Provide a default value for POWI_MAX_MULTS, the maximum number of + multiplications to inline before calling the system library's pow + function. powi(x,n) requires at worst 2*bits(n)-2 multiplications, + so this default never requires calling pow, powf or powl. */ + +#ifndef POWI_MAX_MULTS +#define POWI_MAX_MULTS (2*HOST_BITS_PER_WIDE_INT-2) +#endif + +/* The size of the "optimal power tree" lookup table. All + exponents less than this value are simply looked up in the + powi_table below. This threshold is also used to size the + cache of pseudo registers that hold intermediate results. */ +#define POWI_TABLE_SIZE 256 + +/* The size, in bits of the window, used in the "window method" + exponentiation algorithm. This is equivalent to a radix of + (1<<POWI_WINDOW_SIZE) in the corresponding "m-ary method". */ +#define POWI_WINDOW_SIZE 3 + +/* The following table is an efficient representation of an + "optimal power tree". For each value, i, the corresponding + value, j, in the table states than an optimal evaluation + sequence for calculating pow(x,i) can be found by evaluating + pow(x,j)*pow(x,i-j). An optimal power tree for the first + 100 integers is given in Knuth's "Seminumerical algorithms". */ + +static const unsigned char powi_table[POWI_TABLE_SIZE] = + { + 0, 1, 1, 2, 2, 3, 3, 4, /* 0 - 7 */ + 4, 6, 5, 6, 6, 10, 7, 9, /* 8 - 15 */ + 8, 16, 9, 16, 10, 12, 11, 13, /* 16 - 23 */ + 12, 17, 13, 18, 14, 24, 15, 26, /* 24 - 31 */ + 16, 17, 17, 19, 18, 33, 19, 26, /* 32 - 39 */ + 20, 25, 21, 40, 22, 27, 23, 44, /* 40 - 47 */ + 24, 32, 25, 34, 26, 29, 27, 44, /* 48 - 55 */ + 28, 31, 29, 34, 30, 60, 31, 36, /* 56 - 63 */ + 32, 64, 33, 34, 34, 46, 35, 37, /* 64 - 71 */ + 36, 65, 37, 50, 38, 48, 39, 69, /* 72 - 79 */ + 40, 49, 41, 43, 42, 51, 43, 58, /* 80 - 87 */ + 44, 64, 45, 47, 46, 59, 47, 76, /* 88 - 95 */ + 48, 65, 49, 66, 50, 67, 51, 66, /* 96 - 103 */ + 52, 70, 53, 74, 54, 104, 55, 74, /* 104 - 111 */ + 56, 64, 57, 69, 58, 78, 59, 68, /* 112 - 119 */ + 60, 61, 61, 80, 62, 75, 63, 68, /* 120 - 127 */ + 64, 65, 65, 128, 66, 129, 67, 90, /* 128 - 135 */ + 68, 73, 69, 131, 70, 94, 71, 88, /* 136 - 143 */ + 72, 128, 73, 98, 74, 132, 75, 121, /* 144 - 151 */ + 76, 102, 77, 124, 78, 132, 79, 106, /* 152 - 159 */ + 80, 97, 81, 160, 82, 99, 83, 134, /* 160 - 167 */ + 84, 86, 85, 95, 86, 160, 87, 100, /* 168 - 175 */ + 88, 113, 89, 98, 90, 107, 91, 122, /* 176 - 183 */ + 92, 111, 93, 102, 94, 126, 95, 150, /* 184 - 191 */ + 96, 128, 97, 130, 98, 133, 99, 195, /* 192 - 199 */ + 100, 128, 101, 123, 102, 164, 103, 138, /* 200 - 207 */ + 104, 145, 105, 146, 106, 109, 107, 149, /* 208 - 215 */ + 108, 200, 109, 146, 110, 170, 111, 157, /* 216 - 223 */ + 112, 128, 113, 130, 114, 182, 115, 132, /* 224 - 231 */ + 116, 200, 117, 132, 118, 158, 119, 206, /* 232 - 239 */ + 120, 240, 121, 162, 122, 147, 123, 152, /* 240 - 247 */ + 124, 166, 125, 214, 126, 138, 127, 153, /* 248 - 255 */ + }; + + +/* Return the number of multiplications required to calculate + powi(x,n) where n is less than POWI_TABLE_SIZE. This is a + subroutine of powi_cost. CACHE is an array indicating + which exponents have already been calculated. */ + +static int +powi_lookup_cost (unsigned HOST_WIDE_INT n, bool *cache) +{ + /* If we've already calculated this exponent, then this evaluation + doesn't require any additional multiplications. */ + if (cache[n]) + return 0; + + cache[n] = true; + return powi_lookup_cost (n - powi_table[n], cache) + + powi_lookup_cost (powi_table[n], cache) + 1; +} + +/* Return the number of multiplications required to calculate + powi(x,n) for an arbitrary x, given the exponent N. This + function needs to be kept in sync with expand_powi below. */ + +static int +powi_cost (HOST_WIDE_INT n) +{ + bool cache[POWI_TABLE_SIZE]; + unsigned HOST_WIDE_INT digit; + unsigned HOST_WIDE_INT val; + int result; + + if (n == 0) + return 0; + + /* Ignore the reciprocal when calculating the cost. */ + val = (n < 0) ? -n : n; + + /* Initialize the exponent cache. */ + memset (cache, 0, POWI_TABLE_SIZE * sizeof (bool)); + cache[1] = true; + + result = 0; + + while (val >= POWI_TABLE_SIZE) + { + if (val & 1) + { + digit = val & ((1 << POWI_WINDOW_SIZE) - 1); + result += powi_lookup_cost (digit, cache) + + POWI_WINDOW_SIZE + 1; + val >>= POWI_WINDOW_SIZE; + } + else + { + val >>= 1; + result++; + } + } + + return result + powi_lookup_cost (val, cache); +} + +/* Recursive subroutine of expand_powi. This function takes the array, + CACHE, of already calculated exponents and an exponent N and returns + an RTX that corresponds to CACHE[1]**N, as calculated in mode MODE. */ + +static rtx +expand_powi_1 (enum machine_mode mode, unsigned HOST_WIDE_INT n, rtx *cache) +{ + unsigned HOST_WIDE_INT digit; + rtx target, result; + rtx op0, op1; + + if (n < POWI_TABLE_SIZE) + { + if (cache[n]) + return cache[n]; + + target = gen_reg_rtx (mode); + cache[n] = target; + + op0 = expand_powi_1 (mode, n - powi_table[n], cache); + op1 = expand_powi_1 (mode, powi_table[n], cache); + } + else if (n & 1) + { + target = gen_reg_rtx (mode); + digit = n & ((1 << POWI_WINDOW_SIZE) - 1); + op0 = expand_powi_1 (mode, n - digit, cache); + op1 = expand_powi_1 (mode, digit, cache); + } + else + { + target = gen_reg_rtx (mode); + op0 = expand_powi_1 (mode, n >> 1, cache); + op1 = op0; + } + + result = expand_mult (mode, op0, op1, target, 0); + if (result != target) + emit_move_insn (target, result); + return target; +} + +/* Expand the RTL to evaluate powi(x,n) in mode MODE. X is the + floating point operand in mode MODE, and N is the exponent. This + function needs to be kept in sync with powi_cost above. */ + +static rtx +expand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n) +{ + unsigned HOST_WIDE_INT val; + rtx cache[POWI_TABLE_SIZE]; + rtx result; + + if (n == 0) + return CONST1_RTX (mode); + + val = (n < 0) ? -n : n; + + memset (cache, 0, sizeof (cache)); + cache[1] = x; + + result = expand_powi_1 (mode, (n < 0) ? -n : n, cache); + + /* If the original exponent was negative, reciprocate the result. */ + if (n < 0) + result = expand_binop (mode, sdiv_optab, CONST1_RTX (mode), + result, NULL_RTX, 0, OPTAB_LIB_WIDEN); + + return result; +} + +/* Expand a call to the pow 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_pow (tree exp, rtx target, rtx subtarget) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree arg0, arg1; + + if (! validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + return 0; + + arg0 = TREE_VALUE (arglist); + arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + + 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); + n = real_to_integer (&c); + real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); + if (real_identical (&c, &cint)) + { + /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact. + Otherwise, check the number of multiplications required. + Note that pow never sets errno for an integer exponent. */ + if ((n >= -1 && n <= 2) + || (flag_unsafe_math_optimizations + && ! optimize_size + && powi_cost (n) <= POWI_MAX_MULTS)) + { + enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); + rtx op = expand_expr (arg0, subtarget, VOIDmode, 0); + op = force_reg (mode, op); + return expand_powi (op, mode, n); + } + } + } + return expand_builtin_mathfn_2 (exp, target, NULL_RTX); +} + /* 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. */ static rtx -expand_builtin_strlen (exp, target) - tree exp; - rtx target; +expand_builtin_strlen (tree arglist, rtx target, + enum machine_mode target_mode) { - tree arglist = TREE_OPERAND (exp, 1); - enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp)); - if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) return 0; else { rtx pat; - tree src = TREE_VALUE (arglist); - - int align - = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - + tree len, src = TREE_VALUE (arglist); rtx result, src_reg, char_rtx, before_strlen; - enum machine_mode insn_mode = value_mode, char_mode; + enum machine_mode insn_mode = target_mode, char_mode; enum insn_code icode = CODE_FOR_nothing; + int align; + + /* If the length can be computed at compile-time, return it. */ + len = c_strlen (src, 0); + if (len) + return expand_expr (len, target, target_mode, EXPAND_NORMAL); + + /* If the length can be computed at compile-time and is constant + integer, but there are side-effects in src, evaluate + src for side-effects, then return len. + E.g. x = strlen (i++ ? "xfoo" + 1 : "bar"); + can be optimized into: i++; x = 3; */ + len = c_strlen (src, 1); + if (len && TREE_CODE (len) == INTEGER_CST) + { + expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); + return expand_expr (len, target, target_mode, EXPAND_NORMAL); + } + + align = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; /* If SRC is not a pointer type, don't do this operation inline. */ if (align == 0) @@ -1686,12 +2239,12 @@ expand_builtin_strlen (exp, target) emit_insn_before (pat, get_insns ()); /* Return the value in the proper mode for this function. */ - if (GET_MODE (result) == value_mode) + if (GET_MODE (result) == target_mode) target = result; else if (target != 0) convert_move (target, result, 0); else - target = convert_to_mode (value_mode, result, 0); + target = convert_to_mode (target_mode, result, 0); return target; } @@ -1702,10 +2255,7 @@ expand_builtin_strlen (exp, target) in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strstr (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; @@ -1729,7 +2279,8 @@ expand_builtin_strstr (arglist, target, mode) /* Return an offset into the constant string argument. */ return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), - s1, ssize_int (r - p1))), + s1, convert (TREE_TYPE (s1), + ssize_int (r - p1)))), target, mode, EXPAND_NORMAL); } @@ -1739,7 +2290,7 @@ expand_builtin_strstr (arglist, target, mode) if (p2[1] != '\0') return 0; - fn = built_in_decls[BUILT_IN_STRCHR]; + fn = implicit_built_in_decls[BUILT_IN_STRCHR]; if (!fn) return 0; @@ -1758,10 +2309,7 @@ expand_builtin_strstr (arglist, target, mode) in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strchr (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; @@ -1789,7 +2337,8 @@ expand_builtin_strchr (arglist, target, mode) /* Return an offset into the constant string argument. */ return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), - s1, ssize_int (r - p1))), + s1, convert (TREE_TYPE (s1), + ssize_int (r - p1)))), target, mode, EXPAND_NORMAL); } @@ -1804,10 +2353,7 @@ expand_builtin_strchr (arglist, target, mode) in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strrchr (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; @@ -1836,14 +2382,15 @@ expand_builtin_strrchr (arglist, target, mode) /* Return an offset into the constant string argument. */ return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), - s1, ssize_int (r - p1))), + s1, convert (TREE_TYPE (s1), + ssize_int (r - p1)))), target, mode, EXPAND_NORMAL); } if (! integer_zerop (s2)) return 0; - fn = built_in_decls[BUILT_IN_STRCHR]; + fn = implicit_built_in_decls[BUILT_IN_STRCHR]; if (!fn) return 0; @@ -1858,10 +2405,7 @@ expand_builtin_strrchr (arglist, target, mode) in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strpbrk (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; @@ -1885,7 +2429,8 @@ expand_builtin_strpbrk (arglist, target, mode) /* Return an offset into the constant string argument. */ return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), - s1, ssize_int (r - p1))), + s1, convert (TREE_TYPE (s1), + ssize_int (r - p1)))), target, mode, EXPAND_NORMAL); } @@ -1901,7 +2446,7 @@ expand_builtin_strpbrk (arglist, target, mode) if (p2[1] != '\0') return 0; /* Really call strpbrk. */ - fn = built_in_decls[BUILT_IN_STRCHR]; + fn = implicit_built_in_decls[BUILT_IN_STRCHR]; if (!fn) return 0; @@ -1920,10 +2465,8 @@ expand_builtin_strpbrk (arglist, target, mode) constant. */ static rtx -builtin_memcpy_read_str (data, offset, mode) - PTR data; - HOST_WIDE_INT offset; - enum machine_mode mode; +builtin_memcpy_read_str (void *data, HOST_WIDE_INT offset, + enum machine_mode mode) { const char *str = (const char *) data; @@ -1936,15 +2479,11 @@ builtin_memcpy_read_str (data, offset, mode) } /* Expand a call to the memcpy builtin, with arguments in ARGLIST. - Return 0 if we failed, the caller should emit a normal call, otherwise - try to get the result in TARGET, if convenient (and in mode MODE if - that's convenient). */ - + Return 0 if we failed, the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in + mode MODE if that's convenient). */ static rtx -expand_builtin_memcpy (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -1955,7 +2494,6 @@ expand_builtin_memcpy (arglist, target, mode) tree src = TREE_VALUE (TREE_CHAIN (arglist)); tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); const char *src_str; - unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); unsigned int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); @@ -1966,13 +2504,21 @@ expand_builtin_memcpy (arglist, target, mode) return 0; /* If the LEN parameter is zero, return DEST. */ - if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0) + 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) @@ -1990,16 +2536,13 @@ expand_builtin_memcpy (arglist, target, mode) && GET_CODE (len_rtx) == CONST_INT && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, - (PTR) src_str, dest_align)) + (void *) src_str, dest_align)) { - store_by_pieces (dest_mem, INTVAL (len_rtx), - builtin_memcpy_read_str, - (PTR) src_str, dest_align); + dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), + builtin_memcpy_read_str, + (void *) src_str, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (dest_mem) != ptr_mode) - dest_mem = convert_memory_address (ptr_mode, dest_mem); -#endif + dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } @@ -2013,43 +2556,247 @@ expand_builtin_memcpy (arglist, target, mode) if (dest_addr == 0) { dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (dest_addr) != ptr_mode) - dest_addr = convert_memory_address (ptr_mode, dest_addr); -#endif + dest_addr = convert_memory_address (ptr_mode, dest_addr); } - return dest_addr; } } +/* Expand a call to the mempcpy builtin, with arguments in ARGLIST. + Return 0 if we failed the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in + mode MODE if that's convenient). If ENDP is 0 return the + destination pointer, if ENDP is 1 return the end pointer ala + mempcpy, and if ENDP is 2 return the end pointer minus one ala + stpcpy. */ + +static rtx +expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode, + int endp) +{ + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + /* If return value is ignored, transform mempcpy into memcpy. */ + else if (target == const0_rtx) + { + 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); + } + else + { + tree dest = TREE_VALUE (arglist); + tree src = TREE_VALUE (TREE_CHAIN (arglist)); + tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + const char *src_str; + unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); + unsigned int dest_align + = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + rtx dest_mem, src_mem, len_rtx; + + /* If DEST is not a pointer type, call the normal function. */ + if (dest_align == 0) + return 0; + + /* If SRC and DEST are the same (and not volatile), do nothing. */ + if (operand_equal_p (src, dest, 0)) + { + tree expr; + + if (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 (build (MINUS_EXPR, TREE_TYPE (len), dest, + integer_one_node)); + len = convert (TREE_TYPE (dest), len); + expr = fold (build (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); + + /* If SRC is a string constant and block move would be done + by pieces, we can avoid loading the string from memory + and only stored the computed constants. */ + if (src_str + && GET_CODE (len_rtx) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 + && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, + (void *) src_str, dest_align)) + { + dest_mem = get_memory_rtx (dest); + set_mem_align (dest_mem, dest_align); + dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), + builtin_memcpy_read_str, + (void *) src_str, dest_align, endp); + dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); + dest_mem = convert_memory_address (ptr_mode, dest_mem); + return dest_mem; + } + + if (GET_CODE (len_rtx) == CONST_INT + && can_move_by_pieces (INTVAL (len_rtx), + MIN (dest_align, src_align))) + { + dest_mem = get_memory_rtx (dest); + set_mem_align (dest_mem, dest_align); + src_mem = get_memory_rtx (src); + set_mem_align (src_mem, src_align); + dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx), + MIN (dest_align, src_align), endp); + dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); + dest_mem = convert_memory_address (ptr_mode, dest_mem); + return dest_mem; + } + + return 0; + } +} + +/* Expand expression EXP, which is a call to the memmove builtin. Return 0 + if we failed the caller should emit a normal call. */ + +static rtx +expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode) +{ + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + else + { + tree dest = TREE_VALUE (arglist); + tree src = TREE_VALUE (TREE_CHAIN (arglist)); + tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); + unsigned int dest_align + = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + + /* If DEST is not a pointer type, call the normal function. */ + if (dest_align == 0) + return 0; + + /* If 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) + return 0; + + /* If src is categorized for a readonly section we can use + normal memcpy. */ + if (readonly_data_expr (src)) + { + tree const 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); + } + + /* Otherwise, call the normal function. */ + return 0; + } +} + +/* Expand expression EXP, which is a call to the bcopy builtin. Return 0 + if we failed the caller should emit a normal call. */ + +static rtx +expand_builtin_bcopy (tree arglist) +{ + tree src, dest, size, newarglist; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + + src = TREE_VALUE (arglist); + dest = TREE_VALUE (TREE_CHAIN (arglist)); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* New argument list transforming bcopy(ptr x, ptr y, int z) to + memmove(ptr y, ptr x, size_t z). This is done this way + so that if it isn't expanded inline, we fallback to + calling bcopy instead of memmove. */ + + newarglist = build_tree_list (NULL_TREE, convert (sizetype, size)); + newarglist = tree_cons (NULL_TREE, src, newarglist); + newarglist = tree_cons (NULL_TREE, dest, newarglist); + + return expand_builtin_memmove (newarglist, const0_rtx, VOIDmode); +} + /* Expand expression EXP, which is a call to the strcpy builtin. Return 0 if we failed the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). */ static rtx -expand_builtin_strcpy (exp, target, mode) - tree exp; - rtx target; - enum machine_mode mode; +expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode) { - tree arglist = TREE_OPERAND (exp, 1); tree fn, len, src, dst; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; - fn = built_in_decls[BUILT_IN_MEMCPY]; + 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); + + fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; - src = TREE_VALUE (TREE_CHAIN (arglist)); - len = c_strlen (src); + len = c_strlen (src, 1); if (len == 0 || TREE_SIDE_EFFECTS (len)) return 0; - dst = TREE_VALUE (arglist); len = size_binop (PLUS_EXPR, len, ssize_int (1)); arglist = build_tree_list (NULL_TREE, len); arglist = tree_cons (NULL_TREE, src, arglist); @@ -2058,15 +2805,55 @@ expand_builtin_strcpy (exp, target, mode) target, mode, EXPAND_NORMAL); } +/* Expand a call to the stpcpy builtin, with arguments in ARGLIST. + Return 0 if we failed the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in + mode MODE if that's convenient). */ + +static rtx +expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode) +{ + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + else + { + tree dst, src, len; + + /* If return value is ignored, transform stpcpy into strcpy. */ + if (target == const0_rtx) + { + tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + if (!fn) + return 0; + + return expand_expr (build_function_call_expr (fn, arglist), + target, mode, EXPAND_NORMAL); + } + + /* Ensure we get an actual string whose length can be evaluated at + compile-time, not an expression containing a string. This is + because the latter will potentially produce pessimized code + when used to produce the return value. */ + src = TREE_VALUE (TREE_CHAIN (arglist)); + if (! c_getstr (src) || ! (len = c_strlen (src, 0))) + return 0; + + dst = TREE_VALUE (arglist); + len = fold (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_builtin_mempcpy (arglist, target, mode, /*endp=*/2); + } +} + /* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) bytes from constant string DATA + OFFSET and return it as target constant. */ static rtx -builtin_strncpy_read_str (data, offset, mode) - PTR data; - HOST_WIDE_INT offset; - enum machine_mode mode; +builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset, + enum machine_mode mode) { const char *str = (const char *) data; @@ -2080,17 +2867,14 @@ builtin_strncpy_read_str (data, offset, mode) if we failed the caller should emit a normal call. */ static rtx -expand_builtin_strncpy (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strncpy (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; else { - tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist))); + tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1); tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); tree fn; @@ -2130,23 +2914,20 @@ expand_builtin_strncpy (arglist, target, mode) if (!p || dest_align == 0 || !host_integerp (len, 1) || !can_store_by_pieces (tree_low_cst (len, 1), builtin_strncpy_read_str, - (PTR) p, dest_align)) + (void *) p, dest_align)) return 0; dest_mem = get_memory_rtx (dest); store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_strncpy_read_str, - (PTR) p, dest_align); + (void *) p, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (dest_mem) != ptr_mode) - dest_mem = convert_memory_address (ptr_mode, dest_mem); -#endif + dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } /* OK transform into builtin memcpy. */ - fn = built_in_decls[BUILT_IN_MEMCPY]; + fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; return expand_expr (build_function_call_expr (fn, arglist), @@ -2159,10 +2940,8 @@ expand_builtin_strncpy (arglist, target, mode) constant. */ static rtx -builtin_memset_read_str (data, offset, mode) - PTR data; - HOST_WIDE_INT offset ATTRIBUTE_UNUSED; - enum machine_mode mode; +builtin_memset_read_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, + enum machine_mode mode) { const char *c = (const char *) data; char *p = alloca (GET_MODE_SIZE (mode)); @@ -2178,10 +2957,8 @@ builtin_memset_read_str (data, offset, mode) 4 bytes wide, return the RTL for 0x01010101*data. */ static rtx -builtin_memset_gen_str (data, offset, mode) - PTR data; - HOST_WIDE_INT offset ATTRIBUTE_UNUSED; - enum machine_mode mode; +builtin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, + enum machine_mode mode) { rtx target, coeff; size_t size; @@ -2206,13 +2983,8 @@ builtin_memset_gen_str (data, offset, mode) convenient). */ static rtx -expand_builtin_memset (exp, target, mode) - tree exp; - rtx target; - enum machine_mode mode; +expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode) { - tree arglist = TREE_OPERAND (exp, 1); - if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) return 0; @@ -2233,7 +3005,7 @@ expand_builtin_memset (exp, target, mode) return 0; /* If the LEN parameter is zero, return DEST. */ - if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0) + if (integer_zerop (len)) { /* Evaluate and ignore VAL in case it has side-effects. */ expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL); @@ -2256,7 +3028,7 @@ expand_builtin_memset (exp, target, mode) c = 1; if (!can_store_by_pieces (tree_low_cst (len, 1), builtin_memset_read_str, - (PTR) &c, dest_align)) + &c, dest_align)) return 0; val = fold (build1 (CONVERT_EXPR, unsigned_char_type_node, val)); @@ -2266,12 +3038,9 @@ expand_builtin_memset (exp, target, mode) dest_mem = get_memory_rtx (dest); store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_memset_gen_str, - (PTR) val_rtx, dest_align); + val_rtx, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (dest_mem) != ptr_mode) - dest_mem = convert_memory_address (ptr_mode, dest_mem); -#endif + dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } @@ -2283,19 +3052,16 @@ expand_builtin_memset (exp, target, mode) if (!host_integerp (len, 1)) return 0; if (!can_store_by_pieces (tree_low_cst (len, 1), - builtin_memset_read_str, (PTR) &c, + builtin_memset_read_str, &c, dest_align)) return 0; dest_mem = get_memory_rtx (dest); store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_memset_read_str, - (PTR) &c, dest_align); + &c, dest_align, 0); dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (dest_mem) != ptr_mode) - dest_mem = convert_memory_address (ptr_mode, dest_mem); -#endif + dest_mem = convert_memory_address (ptr_mode, dest_mem); return dest_mem; } @@ -2308,10 +3074,7 @@ expand_builtin_memset (exp, target, mode) if (dest_addr == 0) { dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (dest_addr) != ptr_mode) - dest_addr = convert_memory_address (ptr_mode, dest_addr); -#endif + dest_addr = convert_memory_address (ptr_mode, dest_addr); } return dest_addr; @@ -2322,12 +3085,9 @@ expand_builtin_memset (exp, target, mode) if we failed the caller should emit a normal call. */ static rtx -expand_builtin_bzero (exp) - tree exp; +expand_builtin_bzero (tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree dest, size, newarglist; - rtx result; if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return NULL_RTX; @@ -2344,13 +3104,7 @@ expand_builtin_bzero (exp) newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist); newarglist = tree_cons (NULL_TREE, dest, newarglist); - TREE_OPERAND (exp, 1) = newarglist; - result = expand_builtin_memset (exp, const0_rtx, VOIDmode); - - /* Always restore the original arguments. */ - TREE_OPERAND (exp, 1) = arglist; - - return result; + return expand_builtin_memset (newarglist, const0_rtx, VOIDmode); } /* Expand expression EXP, which is a call to the memcmp built-in function. @@ -2359,11 +3113,8 @@ expand_builtin_bzero (exp) TARGET, if convenient (and in mode MODE, if that's convenient). */ static rtx -expand_builtin_memcmp (exp, arglist, target, mode) - tree exp ATTRIBUTE_UNUSED; - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, + enum machine_mode mode) { tree arg1, arg2, len; const char *p1, *p2; @@ -2377,7 +3128,7 @@ expand_builtin_memcmp (exp, arglist, target, mode) len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the len parameter is zero, return zero. */ - if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0) + if (integer_zerop (len)) { /* Evaluate and ignore arg1 and arg2 in case they have side-effects. */ @@ -2386,6 +3137,14 @@ expand_builtin_memcmp (exp, arglist, target, mode) return const0_rtx; } + /* If both arguments are equal (and not volatile), return zero. */ + if (operand_equal_p (arg1, arg2, 0)) + { + /* Evaluate and ignore len in case it has side-effects. */ + expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); + return const0_rtx; + } + p1 = c_getstr (arg1); p2 = c_getstr (arg2); @@ -2402,7 +3161,7 @@ expand_builtin_memcmp (exp, arglist, target, mode) /* If len parameter is one, return an expression corresponding to (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */ - if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1) + if (integer_onep (len)) { tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); @@ -2418,7 +3177,7 @@ expand_builtin_memcmp (exp, arglist, target, mode) return expand_expr (result, target, mode, EXPAND_NORMAL); } -#ifdef HAVE_cmpstrsi +#if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi { rtx arg1_rtx, arg2_rtx, arg3_rtx; rtx result; @@ -2428,8 +3187,19 @@ expand_builtin_memcmp (exp, arglist, target, mode) = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; int arg2_align = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - enum machine_mode insn_mode - = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; + enum machine_mode insn_mode; + +#ifdef HAVE_cmpmemsi + if (HAVE_cmpmemsi) + insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode; + else +#endif +#ifdef HAVE_cmpstrsi + if (HAVE_cmpstrsi) + insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; + else +#endif + return 0; /* If we don't have POINTER_TYPE, call the function. */ if (arg1_align == 0 || arg2_align == 0) @@ -2445,11 +3215,19 @@ expand_builtin_memcmp (exp, arglist, target, mode) arg1_rtx = get_memory_rtx (arg1); arg2_rtx = get_memory_rtx (arg2); arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); - if (!HAVE_cmpstrsi) - insn = NULL_RTX; +#ifdef HAVE_cmpmemsi + if (HAVE_cmpmemsi) + insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx, + GEN_INT (MIN (arg1_align, arg2_align))); else +#endif +#ifdef HAVE_cmpstrsi + if (HAVE_cmpstrsi) insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, GEN_INT (MIN (arg1_align, arg2_align))); + else +#endif + abort (); if (insn) emit_insn (insn); @@ -2484,10 +3262,7 @@ expand_builtin_memcmp (exp, arglist, target, mode) the result in TARGET, if convenient. */ static rtx -expand_builtin_strcmp (exp, target, mode) - tree exp; - rtx target; - enum machine_mode mode; +expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode) { tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2; @@ -2499,6 +3274,10 @@ expand_builtin_strcmp (exp, target, mode) arg1 = TREE_VALUE (arglist); arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + /* If both arguments are equal (and not volatile), return zero. */ + if (operand_equal_p (arg1, arg2, 0)) + return const0_rtx; + p1 = c_getstr (arg1); p2 = c_getstr (arg2); @@ -2528,85 +3307,97 @@ expand_builtin_strcmp (exp, target, mode) #ifdef HAVE_cmpstrsi if (HAVE_cmpstrsi) - { - tree len, len1, len2; - rtx arg1_rtx, arg2_rtx, arg3_rtx; - rtx result, insn; - - int arg1_align - = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - int arg2_align - = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - enum machine_mode insn_mode - = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; - - len1 = c_strlen (arg1); - len2 = c_strlen (arg2); - - if (len1) - len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); - if (len2) - len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); - - /* If we don't have a constant length for the first, use the length - of the second, if we know it. We don't require a constant for - this case; some cost analysis could be done if both are available - but neither is constant. For now, assume they're equally cheap - unless one has side effects. If both strings have constant lengths, - use the smaller. */ - - if (!len1) - len = len2; - else if (!len2) - len = len1; - else if (TREE_SIDE_EFFECTS (len1)) - len = len2; - else if (TREE_SIDE_EFFECTS (len2)) - len = len1; - else if (TREE_CODE (len1) != INTEGER_CST) - len = len2; - else if (TREE_CODE (len2) != INTEGER_CST) - len = len1; - else if (tree_int_cst_lt (len1, len2)) - len = len1; - else - len = len2; + { + tree len, len1, len2; + rtx arg1_rtx, arg2_rtx, arg3_rtx; + rtx result, insn; + tree fndecl; - /* If both arguments have side effects, we cannot optimize. */ - if (!len || TREE_SIDE_EFFECTS (len)) - return 0; + int arg1_align + = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + int arg2_align + = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + enum machine_mode insn_mode + = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; - /* If we don't have POINTER_TYPE, call the function. */ - if (arg1_align == 0 || arg2_align == 0) - return 0; + len1 = c_strlen (arg1, 1); + len2 = c_strlen (arg2, 1); + + if (len1) + len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); + if (len2) + len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); + + /* If we don't have a constant length for the first, use the length + of the second, if we know it. We don't require a constant for + this case; some cost analysis could be done if both are available + but neither is constant. For now, assume they're equally cheap, + unless one has side effects. If both strings have constant lengths, + use the smaller. */ + + if (!len1) + len = len2; + else if (!len2) + len = len1; + else if (TREE_SIDE_EFFECTS (len1)) + len = len2; + else if (TREE_SIDE_EFFECTS (len2)) + len = len1; + else if (TREE_CODE (len1) != INTEGER_CST) + len = len2; + else if (TREE_CODE (len2) != INTEGER_CST) + len = len1; + else if (tree_int_cst_lt (len1, len2)) + len = len1; + else + len = len2; - /* Make a place to write the result of the instruction. */ - result = target; - if (! (result != 0 - && GET_CODE (result) == REG - && GET_MODE (result) == insn_mode - && REGNO (result) >= FIRST_PSEUDO_REGISTER)) - result = gen_reg_rtx (insn_mode); + /* If both arguments have side effects, we cannot optimize. */ + if (!len || TREE_SIDE_EFFECTS (len)) + return 0; - arg1_rtx = get_memory_rtx (arg1); - arg2_rtx = get_memory_rtx (arg2); - arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); - insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, - GEN_INT (MIN (arg1_align, arg2_align))); - if (!insn) - return 0; + /* If we don't have POINTER_TYPE, call the function. */ + if (arg1_align == 0 || arg2_align == 0) + return 0; - emit_insn (insn); + /* Make a place to write the result of the instruction. */ + result = target; + if (! (result != 0 + && GET_CODE (result) == REG && GET_MODE (result) == insn_mode + && REGNO (result) >= FIRST_PSEUDO_REGISTER)) + result = gen_reg_rtx (insn_mode); - /* Return the value in the proper mode for this function. */ - mode = TYPE_MODE (TREE_TYPE (exp)); - if (GET_MODE (result) == mode) - return result; - if (target == 0) - return convert_to_mode (mode, result, 0); - convert_move (target, result, 0); - return target; - } + /* Stabilize the arguments in case gen_cmpstrsi fails. */ + arg1 = save_expr (arg1); + arg2 = save_expr (arg2); + + arg1_rtx = get_memory_rtx (arg1); + arg2_rtx = get_memory_rtx (arg2); + arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); + insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, + GEN_INT (MIN (arg1_align, arg2_align))); + if (insn) + { + emit_insn (insn); + + /* Return the value in the proper mode for this function. */ + mode = TYPE_MODE (TREE_TYPE (exp)); + if (GET_MODE (result) == mode) + return result; + if (target == 0) + return convert_to_mode (mode, result, 0); + convert_move (target, result, 0); + return target; + } + + /* Expand the library call ourselves using a stabilized argument + list to avoid re-evaluating the function's arguments twice. */ + arglist = build_tree_list (NULL_TREE, arg2); + arglist = tree_cons (NULL_TREE, arg1, arglist); + fndecl = get_callee_fndecl (exp); + exp = build_function_call_expr (fndecl, arglist); + return expand_call (exp, target, target == const0_rtx); + } #endif return 0; } @@ -2616,10 +3407,7 @@ expand_builtin_strcmp (exp, target, mode) the result in TARGET, if convenient. */ static rtx -expand_builtin_strncmp (exp, target, mode) - tree exp; - rtx target; - enum machine_mode mode; +expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) { tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2, arg3; @@ -2634,7 +3422,7 @@ expand_builtin_strncmp (exp, target, mode) arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); /* If the len parameter is zero, return zero. */ - if (host_integerp (arg3, 1) && tree_low_cst (arg3, 1) == 0) + if (integer_zerop (arg3)) { /* Evaluate and ignore arg1 and arg2 in case they have side-effects. */ @@ -2643,6 +3431,14 @@ expand_builtin_strncmp (exp, target, mode) return const0_rtx; } + /* If arg1 and arg2 are equal (and not volatile), return zero. */ + if (operand_equal_p (arg1, arg2, 0)) + { + /* Evaluate and ignore arg3 in case it has side-effects. */ + expand_expr (arg3, const0_rtx, VOIDmode, EXPAND_NORMAL); + return const0_rtx; + } + p1 = c_getstr (arg1); p2 = c_getstr (arg2); @@ -2679,88 +3475,102 @@ expand_builtin_strncmp (exp, target, mode) using length MIN(strlen(string)+1, arg3). */ #ifdef HAVE_cmpstrsi if (HAVE_cmpstrsi) - { - tree len, len1, len2; - rtx arg1_rtx, arg2_rtx, arg3_rtx; - rtx result, insn; - - int arg1_align - = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - int arg2_align - = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - enum machine_mode insn_mode - = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; - - len1 = c_strlen (arg1); - len2 = c_strlen (arg2); - - if (len1) - len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); - if (len2) - len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); - - /* If we don't have a constant length for the first, use the length - of the second, if we know it. We don't require a constant for - this case; some cost analysis could be done if both are available - but neither is constant. For now, assume they're equally cheap, - unless one has side effects. If both strings have constant lengths, - use the smaller. */ - - if (!len1) - len = len2; - else if (!len2) - len = len1; - else if (TREE_SIDE_EFFECTS (len1)) - len = len2; - else if (TREE_SIDE_EFFECTS (len2)) - len = len1; - else if (TREE_CODE (len1) != INTEGER_CST) - len = len2; - else if (TREE_CODE (len2) != INTEGER_CST) - len = len1; - else if (tree_int_cst_lt (len1, len2)) - len = len1; - else - len = len2; + { + tree len, len1, len2; + rtx arg1_rtx, arg2_rtx, arg3_rtx; + rtx result, insn; + tree fndecl; - /* If both arguments have side effects, we cannot optimize. */ - if (!len || TREE_SIDE_EFFECTS (len)) - return 0; + int arg1_align + = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + int arg2_align + = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + enum machine_mode insn_mode + = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; - /* The actual new length parameter is MIN(len,arg3). */ - len = fold (build (MIN_EXPR, TREE_TYPE (len), len, arg3)); + len1 = c_strlen (arg1, 1); + len2 = c_strlen (arg2, 1); + + if (len1) + len1 = size_binop (PLUS_EXPR, ssize_int (1), len1); + if (len2) + len2 = size_binop (PLUS_EXPR, ssize_int (1), len2); + + /* If we don't have a constant length for the first, use the length + of the second, if we know it. We don't require a constant for + this case; some cost analysis could be done if both are available + but neither is constant. For now, assume they're equally cheap, + unless one has side effects. If both strings have constant lengths, + use the smaller. */ + + if (!len1) + len = len2; + else if (!len2) + len = len1; + else if (TREE_SIDE_EFFECTS (len1)) + len = len2; + else if (TREE_SIDE_EFFECTS (len2)) + len = len1; + else if (TREE_CODE (len1) != INTEGER_CST) + len = len2; + else if (TREE_CODE (len2) != INTEGER_CST) + len = len1; + else if (tree_int_cst_lt (len1, len2)) + len = len1; + else + len = len2; - /* If we don't have POINTER_TYPE, call the function. */ - if (arg1_align == 0 || arg2_align == 0) - return 0; + /* If both arguments have side effects, we cannot optimize. */ + if (!len || TREE_SIDE_EFFECTS (len)) + return 0; - /* Make a place to write the result of the instruction. */ - result = target; - if (! (result != 0 - && GET_CODE (result) == REG - && GET_MODE (result) == insn_mode - && REGNO (result) >= FIRST_PSEUDO_REGISTER)) - result = gen_reg_rtx (insn_mode); + /* The actual new length parameter is MIN(len,arg3). */ + len = fold (build (MIN_EXPR, TREE_TYPE (len), len, arg3)); - arg1_rtx = get_memory_rtx (arg1); - arg2_rtx = get_memory_rtx (arg2); - arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); - insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, - GEN_INT (MIN (arg1_align, arg2_align))); - if (!insn) - return 0; + /* If we don't have POINTER_TYPE, call the function. */ + if (arg1_align == 0 || arg2_align == 0) + return 0; - emit_insn (insn); + /* Make a place to write the result of the instruction. */ + result = target; + if (! (result != 0 + && GET_CODE (result) == REG && GET_MODE (result) == insn_mode + && REGNO (result) >= FIRST_PSEUDO_REGISTER)) + result = gen_reg_rtx (insn_mode); - /* Return the value in the proper mode for this function. */ - mode = TYPE_MODE (TREE_TYPE (exp)); - if (GET_MODE (result) == mode) - return result; - if (target == 0) - return convert_to_mode (mode, result, 0); - convert_move (target, result, 0); - return target; - } + /* Stabilize the arguments in case gen_cmpstrsi fails. */ + arg1 = save_expr (arg1); + arg2 = save_expr (arg2); + len = save_expr (len); + + arg1_rtx = get_memory_rtx (arg1); + arg2_rtx = get_memory_rtx (arg2); + arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); + insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx, + GEN_INT (MIN (arg1_align, arg2_align))); + if (insn) + { + emit_insn (insn); + + /* Return the value in the proper mode for this function. */ + mode = TYPE_MODE (TREE_TYPE (exp)); + if (GET_MODE (result) == mode) + return result; + if (target == 0) + return convert_to_mode (mode, result, 0); + convert_move (target, result, 0); + return target; + } + + /* Expand the library call ourselves using a stabilized argument + list to avoid re-evaluating the function's arguments twice. */ + arglist = build_tree_list (NULL_TREE, len); + 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); + } #endif return 0; } @@ -2770,10 +3580,7 @@ expand_builtin_strncmp (exp, target, mode) otherwise try to get the result in TARGET, if convenient. */ static rtx -expand_builtin_strcat (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; @@ -2783,9 +3590,52 @@ expand_builtin_strcat (arglist, target, mode) src = TREE_VALUE (TREE_CHAIN (arglist)); const char *p = c_getstr (src); - /* If the string length is zero, return the dst parameter. */ - if (p && *p == '\0') - return expand_expr (dst, target, mode, EXPAND_NORMAL); + if (p) + { + /* If the string length is zero, return the dst parameter. */ + if (*p == '\0') + return expand_expr (dst, target, mode, EXPAND_NORMAL); + else if (!optimize_size) + { + /* Otherwise if !optimize_size, see if we can store by + pieces into (dst + strlen(dst)). */ + tree newdst, arglist, + strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN]; + + /* This is the length argument. */ + arglist = build_tree_list (NULL_TREE, + fold (size_binop (PLUS_EXPR, + c_strlen (src, 0), + ssize_int (1)))); + /* Prepend src argument. */ + arglist = tree_cons (NULL_TREE, src, arglist); + + /* We're going to use dst more than once. */ + dst = save_expr (dst); + + /* Create strlen (dst). */ + newdst = + fold (build_function_call_expr (strlen_fn, + build_tree_list (NULL_TREE, + dst))); + /* Create (dst + strlen (dst)). */ + newdst = fold (build (PLUS_EXPR, TREE_TYPE (dst), dst, newdst)); + + /* Prepend the new dst argument. */ + arglist = tree_cons (NULL_TREE, newdst, arglist); + + /* We don't want to get turned into a memcpy if the + target is const0_rtx, i.e. when the return value + isn't used. That would produce pessimized code so + pass in a target of zero, it should never actually be + used. If this was successful return the original + dst, not the result of mempcpy. */ + if (expand_builtin_mempcpy (arglist, /*target=*/0, mode, /*endp=*/0)) + return expand_expr (dst, target, mode, EXPAND_NORMAL); + else + return 0; + } + } return 0; } @@ -2796,10 +3646,7 @@ expand_builtin_strcat (arglist, target, mode) otherwise try to get the result in TARGET, if convenient. */ static rtx -expand_builtin_strncat (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strncat (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -2829,7 +3676,7 @@ expand_builtin_strncat (arglist, target, mode) { tree newarglist = tree_cons (NULL_TREE, dst, build_tree_list (NULL_TREE, src)); - tree fn = built_in_decls[BUILT_IN_STRCAT]; + tree fn = implicit_built_in_decls[BUILT_IN_STRCAT]; /* If the replacement _DECL isn't initialized, don't do the transformation. */ @@ -2848,10 +3695,7 @@ expand_builtin_strncat (arglist, target, mode) otherwise try to get the result in TARGET, if convenient. */ static rtx -expand_builtin_strspn (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strspn (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; @@ -2885,10 +3729,7 @@ expand_builtin_strspn (arglist, target, mode) otherwise try to get the result in TARGET, if convenient. */ static rtx -expand_builtin_strcspn (arglist, target, mode) - tree arglist; - rtx target; - enum machine_mode mode; +expand_builtin_strcspn (tree arglist, rtx target, enum machine_mode mode) { if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; @@ -2917,7 +3758,7 @@ expand_builtin_strcspn (arglist, target, mode) if (p2 && *p2 == '\0') { tree newarglist = build_tree_list (NULL_TREE, s1), - fn = built_in_decls[BUILT_IN_STRLEN]; + fn = implicit_built_in_decls[BUILT_IN_STRLEN]; /* If the replacement _DECL isn't initialized, don't do the transformation. */ @@ -2935,7 +3776,7 @@ expand_builtin_strcspn (arglist, target, mode) if that's convenient. */ rtx -expand_builtin_saveregs () +expand_builtin_saveregs (void) { rtx val, seq; @@ -2950,21 +3791,8 @@ expand_builtin_saveregs () start_sequence (); -#ifdef EXPAND_BUILTIN_SAVEREGS /* Do whatever the machine needs done in this case. */ - val = EXPAND_BUILTIN_SAVEREGS (); -#else - /* ??? We used to try and build up a call to the out of line function, - guessing about what registers needed saving etc. This became much - harder with __builtin_va_start, since we don't have a tree for a - call to __builtin_saveregs to fall back on. There was exactly one - port (i860) that used this code, and I'm unconvinced it could actually - handle the general case. So we no longer try to handle anything - weird and make the backend absorb the evil. */ - - error ("__builtin_saveregs not supported by this target"); - val = const0_rtx; -#endif + val = targetm.calls.expand_builtin_saveregs (); seq = get_insns (); end_sequence (); @@ -2986,17 +3814,10 @@ expand_builtin_saveregs () is controlled by the definition of CUMULATIVE_ARGS. */ static rtx -expand_builtin_args_info (exp) - tree exp; +expand_builtin_args_info (tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int); int *word_ptr = (int *) ¤t_function_args_info; -#if 0 - /* These are used by the code below that is if 0'ed away */ - int i; - tree type, elts, result; -#endif if (sizeof (CUMULATIVE_ARGS) % sizeof (int) != 0) abort (); @@ -3019,27 +3840,12 @@ expand_builtin_args_info (exp) error ("missing argument in `__builtin_args_info'"); return const0_rtx; - -#if 0 - for (i = 0; i < nwords; i++) - elts = tree_cons (NULL_TREE, build_int_2 (word_ptr[i], 0)); - - type = build_array_type (integer_type_node, - build_index_type (build_int_2 (nwords, 0))); - result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (elts)); - TREE_CONSTANT (result) = 1; - TREE_STATIC (result) = 1; - result = build1 (INDIRECT_REF, build_pointer_type (type), result); - TREE_CONSTANT (result) = 1; - return expand_expr (result, NULL_RTX, VOIDmode, 0); -#endif } /* Expand ARGLIST, from a call to __builtin_next_arg. */ static rtx -expand_builtin_next_arg (arglist) - tree arglist; +expand_builtin_next_arg (tree arglist) { tree fntype = TREE_TYPE (current_function_decl); @@ -3083,9 +3889,7 @@ expand_builtin_next_arg (arglist) from multiple evaluations. */ static tree -stabilize_va_list (valist, needs_lvalue) - tree valist; - int needs_lvalue; +stabilize_va_list (tree valist, int needs_lvalue) { if (TREE_CODE (va_list_type_node) == ARRAY_TYPE) { @@ -3128,13 +3932,19 @@ stabilize_va_list (valist, needs_lvalue) return valist; } +/* The "standard" definition of va_list is void*. */ + +tree +std_build_builtin_va_list (void) +{ + return ptr_type_node; +} + /* The "standard" implementation of va_start: just assign `nextarg' to the variable. */ void -std_expand_builtin_va_start (valist, nextarg) - tree valist; - rtx nextarg; +std_expand_builtin_va_start (tree valist, rtx nextarg) { tree t; @@ -3148,8 +3958,7 @@ std_expand_builtin_va_start (valist, nextarg) /* Expand ARGLIST, from a call to __builtin_va_start. */ static rtx -expand_builtin_va_start (arglist) - tree arglist; +expand_builtin_va_start (tree arglist) { rtx nextarg; tree chain, valist; @@ -3175,17 +3984,38 @@ expand_builtin_va_start (arglist) current (padded) address and increment by the (padded) size. */ rtx -std_expand_builtin_va_arg (valist, type) - tree valist, type; +std_expand_builtin_va_arg (tree valist, tree type) { tree addr_tree, t, type_size = NULL; tree align, alignm1; tree rounded_size; rtx addr; + HOST_WIDE_INT boundary; /* Compute the rounded size of the type. */ align = size_int (PARM_BOUNDARY / BITS_PER_UNIT); alignm1 = size_int (PARM_BOUNDARY / BITS_PER_UNIT - 1); + boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type); + + /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually + requires greater alignment, we must perform dynamic alignment. */ + + if (boundary > PARM_BOUNDARY) + { + if (!PAD_VARARGS_DOWN) + { + t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, + build (PLUS_EXPR, TREE_TYPE (valist), valist, + build_int_2 (boundary / BITS_PER_UNIT - 1, 0))); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } + t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, + build (BIT_AND_EXPR, TREE_TYPE (valist), valist, + build_int_2 (~(boundary / BITS_PER_UNIT - 1), -1))); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } if (type == error_mark_node || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL || TREE_OVERFLOW (type_size)) @@ -3234,8 +4064,7 @@ std_expand_builtin_va_arg (valist, type) a very special sort of operator. */ rtx -expand_builtin_va_arg (valist, type) - tree valist, type; +expand_builtin_va_arg (tree valist, tree type) { rtx addr, result; tree promoted_type, want_va_type, have_va_type; @@ -3302,6 +4131,7 @@ expand_builtin_va_arg (valist, type) /* We can, however, treat "undefined" any way we please. Call abort to encourage the user to fix the program. */ + inform ("if this code is reached, the program will abort"); expand_builtin_trap (); /* This is dead code, but go ahead and finish so that the @@ -3321,10 +4151,7 @@ expand_builtin_va_arg (valist, type) #endif } -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (addr) != Pmode) - addr = convert_memory_address (Pmode, addr); -#endif + addr = convert_memory_address (Pmode, addr); result = gen_rtx_MEM (TYPE_MODE (type), addr); set_mem_alias_set (result, get_varargs_alias_set ()); @@ -3335,20 +4162,14 @@ expand_builtin_va_arg (valist, type) /* Expand ARGLIST, from a call to __builtin_va_end. */ static rtx -expand_builtin_va_end (arglist) - tree arglist; +expand_builtin_va_end (tree arglist) { tree valist = TREE_VALUE (arglist); -#ifdef EXPAND_BUILTIN_VA_END - valist = stabilize_va_list (valist, 0); - EXPAND_BUILTIN_VA_END (arglist); -#else /* Evaluate for side effects, if needed. I hate macros that don't do that. */ if (TREE_SIDE_EFFECTS (valist)) expand_expr (valist, const0_rtx, VOIDmode, EXPAND_NORMAL); -#endif return const0_rtx; } @@ -3358,8 +4179,7 @@ expand_builtin_va_end (arglist) nastiness of array-type va_list types. */ static rtx -expand_builtin_va_copy (arglist) - tree arglist; +expand_builtin_va_copy (tree arglist) { tree dst, src, t; @@ -3385,13 +4205,8 @@ expand_builtin_va_copy (arglist) size = expand_expr (TYPE_SIZE_UNIT (va_list_type_node), NULL_RTX, VOIDmode, EXPAND_NORMAL); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (dstb) != Pmode) - dstb = convert_memory_address (Pmode, dstb); - - if (GET_MODE (srcb) != Pmode) - srcb = convert_memory_address (Pmode, srcb); -#endif + dstb = convert_memory_address (Pmode, dstb); + srcb = convert_memory_address (Pmode, srcb); /* "Dereference" to BLKmode memories. */ dstb = gen_rtx_MEM (BLKmode, dstb); @@ -3412,12 +4227,8 @@ expand_builtin_va_copy (arglist) __builtin_return_address. */ static rtx -expand_builtin_frame_address (exp) - tree exp; +expand_builtin_frame_address (tree fndecl, tree arglist) { - tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); - tree arglist = TREE_OPERAND (exp, 1); - /* The argument must be a nonnegative integer constant. It counts the number of frames to scan up the stack. The value is the return address saved in that frame. */ @@ -3465,9 +4276,7 @@ expand_builtin_frame_address (exp) the result in TARGET, if convenient. */ static rtx -expand_builtin_alloca (arglist, target) - tree arglist; - rtx target; +expand_builtin_alloca (tree arglist, rtx target) { rtx op0; rtx result; @@ -3480,24 +4289,19 @@ expand_builtin_alloca (arglist, target) /* Allocate the desired space. */ result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT); - -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (result) != ptr_mode) - result = convert_memory_address (ptr_mode, result); -#endif + result = convert_memory_address (ptr_mode, result); return result; } -/* Expand a call to the ffs builtin. The arguments are in ARGLIST. +/* Expand a call to a unary builtin. The arguments are in ARGLIST. Return 0 if a normal call should be emitted rather than expanding the function in-line. 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_ffs (arglist, target, subtarget) - tree arglist; - rtx target, subtarget; +expand_builtin_unop (enum machine_mode target_mode, tree arglist, rtx target, + rtx subtarget, optab op_optab) { rtx op0; if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) @@ -3505,33 +4309,31 @@ expand_builtin_ffs (arglist, target, subtarget) /* Compute the argument. */ op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); - /* Compute ffs, into TARGET if possible. + /* Compute op, into TARGET if possible. Set TARGET to wherever the result comes back. */ target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))), - ffs_optab, op0, target, 1); + op_optab, op0, target, 1); if (target == 0) abort (); - return target; + + return convert_to_mode (target_mode, target, 0); } /* If the string passed to fputs is a constant and is one character long, we attempt to transform this call into __builtin_fputc(). */ static rtx -expand_builtin_fputs (arglist, ignore, unlocked) - tree arglist; - int ignore; - int unlocked; +expand_builtin_fputs (tree arglist, rtx target, bool unlocked) { tree len, fn; - tree fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED] - : built_in_decls[BUILT_IN_FPUTC]; - tree fn_fwrite = unlocked ? built_in_decls[BUILT_IN_FWRITE_UNLOCKED] - : built_in_decls[BUILT_IN_FWRITE]; + tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_FPUTC]; + tree fn_fwrite = unlocked ? implicit_built_in_decls[BUILT_IN_FWRITE_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_FWRITE]; /* If the return value is used, or the replacement _DECL isn't initialized, don't do the transformation. */ - if (!ignore || !fn_fputc || !fn_fwrite) + if (target != const0_rtx || !fn_fputc || !fn_fwrite) return 0; /* Verify the arguments in the original call. */ @@ -3540,7 +4342,7 @@ expand_builtin_fputs (arglist, ignore, unlocked) /* Get the length of the string passed to fputs. If the length can't be determined, punt. */ - if (!(len = c_strlen (TREE_VALUE (arglist))) + if (!(len = c_strlen (TREE_VALUE (arglist), 1)) || TREE_CODE (len) != INTEGER_CST) return 0; @@ -3570,12 +4372,12 @@ expand_builtin_fputs (arglist, ignore, unlocked) break; } } - /* FALLTHROUGH */ + /* Fall through. */ case 1: /* length is greater than 1, call fwrite. */ { tree string_arg; - /* If optimizing for size keep fputs. */ + /* If optimizing for size keep fputs. */ if (optimize_size) return 0; string_arg = TREE_VALUE (arglist); @@ -3593,8 +4395,7 @@ expand_builtin_fputs (arglist, ignore, unlocked) } return expand_expr (build_function_call_expr (fn, arglist), - (ignore ? const0_rtx : NULL_RTX), - VOIDmode, EXPAND_NORMAL); + const0_rtx, VOIDmode, EXPAND_NORMAL); } /* Expand a call to __builtin_expect. We return our argument and emit a @@ -3602,9 +4403,7 @@ expand_builtin_fputs (arglist, ignore, unlocked) a non-jump context. */ static rtx -expand_builtin_expect (arglist, target) - tree arglist; - rtx target; +expand_builtin_expect (tree arglist, rtx target) { tree exp, c; rtx note, rtx_c; @@ -3633,7 +4432,7 @@ expand_builtin_expect (arglist, target) rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL); - note = emit_note (NULL, NOTE_INSN_EXPECTED_VALUE); + note = emit_note (NOTE_INSN_EXPECTED_VALUE); NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c); } @@ -3648,10 +4447,7 @@ expand_builtin_expect (arglist, target) based on the test being 0/1. */ rtx -expand_builtin_expect_jump (exp, if_false_label, if_true_label) - tree exp; - rtx if_false_label; - rtx if_true_label; +expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) { tree arglist = TREE_OPERAND (exp, 1); tree arg0 = TREE_VALUE (arglist); @@ -3663,111 +4459,106 @@ expand_builtin_expect_jump (exp, if_false_label, if_true_label) if (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE && (integer_zerop (arg1) || integer_onep (arg1))) { - int num_jumps = 0; - rtx insn; - - /* If we fail to locate an appropriate conditional jump, we'll - fall back to normal evaluation. Ensure that the expression - can be re-evaluated. */ - switch (unsafe_for_reeval (arg0)) - { - case 0: /* Safe. */ - break; - - case 1: /* Mildly unsafe. */ - arg0 = unsave_expr (arg0); - break; - - case 2: /* Wildly unsafe. */ - return NULL_RTX; - } + rtx insn, drop_through_label, temp; /* Expand the jump insns. */ start_sequence (); do_jump (arg0, if_false_label, if_true_label); ret = get_insns (); + + drop_through_label = get_last_insn (); + if (drop_through_label && GET_CODE (drop_through_label) == NOTE) + drop_through_label = prev_nonnote_insn (drop_through_label); + if (drop_through_label && GET_CODE (drop_through_label) != CODE_LABEL) + drop_through_label = NULL_RTX; end_sequence (); - /* Now that the __builtin_expect has been validated, go through and add - the expect's to each of the conditional jumps. If we run into an - error, just give up and generate the 'safe' code of doing a SCC - operation and then doing a branch on that. */ + if (! if_true_label) + if_true_label = drop_through_label; + if (! if_false_label) + if_false_label = drop_through_label; + + /* Go through and add the expect's to each of the conditional jumps. */ insn = ret; while (insn != NULL_RTX) { rtx next = NEXT_INSN (insn); - rtx pattern; - if (GET_CODE (insn) == JUMP_INSN && any_condjump_p (insn) - && (pattern = pc_set (insn)) != NULL_RTX) + if (GET_CODE (insn) == JUMP_INSN && any_condjump_p (insn)) { - rtx ifelse = SET_SRC (pattern); - rtx label; - int taken; - - if (GET_CODE (ifelse) != IF_THEN_ELSE) - goto do_next_insn; - - if (GET_CODE (XEXP (ifelse, 1)) == LABEL_REF) - { - taken = 1; - label = XEXP (XEXP (ifelse, 1), 0); - } - /* An inverted jump reverses the probabilities. */ - else if (GET_CODE (XEXP (ifelse, 2)) == LABEL_REF) + rtx ifelse = SET_SRC (pc_set (insn)); + rtx then_dest = XEXP (ifelse, 1); + rtx else_dest = XEXP (ifelse, 2); + int taken = -1; + + /* First check if we recognize any of the labels. */ + if (GET_CODE (then_dest) == LABEL_REF + && XEXP (then_dest, 0) == if_true_label) + taken = 1; + else if (GET_CODE (then_dest) == LABEL_REF + && XEXP (then_dest, 0) == if_false_label) + taken = 0; + else if (GET_CODE (else_dest) == LABEL_REF + && XEXP (else_dest, 0) == if_false_label) + taken = 1; + else if (GET_CODE (else_dest) == LABEL_REF + && XEXP (else_dest, 0) == if_true_label) + taken = 0; + /* Otherwise check where we drop through. */ + else if (else_dest == pc_rtx) { - taken = 0; - label = XEXP (XEXP (ifelse, 2), 0); + if (next && GET_CODE (next) == NOTE) + next = next_nonnote_insn (next); + + if (next && GET_CODE (next) == JUMP_INSN + && any_uncondjump_p (next)) + temp = XEXP (SET_SRC (pc_set (next)), 0); + else + temp = next; + + /* TEMP is either a CODE_LABEL, NULL_RTX or something + else that can't possibly match either target label. */ + if (temp == if_false_label) + taken = 1; + else if (temp == if_true_label) + taken = 0; } - /* We shouldn't have to worry about conditional returns during - the expansion stage, but handle it gracefully anyway. */ - else if (GET_CODE (XEXP (ifelse, 1)) == RETURN) + else if (then_dest == pc_rtx) { - taken = 1; - label = NULL_RTX; + if (next && GET_CODE (next) == NOTE) + next = next_nonnote_insn (next); + + if (next && GET_CODE (next) == JUMP_INSN + && any_uncondjump_p (next)) + temp = XEXP (SET_SRC (pc_set (next)), 0); + else + temp = next; + + if (temp == if_false_label) + taken = 0; + else if (temp == if_true_label) + taken = 1; } - /* An inverted return reverses the probabilities. */ - else if (GET_CODE (XEXP (ifelse, 2)) == RETURN) + + if (taken != -1) { - taken = 0; - label = NULL_RTX; + /* If the test is expected to fail, reverse the + probabilities. */ + if (integer_zerop (arg1)) + taken = 1 - taken; + predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken); } - else - goto do_next_insn; - - /* If the test is expected to fail, reverse the - probabilities. */ - if (integer_zerop (arg1)) - taken = 1 - taken; - - /* If we are jumping to the false label, reverse the - probabilities. */ - if (label == NULL_RTX) - ; /* conditional return */ - else if (label == if_false_label) - taken = 1 - taken; - else if (label != if_true_label) - goto do_next_insn; - - num_jumps++; - predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken); } - do_next_insn: insn = next; } - - /* If no jumps were modified, fail and do __builtin_expect the normal - way. */ - if (num_jumps == 0) - ret = NULL_RTX; } return ret; } void -expand_builtin_trap () +expand_builtin_trap (void) { #ifdef HAVE_trap if (HAVE_trap) @@ -3777,6 +4568,358 @@ expand_builtin_trap () emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0); emit_barrier (); } + +/* Expand a call to fabs, fabsf or fabsl with arguments ARGLIST. + Return 0 if 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_fabs (tree arglist, rtx target, rtx subtarget) +{ + enum machine_mode mode; + tree arg; + rtx op0; + + if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + return 0; + + arg = TREE_VALUE (arglist); + mode = TYPE_MODE (TREE_TYPE (arg)); + op0 = expand_expr (arg, subtarget, VOIDmode, 0); + return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1)); +} + +/* Expand a call to cabs, cabsf or cabsl with arguments ARGLIST. + Return 0 if a normal call should be emitted rather than expanding + the function inline. If convenient, the result should be placed + in target. */ + +static rtx +expand_builtin_cabs (tree arglist, rtx target) +{ + enum machine_mode mode; + tree arg; + rtx op0; + + if (arglist == 0 || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (arg)) != COMPLEX_TYPE + || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE) + return 0; + + mode = TYPE_MODE (TREE_TYPE (arg)); + op0 = expand_expr (arg, NULL_RTX, VOIDmode, 0); + return expand_complex_abs (mode, op0, target, 0); +} + +/* 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 +build_string_literal (int len, const char *str) +{ + tree t, elem, index, type; + + t = build_string (len, str); + elem = build_type_variant (char_type_node, 1, 0); + index = build_index_type (build_int_2 (len - 1, 0)); + type = build_array_type (elem, index); + TREE_TYPE (t) = type; + TREE_CONSTANT (t) = 1; + TREE_READONLY (t) = 1; + TREE_STATIC (t) = 1; + + type = build_pointer_type (type); + t = build1 (ADDR_EXPR, type, t); + + type = build_pointer_type (elem); + t = build1 (NOP_EXPR, type, t); + return t; +} + +/* Expand a call to printf or printf_unlocked with argument list ARGLIST. + 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, + bool unlocked) +{ + tree fn_putchar = unlocked + ? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_PUTCHAR]; + tree fn_puts = unlocked ? implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_PUTS]; + const char *fmt_str; + tree fn, fmt, arg; + + /* If the return value is used, don't do the transformation. */ + if (target != const0_rtx) + return 0; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return 0; + + /* If the format specifier was "%s\n", call __builtin_puts(arg). */ + if (strcmp (fmt_str, "%s\n") == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || TREE_CHAIN (arglist)) + return 0; + fn = fn_puts; + } + /* If the format specifier was "%c", call __builtin_putchar(arg). */ + else if (strcmp (fmt_str, "%c") == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE + || TREE_CHAIN (arglist)) + return 0; + fn = fn_putchar; + } + else + { + /* We can't handle anything else with % args or %% ... yet. */ + if (strchr (fmt_str, '%')) + return 0; + + if (arglist) + return 0; + + /* If the format specifier was "", printf does nothing. */ + if (fmt_str[0] == '\0') + return const0_rtx; + /* If the format specifier has length of 1, call putchar. */ + if (fmt_str[1] == '\0') + { + /* Given printf("c"), (where c is any one character,) + convert "c"[0] to an int and pass that to the replacement + function. */ + arg = build_int_2 (fmt_str[0], 0); + arglist = build_tree_list (NULL_TREE, arg); + fn = fn_putchar; + } + else + { + /* If the format specifier was "string\n", call puts("string"). */ + size_t len = strlen (fmt_str); + if (fmt_str[len - 1] == '\n') + { + /* Create a NUL-terminated string that's one char shorter + than the original, stripping off the trailing '\n'. */ + char *newstr = (char *) alloca (len); + memcpy (newstr, fmt_str, len - 1); + newstr[len - 1] = 0; + + arg = build_string_literal (len, newstr); + arglist = build_tree_list (NULL_TREE, arg); + fn = fn_puts; + } + else + /* We'd like to arrange to call fputs(string,stdout) here, + but we need stdout and don't have a way to get it yet. */ + return 0; + } + } + + if (!fn) + return 0; + return expand_expr (build_function_call_expr (fn, arglist), + target, mode, EXPAND_NORMAL); +} + +/* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST. + 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, + bool unlocked) +{ + tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_FPUTC]; + tree fn_fputs = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED] + : implicit_built_in_decls[BUILT_IN_FPUTS]; + const char *fmt_str; + tree fn, fmt, fp, arg; + + /* If the return value is used, don't do the transformation. */ + if (target != const0_rtx) + return 0; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + fp = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (fp)) != POINTER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return 0; + + /* If the format specifier was "%s", call __builtin_fputs(arg,fp). */ + if (strcmp (fmt_str, "%s") == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, arg, arglist); + fn = fn_fputs; + } + /* If the format specifier was "%c", call __builtin_fputc(arg,fp). */ + else if (strcmp (fmt_str, "%c") == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE + || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, arg, arglist); + fn = fn_fputc; + } + else + { + /* We can't handle anything else with % args or %% ... yet. */ + if (strchr (fmt_str, '%')) + return 0; + + if (arglist) + return 0; + + /* If the format specifier was "", fprintf does nothing. */ + if (fmt_str[0] == '\0') + { + /* Evaluate and ignore FILE* argument for side-effects. */ + expand_expr (fp, const0_rtx, VOIDmode, EXPAND_NORMAL); + return const0_rtx; + } + + /* When "string" doesn't contain %, replace all cases of + fprintf(stream,string) with fputs(string,stream). The fputs + builtin will take care of special cases like length == 1. */ + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, fmt, arglist); + fn = fn_fputs; + } + + if (!fn) + return 0; + return expand_expr (build_function_call_expr (fn, arglist), + target, mode, EXPAND_NORMAL); +} + +/* Expand a call to sprintf with argument list ARGLIST. Return 0 if + a normal call should be emitted rather than expanding the function + inline. If convenient, the result should be placed in TARGET with + mode MODE. */ + +static rtx +expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) +{ + tree orig_arglist, dest, fmt; + const char *fmt_str; + + orig_arglist = arglist; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + dest = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return 0; + + /* If the format doesn't contain % args or %%, use strcpy. */ + if (strchr (fmt_str, '%') == 0) + { + tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + tree exp; + + if (arglist || ! fn) + return 0; + expand_expr (build_function_call_expr (fn, orig_arglist), + const0_rtx, VOIDmode, EXPAND_NORMAL); + if (target == const0_rtx) + return const0_rtx; + exp = build_int_2 (strlen (fmt_str), 0); + exp = fold (build1 (NOP_EXPR, integer_type_node, exp)); + return expand_expr (exp, target, mode, EXPAND_NORMAL); + } + /* If the format is "%s", use strcpy if the result isn't used. */ + else if (strcmp (fmt_str, "%s") == 0) + { + tree fn, arg, len; + fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + + if (! fn) + return 0; + + if (! arglist || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) + return 0; + + if (target != const0_rtx) + { + len = c_strlen (arg, 1); + if (! len || TREE_CODE (len) != INTEGER_CST) + return 0; + } + else + len = NULL_TREE; + + arglist = build_tree_list (NULL_TREE, arg); + arglist = tree_cons (NULL_TREE, dest, arglist); + expand_expr (build_function_call_expr (fn, arglist), + const0_rtx, VOIDmode, EXPAND_NORMAL); + + if (target == const0_rtx) + return const0_rtx; + return expand_expr (len, target, mode, EXPAND_NORMAL); + } + + return 0; +} /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient @@ -3785,18 +4928,15 @@ expand_builtin_trap () IGNORE is nonzero if the value is to be ignored. */ rtx -expand_builtin (exp, target, subtarget, mode, ignore) - tree exp; - rtx target; - rtx subtarget; - enum machine_mode mode; - int ignore; +expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, + int ignore) { - tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); + enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp)); - /* Perform postincrements before expanding builtin functions. */ + /* Perform postincrements before expanding builtin functions. */ emit_queue (); if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) @@ -3804,59 +4944,41 @@ expand_builtin (exp, target, subtarget, mode, ignore) /* When not optimizing, generate calls to library functions for a certain set of builtins. */ - if (!optimize && !CALLED_AS_BUILT_IN (fndecl)) - switch (fcode) - { - case BUILT_IN_SQRT: - case BUILT_IN_SQRTF: - case BUILT_IN_SQRTL: - case BUILT_IN_SIN: - case BUILT_IN_SINF: - case BUILT_IN_SINL: - case BUILT_IN_COS: - case BUILT_IN_COSF: - case BUILT_IN_COSL: - case BUILT_IN_EXP: - case BUILT_IN_EXPF: - case BUILT_IN_EXPL: - case BUILT_IN_MEMSET: - case BUILT_IN_MEMCPY: - case BUILT_IN_MEMCMP: - case BUILT_IN_BCMP: - case BUILT_IN_BZERO: - case BUILT_IN_INDEX: - case BUILT_IN_RINDEX: - case BUILT_IN_STRCHR: - case BUILT_IN_STRRCHR: - case BUILT_IN_STRLEN: - case BUILT_IN_STRCPY: - case BUILT_IN_STRNCPY: - case BUILT_IN_STRNCMP: - case BUILT_IN_STRSTR: - case BUILT_IN_STRPBRK: - case BUILT_IN_STRCAT: - case BUILT_IN_STRNCAT: - case BUILT_IN_STRSPN: - case BUILT_IN_STRCSPN: - case BUILT_IN_STRCMP: - case BUILT_IN_FFS: - case BUILT_IN_PUTCHAR: - case BUILT_IN_PUTS: - case BUILT_IN_PRINTF: - case BUILT_IN_FPUTC: - case BUILT_IN_FPUTS: - case BUILT_IN_FWRITE: - case BUILT_IN_PUTCHAR_UNLOCKED: - case BUILT_IN_PUTS_UNLOCKED: - case BUILT_IN_PRINTF_UNLOCKED: - case BUILT_IN_FPUTC_UNLOCKED: - case BUILT_IN_FPUTS_UNLOCKED: - case BUILT_IN_FWRITE_UNLOCKED: - return expand_call (exp, target, ignore); + if (!optimize + && !CALLED_AS_BUILT_IN (fndecl) + && DECL_ASSEMBLER_NAME_SET_P (fndecl) + && fcode != BUILT_IN_ALLOCA) + return expand_call (exp, target, ignore); + + /* The built-in function expanders test for target == const0_rtx + to determine whether the function's result will be ignored. */ + if (ignore) + target = const0_rtx; + + /* If the result of a pure or const built-in function is ignored, and + none of its arguments are volatile, we can avoid expanding the + built-in call and just evaluate the arguments for side-effects. */ + if (target == const0_rtx + && (DECL_IS_PURE (fndecl) || TREE_READONLY (fndecl))) + { + bool volatilep = false; + tree arg; - default: - break; - } + for (arg = arglist; arg; arg = TREE_CHAIN (arg)) + if (TREE_THIS_VOLATILE (TREE_VALUE (arg))) + { + volatilep = true; + break; + } + + if (! volatilep) + { + for (arg = arglist; arg; arg = TREE_CHAIN (arg)) + expand_expr (TREE_VALUE (arg), const0_rtx, + VOIDmode, EXPAND_NORMAL); + return const0_rtx; + } + } switch (fcode) { @@ -3864,11 +4986,27 @@ expand_builtin (exp, target, subtarget, mode, ignore) case BUILT_IN_LABS: case BUILT_IN_LLABS: case BUILT_IN_IMAXABS: + /* build_function_call changes these into ABS_EXPR. */ + abort (); + case BUILT_IN_FABS: case BUILT_IN_FABSF: case BUILT_IN_FABSL: - /* build_function_call changes these into ABS_EXPR. */ - abort (); + target = expand_builtin_fabs (arglist, target, subtarget); + if (target) + return target; + break; + + case BUILT_IN_CABS: + case BUILT_IN_CABSF: + case BUILT_IN_CABSL: + if (flag_unsafe_math_optimizations) + { + target = expand_builtin_cabs (arglist, target); + if (target) + return target; + } + break; case BUILT_IN_CONJ: case BUILT_IN_CONJF: @@ -3895,6 +5033,12 @@ expand_builtin (exp, target, subtarget, mode, ignore) case BUILT_IN_LOG: case BUILT_IN_LOGF: case BUILT_IN_LOGL: + case BUILT_IN_TAN: + case BUILT_IN_TANF: + case BUILT_IN_TANL: + case BUILT_IN_ATAN: + case BUILT_IN_ATANF: + case BUILT_IN_ATANL: /* Treat these like sqrt only if unsafe math optimizations are allowed, because of possible accuracy problems. */ if (! flag_unsafe_math_optimizations) @@ -3902,11 +5046,46 @@ expand_builtin (exp, target, subtarget, mode, ignore) case BUILT_IN_SQRT: case BUILT_IN_SQRTF: case BUILT_IN_SQRTL: + case BUILT_IN_FLOOR: + case BUILT_IN_FLOORF: + case BUILT_IN_FLOORL: + case BUILT_IN_CEIL: + case BUILT_IN_CEILF: + case BUILT_IN_CEILL: + case BUILT_IN_TRUNC: + case BUILT_IN_TRUNCF: + case BUILT_IN_TRUNCL: + case BUILT_IN_ROUND: + case BUILT_IN_ROUNDF: + case BUILT_IN_ROUNDL: + case BUILT_IN_NEARBYINT: + case BUILT_IN_NEARBYINTF: + case BUILT_IN_NEARBYINTL: target = expand_builtin_mathfn (exp, target, subtarget); if (target) return target; break; + case BUILT_IN_POW: + case BUILT_IN_POWF: + case BUILT_IN_POWL: + if (! flag_unsafe_math_optimizations) + break; + target = expand_builtin_pow (exp, target, subtarget); + if (target) + return target; + break; + + case BUILT_IN_ATAN2: + case BUILT_IN_ATAN2F: + case BUILT_IN_ATAN2L: + if (! flag_unsafe_math_optimizations) + break; + target = expand_builtin_mathfn_2 (exp, target, subtarget); + if (target) + return target; + break; + case BUILT_IN_APPLY_ARGS: return expand_builtin_apply_args (); @@ -3951,7 +5130,7 @@ expand_builtin (exp, target, subtarget, mode, ignore) return expand_builtin_saveregs (); case BUILT_IN_ARGS_INFO: - return expand_builtin_args_info (exp); + return expand_builtin_args_info (arglist); /* Return the address of the first anonymous stack arg. */ case BUILT_IN_NEXT_ARG: @@ -3961,11 +5140,11 @@ expand_builtin (exp, target, subtarget, mode, ignore) return expand_builtin_classify_type (arglist); case BUILT_IN_CONSTANT_P: - return expand_builtin_constant_p (exp); + return expand_builtin_constant_p (arglist, target_mode); case BUILT_IN_FRAME_ADDRESS: case BUILT_IN_RETURN_ADDRESS: - return expand_builtin_frame_address (exp); + return expand_builtin_frame_address (fndecl, arglist); /* Returns the address of the area where the structure is returned. 0 otherwise. */ @@ -3984,19 +5163,58 @@ expand_builtin (exp, target, subtarget, mode, ignore) break; case BUILT_IN_FFS: - target = expand_builtin_ffs (arglist, target, subtarget); + case BUILT_IN_FFSL: + case BUILT_IN_FFSLL: + target = expand_builtin_unop (target_mode, arglist, target, + subtarget, ffs_optab); + if (target) + return target; + break; + + case BUILT_IN_CLZ: + case BUILT_IN_CLZL: + case BUILT_IN_CLZLL: + target = expand_builtin_unop (target_mode, arglist, target, + subtarget, clz_optab); + if (target) + return target; + break; + + case BUILT_IN_CTZ: + case BUILT_IN_CTZL: + case BUILT_IN_CTZLL: + target = expand_builtin_unop (target_mode, arglist, target, + subtarget, ctz_optab); + if (target) + return target; + break; + + case BUILT_IN_POPCOUNT: + case BUILT_IN_POPCOUNTL: + case BUILT_IN_POPCOUNTLL: + target = expand_builtin_unop (target_mode, arglist, target, + subtarget, popcount_optab); + if (target) + return target; + break; + + case BUILT_IN_PARITY: + case BUILT_IN_PARITYL: + case BUILT_IN_PARITYLL: + target = expand_builtin_unop (target_mode, arglist, target, + subtarget, parity_optab); if (target) return target; break; case BUILT_IN_STRLEN: - target = expand_builtin_strlen (exp, target); + target = expand_builtin_strlen (arglist, target, target_mode); if (target) return target; break; case BUILT_IN_STRCPY: - target = expand_builtin_strcpy (exp, target, mode); + target = expand_builtin_strcpy (arglist, target, mode); if (target) return target; break; @@ -4007,6 +5225,12 @@ expand_builtin (exp, target, subtarget, mode, ignore) return target; break; + case BUILT_IN_STPCPY: + target = expand_builtin_stpcpy (arglist, target, mode); + if (target) + return target; + break; + case BUILT_IN_STRCAT: target = expand_builtin_strcat (arglist, target, mode); if (target) @@ -4063,14 +5287,32 @@ expand_builtin (exp, target, subtarget, mode, ignore) return target; break; + case BUILT_IN_MEMPCPY: + target = expand_builtin_mempcpy (arglist, target, mode, /*endp=*/ 1); + if (target) + return target; + break; + + case BUILT_IN_MEMMOVE: + target = expand_builtin_memmove (arglist, target, mode); + if (target) + return target; + break; + + case BUILT_IN_BCOPY: + target = expand_builtin_bcopy (arglist); + if (target) + return target; + break; + case BUILT_IN_MEMSET: - target = expand_builtin_memset (exp, target, mode); + target = expand_builtin_memset (arglist, target, mode); if (target) return target; break; case BUILT_IN_BZERO: - target = expand_builtin_bzero (exp); + target = expand_builtin_bzero (arglist); if (target) return target; break; @@ -4127,13 +5369,44 @@ expand_builtin (exp, target, subtarget, mode, ignore) expand_builtin_trap (); return const0_rtx; + case BUILT_IN_PRINTF: + target = expand_builtin_printf (arglist, target, mode, false); + if (target) + return target; + break; + + case BUILT_IN_PRINTF_UNLOCKED: + target = expand_builtin_printf (arglist, target, mode, true); + if (target) + return target; + break; + case BUILT_IN_FPUTS: - target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 0); + target = expand_builtin_fputs (arglist, target, false); if (target) return target; break; + case BUILT_IN_FPUTS_UNLOCKED: - target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 1); + target = expand_builtin_fputs (arglist, target, true); + if (target) + return target; + break; + + case BUILT_IN_FPRINTF: + target = expand_builtin_fprintf (arglist, target, mode, false); + if (target) + return target; + break; + + case BUILT_IN_FPRINTF_UNLOCKED: + target = expand_builtin_fprintf (arglist, target, mode, true); + if (target) + return target; + break; + + case BUILT_IN_SPRINTF: + target = expand_builtin_sprintf (arglist, target, mode); if (target) return target; break; @@ -4163,6 +5436,9 @@ expand_builtin (exp, target, subtarget, mode, ignore) case BUILT_IN_EH_RETURN_DATA_REGNO: return expand_builtin_eh_return_data_regno (arglist); #endif + case BUILT_IN_EXTEND_POINTER: + return expand_builtin_extend_pointer (TREE_VALUE (arglist)); + case BUILT_IN_VA_START: case BUILT_IN_STDARG_START: return expand_builtin_va_start (arglist); @@ -4188,12 +5464,83 @@ expand_builtin (exp, target, subtarget, mode, ignore) return expand_call (exp, target, ignore); } +/* Determine whether a tree node represents a call to a built-in + function. If the tree T is a call to a built-in function with + the right number of arguments of the appropriate types, return + the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT. + Otherwise the return value is END_BUILTINS. */ + +enum built_in_function +builtin_mathfn_code (tree t) +{ + tree fndecl, arglist, parmlist; + tree argtype, parmtype; + + if (TREE_CODE (t) != CALL_EXPR + || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR) + return END_BUILTINS; + + fndecl = get_callee_fndecl (t); + if (fndecl == NULL_TREE + || TREE_CODE (fndecl) != FUNCTION_DECL + || ! DECL_BUILT_IN (fndecl) + || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) + return END_BUILTINS; + + arglist = TREE_OPERAND (t, 1); + parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + for (; parmlist; parmlist = TREE_CHAIN (parmlist)) + { + /* If a function doesn't take a variable number of arguments, + the last element in the list will have type `void'. */ + parmtype = TREE_VALUE (parmlist); + if (VOID_TYPE_P (parmtype)) + { + if (arglist) + return END_BUILTINS; + return DECL_FUNCTION_CODE (fndecl); + } + + if (! arglist) + return END_BUILTINS; + + argtype = TREE_TYPE (TREE_VALUE (arglist)); + + if (SCALAR_FLOAT_TYPE_P (parmtype)) + { + if (! SCALAR_FLOAT_TYPE_P (argtype)) + return END_BUILTINS; + } + else if (COMPLEX_FLOAT_TYPE_P (parmtype)) + { + if (! COMPLEX_FLOAT_TYPE_P (argtype)) + return END_BUILTINS; + } + else if (POINTER_TYPE_P (parmtype)) + { + if (! POINTER_TYPE_P (argtype)) + return END_BUILTINS; + } + else if (INTEGRAL_TYPE_P (parmtype)) + { + if (! INTEGRAL_TYPE_P (argtype)) + return END_BUILTINS; + } + else + return END_BUILTINS; + + arglist = TREE_CHAIN (arglist); + } + + /* Variable-length argument list. */ + return DECL_FUNCTION_CODE (fndecl); +} + /* Fold a call to __builtin_constant_p, if we know it will evaluate to a constant. ARGLIST is the argument list of the call. */ static tree -fold_builtin_constant_p (arglist) - tree arglist; +fold_builtin_constant_p (tree arglist) { if (arglist == 0) return 0; @@ -4213,15 +5560,14 @@ fold_builtin_constant_p (arglist) && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST)) return integer_one_node; - /* If we aren't going to be running CSE or 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 those - case we only want literals, since those are only optimized + /* 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 + those case we only want literals, since those are only optimized when generating RTL, not later. And finally, if we are compiling an initializer, not code, we need to return a definite result now; there's not going to be any more optimization done. */ - if (TREE_SIDE_EFFECTS (arglist) || cse_not_expected + if (TREE_SIDE_EFFECTS (arglist) || AGGREGATE_TYPE_P (TREE_TYPE (arglist)) || POINTER_TYPE_P (TREE_TYPE (arglist)) || cfun == 0) @@ -4233,8 +5579,7 @@ fold_builtin_constant_p (arglist) /* Fold a call to __builtin_classify_type. */ static tree -fold_builtin_classify_type (arglist) - tree arglist; +fold_builtin_classify_type (tree arglist) { if (arglist == 0) return build_int_2 (no_type_class, 0); @@ -4245,9 +5590,7 @@ fold_builtin_classify_type (arglist) /* Fold a call to __builtin_inf or __builtin_huge_val. */ static tree -fold_builtin_inf (type, warn) - tree type; - int warn; +fold_builtin_inf (tree type, int warn) { REAL_VALUE_TYPE real; @@ -4261,9 +5604,7 @@ fold_builtin_inf (type, warn) /* Fold a call to __builtin_nan or __builtin_nans. */ static tree -fold_builtin_nan (arglist, type, quiet) - tree arglist, type; - int quiet; +fold_builtin_nan (tree arglist, tree type, int quiet) { REAL_VALUE_TYPE real; const char *str; @@ -4280,21 +5621,898 @@ fold_builtin_nan (arglist, type, quiet) return build_real (type, real); } +/* Return true if the floating point expression T has an integer value. + We also allow +Inf, -Inf and NaN to be considered integer values. */ + +static bool +integer_valued_real_p (tree t) +{ + switch (TREE_CODE (t)) + { + case FLOAT_EXPR: + return true; + + case ABS_EXPR: + case SAVE_EXPR: + case NON_LVALUE_EXPR: + return integer_valued_real_p (TREE_OPERAND (t, 0)); + + case COMPOUND_EXPR: + case MODIFY_EXPR: + case BIND_EXPR: + return integer_valued_real_p (TREE_OPERAND (t, 1)); + + case PLUS_EXPR: + case MINUS_EXPR: + case MULT_EXPR: + case MIN_EXPR: + case MAX_EXPR: + return integer_valued_real_p (TREE_OPERAND (t, 0)) + && integer_valued_real_p (TREE_OPERAND (t, 1)); + + case COND_EXPR: + return integer_valued_real_p (TREE_OPERAND (t, 1)) + && integer_valued_real_p (TREE_OPERAND (t, 2)); + + case REAL_CST: + if (! TREE_CONSTANT_OVERFLOW (t)) + { + REAL_VALUE_TYPE c, cint; + + c = TREE_REAL_CST (t); + real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c); + return real_identical (&c, &cint); + } + + case NOP_EXPR: + { + tree type = TREE_TYPE (TREE_OPERAND (t, 0)); + if (TREE_CODE (type) == INTEGER_TYPE) + return true; + if (TREE_CODE (type) == REAL_TYPE) + return integer_valued_real_p (TREE_OPERAND (t, 0)); + break; + } + + case CALL_EXPR: + switch (builtin_mathfn_code (t)) + { + case BUILT_IN_CEIL: + case BUILT_IN_CEILF: + case BUILT_IN_CEILL: + case BUILT_IN_FLOOR: + case BUILT_IN_FLOORF: + case BUILT_IN_FLOORL: + case BUILT_IN_NEARBYINT: + case BUILT_IN_NEARBYINTF: + case BUILT_IN_NEARBYINTL: + case BUILT_IN_ROUND: + case BUILT_IN_ROUNDF: + case BUILT_IN_ROUNDL: + case BUILT_IN_TRUNC: + case BUILT_IN_TRUNCF: + case BUILT_IN_TRUNCL: + return true; + + default: + break; + } + break; + + default: + break; + } + return false; +} + +/* EXP is assumed to be builtin call where truncation can be propagated + across (for instance floor((double)f) == (double)floorf (f). + Do the transformation. */ + +static tree +fold_trunc_transparent_mathfn (tree exp) +{ + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); + tree arg; + + if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + return 0; + + arg = TREE_VALUE (arglist); + /* Integer rounding functions are idempotent. */ + if (fcode == builtin_mathfn_code (arg)) + return arg; + + /* 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 arg; + + if (optimize) + { + tree arg0 = strip_float_extensions (arg); + tree ftype = TREE_TYPE (exp); + tree newtype = TREE_TYPE (arg0); + tree decl; + + if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype) + && (decl = mathfn_built_in (newtype, fcode))) + { + arglist = + build_tree_list (NULL_TREE, fold (convert (newtype, arg0))); + return convert (ftype, + build_function_call_expr (decl, arglist)); + } + } + return 0; +} + +/* Fold function call to builtin cabs, cabsf or cabsl. FNDECL is the + function's DECL, ARGLIST is the argument list and TYPE is the return + type. Return NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_cabs (tree fndecl, tree arglist, tree type) +{ + tree arg; + + if (!arglist || TREE_CHAIN (arglist)) + return NULL_TREE; + + arg = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (arg)) != COMPLEX_TYPE + || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE) + return NULL_TREE; + + /* Evaluate cabs of a constant at compile-time. */ + if (flag_unsafe_math_optimizations + && TREE_CODE (arg) == COMPLEX_CST + && TREE_CODE (TREE_REALPART (arg)) == REAL_CST + && TREE_CODE (TREE_IMAGPART (arg)) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (TREE_REALPART (arg)) + && ! TREE_CONSTANT_OVERFLOW (TREE_IMAGPART (arg))) + { + REAL_VALUE_TYPE r, i; + + r = TREE_REAL_CST (TREE_REALPART (arg)); + i = TREE_REAL_CST (TREE_IMAGPART (arg)); + + real_arithmetic (&r, MULT_EXPR, &r, &r); + real_arithmetic (&i, MULT_EXPR, &i, &i); + real_arithmetic (&r, PLUS_EXPR, &r, &i); + if (real_sqrt (&r, TYPE_MODE (type), &r) + || ! flag_trapping_math) + return build_real (type, r); + } + + /* If either part is zero, cabs is fabs of the other. */ + if (TREE_CODE (arg) == COMPLEX_EXPR + && real_zerop (TREE_OPERAND (arg, 0))) + return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1))); + if (TREE_CODE (arg) == COMPLEX_EXPR + && real_zerop (TREE_OPERAND (arg, 1))) + return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0))); + + if (flag_unsafe_math_optimizations) + { + enum built_in_function fcode; + tree sqrtfn; + + fcode = DECL_FUNCTION_CODE (fndecl); + if (fcode == BUILT_IN_CABS) + sqrtfn = implicit_built_in_decls[BUILT_IN_SQRT]; + else if (fcode == BUILT_IN_CABSF) + sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTF]; + else if (fcode == BUILT_IN_CABSL) + sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTL]; + else + sqrtfn = NULL_TREE; + + if (sqrtfn != NULL_TREE) + { + tree rpart, ipart, result, arglist; + + arg = save_expr (arg); + + rpart = fold (build1 (REALPART_EXPR, type, arg)); + ipart = fold (build1 (IMAGPART_EXPR, type, arg)); + + rpart = save_expr (rpart); + ipart = save_expr (ipart); + + result = fold (build (PLUS_EXPR, type, + fold (build (MULT_EXPR, type, + rpart, rpart)), + fold (build (MULT_EXPR, type, + ipart, ipart)))); + + arglist = build_tree_list (NULL_TREE, result); + return build_function_call_expr (sqrtfn, arglist); + } + } + + return NULL_TREE; +} + +/* Fold function call to builtin trunc, truncf or truncl. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_trunc (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree arg; + + if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + return 0; + + /* Optimize trunc of constant value. */ + arg = TREE_VALUE (arglist); + if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) + { + REAL_VALUE_TYPE r, x; + tree type = TREE_TYPE (exp); + + x = TREE_REAL_CST (arg); + real_trunc (&r, TYPE_MODE (type), &x); + return build_real (type, r); + } + + return fold_trunc_transparent_mathfn (exp); +} + +/* 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) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree arg; + + if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + return 0; + + /* Optimize floor of constant value. */ + arg = TREE_VALUE (arglist); + if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) + { + REAL_VALUE_TYPE x; + + x = TREE_REAL_CST (arg); + if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) + { + tree type = TREE_TYPE (exp); + REAL_VALUE_TYPE r; + + real_floor (&r, TYPE_MODE (type), &x); + return build_real (type, r); + } + } + + return fold_trunc_transparent_mathfn (exp); +} + +/* 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) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree arg; + + if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + return 0; + + /* Optimize ceil of constant value. */ + arg = TREE_VALUE (arglist); + if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) + { + REAL_VALUE_TYPE x; + + x = TREE_REAL_CST (arg); + if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math) + { + tree type = TREE_TYPE (exp); + REAL_VALUE_TYPE r; + + real_ceil (&r, TYPE_MODE (type), &x); + return build_real (type, r); + } + } + + return fold_trunc_transparent_mathfn (exp); +} + +/* Fold function call to builtin ffs, clz, ctz, popcount and parity + and their long and long long variants (i.e. ffsl and ffsll). + Return NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_bitop (tree exp) +{ + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + tree arg; + + if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) + return NULL_TREE; + + /* Optimize for constant argument. */ + arg = TREE_VALUE (arglist); + if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg)) + { + HOST_WIDE_INT hi, width, result; + unsigned HOST_WIDE_INT lo; + tree type, t; + + type = TREE_TYPE (arg); + width = TYPE_PRECISION (type); + lo = TREE_INT_CST_LOW (arg); + + /* Clear all the bits that are beyond the type's precision. */ + if (width > HOST_BITS_PER_WIDE_INT) + { + hi = TREE_INT_CST_HIGH (arg); + if (width < 2 * HOST_BITS_PER_WIDE_INT) + hi &= ~((HOST_WIDE_INT) (-1) >> (width - HOST_BITS_PER_WIDE_INT)); + } + else + { + hi = 0; + if (width < HOST_BITS_PER_WIDE_INT) + lo &= ~((unsigned HOST_WIDE_INT) (-1) << width); + } + + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_FFS: + case BUILT_IN_FFSL: + case BUILT_IN_FFSLL: + if (lo != 0) + result = exact_log2 (lo & -lo) + 1; + else if (hi != 0) + result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi) + 1; + else + result = 0; + break; + + case BUILT_IN_CLZ: + case BUILT_IN_CLZL: + case BUILT_IN_CLZLL: + if (hi != 0) + result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT; + else if (lo != 0) + result = width - floor_log2 (lo) - 1; + else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result)) + result = width; + break; + + case BUILT_IN_CTZ: + case BUILT_IN_CTZL: + case BUILT_IN_CTZLL: + if (lo != 0) + result = exact_log2 (lo & -lo); + else if (hi != 0) + result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi); + else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result)) + result = width; + break; + + case BUILT_IN_POPCOUNT: + case BUILT_IN_POPCOUNTL: + case BUILT_IN_POPCOUNTLL: + result = 0; + while (lo) + result++, lo &= lo - 1; + while (hi) + result++, hi &= hi - 1; + break; + + case BUILT_IN_PARITY: + case BUILT_IN_PARITYL: + case BUILT_IN_PARITYLL: + result = 0; + while (lo) + result++, lo &= lo - 1; + while (hi) + result++, hi &= hi - 1; + result &= 1; + break; + + default: + abort(); + } + + t = build_int_2 (result, 0); + TREE_TYPE (t) = TREE_TYPE (exp); + return t; + } + + return NULL_TREE; +} + +/* Return true if EXPR is the real constant contained in VALUE. */ + +static bool +real_dconstp (tree expr, const REAL_VALUE_TYPE *value) +{ + STRIP_NOPS (expr); + + return ((TREE_CODE (expr) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (expr) + && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), *value)) + || (TREE_CODE (expr) == COMPLEX_CST + && real_dconstp (TREE_REALPART (expr), value) + && real_zerop (TREE_IMAGPART (expr)))); +} + +/* A subroutine of fold_builtin to fold the various logarithmic + functions. EXP is the CALL_EXPR of a call to a builtin log* + function. VALUE is the base of the log* function. */ + +static tree +fold_builtin_logarithm (tree exp, 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); + + /* Optimize log*(1.0) = 0.0. */ + if (real_onep (arg)) + return build_real (type, dconst0); + + /* Optimize logN(N) = 1.0. If N can't be truncated to MODE + exactly, then only do this if flag_unsafe_math_optimizations. */ + if (exact_real_truncate (TYPE_MODE (type), value) + || flag_unsafe_math_optimizations) + { + const REAL_VALUE_TYPE value_truncate = + real_value_truncate (TYPE_MODE (type), *value); + if (real_dconstp (arg, &value_truncate)) + return build_real (type, dconst1); + } + + /* Special case, optimize logN(expN(x)) = x. */ + if (flag_unsafe_math_optimizations + && ((value == &dconste + && (fcode == BUILT_IN_EXP + || fcode == BUILT_IN_EXPF + || fcode == BUILT_IN_EXPL)) + || (value == &dconst2 + && (fcode == BUILT_IN_EXP2 + || fcode == BUILT_IN_EXP2F + || fcode == BUILT_IN_EXP2L)) + || (value == &dconst10 + && (fcode == BUILT_IN_EXP10 + || fcode == BUILT_IN_EXP10F + || fcode == BUILT_IN_EXP10L)))) + return convert (type, TREE_VALUE (TREE_OPERAND (arg, 1))); + + /* Optimize log*(func()) for various exponential functions. We + want to determine the value "x" and the power "exponent" in + order to transform logN(x**exponent) into exponent*logN(x). */ + if (flag_unsafe_math_optimizations) + { + tree exponent = 0, x = 0; + + switch (fcode) + { + case BUILT_IN_EXP: + case BUILT_IN_EXPF: + case BUILT_IN_EXPL: + /* Prepare to do logN(exp(exponent) -> exponent*logN(e). */ + x = build_real (type, + real_value_truncate (TYPE_MODE (type), dconste)); + exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); + break; + case BUILT_IN_EXP2: + case BUILT_IN_EXP2F: + case BUILT_IN_EXP2L: + /* Prepare to do logN(exp2(exponent) -> exponent*logN(2). */ + x = build_real (type, dconst2); + exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); + break; + case BUILT_IN_EXP10: + case BUILT_IN_EXP10F: + case BUILT_IN_EXP10L: + case BUILT_IN_POW10: + case BUILT_IN_POW10F: + case BUILT_IN_POW10L: + /* Prepare to do logN(exp10(exponent) -> exponent*logN(10). */ + x = build_real (type, dconst10); + exponent = TREE_VALUE (TREE_OPERAND (arg, 1)); + break; + case BUILT_IN_SQRT: + case BUILT_IN_SQRTF: + case BUILT_IN_SQRTL: + /* Prepare to do logN(sqrt(x) -> 0.5*logN(x). */ + x = TREE_VALUE (TREE_OPERAND (arg, 1)); + exponent = build_real (type, dconsthalf); + break; + case BUILT_IN_CBRT: + case BUILT_IN_CBRTF: + case BUILT_IN_CBRTL: + /* Prepare to do logN(cbrt(x) -> (1/3)*logN(x). */ + x = TREE_VALUE (TREE_OPERAND (arg, 1)); + exponent = build_real (type, real_value_truncate (TYPE_MODE (type), + dconstthird)); + break; + case BUILT_IN_POW: + case BUILT_IN_POWF: + case BUILT_IN_POWL: + /* Prepare to do logN(pow(x,exponent) -> exponent*logN(x). */ + x = TREE_VALUE (TREE_OPERAND (arg, 1)); + exponent = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1))); + break; + default: + break; + } + + /* Now perform the optimization. */ + if (x && exponent) + { + tree logfn; + arglist = build_tree_list (NULL_TREE, x); + logfn = build_function_call_expr (fndecl, arglist); + return fold (build (MULT_EXPR, type, exponent, logfn)); + } + } + } + + return 0; +} + +/* A subroutine of fold_builtin to fold the various exponent + functions. EXP is the CALL_EXPR of a call to a builtin function. + VALUE is the value which will be raised to a power. */ + +static tree +fold_builtin_exponent (tree exp, 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); + + /* Optimize exp*(0.0) = 1.0. */ + if (real_zerop (arg)) + return build_real (type, dconst1); + + /* Optimize expN(1.0) = N. */ + if (real_onep (arg)) + { + REAL_VALUE_TYPE cst; + + real_convert (&cst, TYPE_MODE (type), value); + return build_real (type, cst); + } + + /* Attempt to evaluate expN(integer) at compile-time. */ + if (flag_unsafe_math_optimizations + && TREE_CODE (arg) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg)) + { + REAL_VALUE_TYPE cint; + REAL_VALUE_TYPE c; + HOST_WIDE_INT n; + + c = TREE_REAL_CST (arg); + n = real_to_integer (&c); + real_from_integer (&cint, VOIDmode, n, + n < 0 ? -1 : 0, 0); + if (real_identical (&c, &cint)) + { + REAL_VALUE_TYPE x; + + real_powi (&x, TYPE_MODE (type), value, n); + return build_real (type, x); + } + } + + /* Optimize expN(logN(x)) = x. */ + if (flag_unsafe_math_optimizations) + { + const enum built_in_function fcode = builtin_mathfn_code (arg); + + if ((value == &dconste + && (fcode == BUILT_IN_LOG + || fcode == BUILT_IN_LOGF + || fcode == BUILT_IN_LOGL)) + || (value == &dconst2 + && (fcode == BUILT_IN_LOG2 + || fcode == BUILT_IN_LOG2F + || fcode == BUILT_IN_LOG2L)) + || (value == &dconst10 + && (fcode == BUILT_IN_LOG10 + || fcode == BUILT_IN_LOG10F + || fcode == BUILT_IN_LOG10L))) + return convert (type, TREE_VALUE (TREE_OPERAND (arg, 1))); + } + } + + return 0; +} + +/* Fold function call to builtin memcpy. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_memcpy (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, src, len; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* 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. */ + if (operand_equal_p (src, dest, 0)) + return omit_one_operand (TREE_TYPE (exp), dest, len); + + return 0; +} + +/* Fold function call to builtin mempcpy. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_mempcpy (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, src, len; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* 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)) + { + tree temp = convert (TREE_TYPE (dest), len); + temp = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len)); + return convert (TREE_TYPE (exp), temp); + } + + return 0; +} + +/* Fold function call to builtin memmove. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_memmove (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, src, len; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* 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. */ + if (operand_equal_p (src, dest, 0)) + return omit_one_operand (TREE_TYPE (exp), dest, len); + + return 0; +} + +/* Fold function call to builtin strcpy. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_strcpy (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, src; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + + /* If SRC and DEST are the same (and not volatile), return DEST. */ + if (operand_equal_p (src, dest, 0)) + return convert (TREE_TYPE (exp), dest); + + return 0; +} + +/* Fold function call to builtin strncpy. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_strncpy (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, src, len; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* If the LEN parameter is zero, return DEST. */ + if (integer_zerop (len)) + return omit_one_operand (TREE_TYPE (exp), dest, src); + + return 0; +} + +/* Fold function call to builtin memcmp. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_memcmp (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree arg1, arg2, len; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + arg1 = TREE_VALUE (arglist); + arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* If the LEN parameter is zero, return zero. */ + if (integer_zerop (len)) + { + tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2); + return omit_one_operand (TREE_TYPE (exp), temp, arg1); + } + + /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ + if (operand_equal_p (arg1, arg2, 0)) + return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len); + + return 0; +} + +/* Fold function call to builtin strcmp. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_strcmp (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree arg1, arg2; + const char *p1, *p2; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + + arg1 = TREE_VALUE (arglist); + arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + + /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ + if (operand_equal_p (arg1, arg2, 0)) + return convert (TREE_TYPE (exp), integer_zero_node); + + p1 = c_getstr (arg1); + p2 = c_getstr (arg2); + + if (p1 && p2) + { + tree temp; + const int i = strcmp (p1, p2); + if (i < 0) + temp = integer_minus_one_node; + else if (i > 0) + temp = integer_one_node; + else + temp = integer_zero_node; + return convert (TREE_TYPE (exp), temp); + } + + return 0; +} + +/* Fold function call to builtin strncmp. Return + NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_strncmp (tree exp) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree arg1, arg2, len; + const char *p1, *p2; + + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + arg1 = TREE_VALUE (arglist); + arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* If the LEN parameter is zero, return zero. */ + if (integer_zerop (len)) + { + tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2); + return omit_one_operand (TREE_TYPE (exp), temp, arg1); + } + + /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ + if (operand_equal_p (arg1, arg2, 0)) + return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len); + + p1 = c_getstr (arg1); + p2 = c_getstr (arg2); + + if (host_integerp (len, 1) && p1 && p2) + { + tree temp; + const int i = strncmp (p1, p2, tree_low_cst (len, 1)); + if (i < 0) + temp = integer_minus_one_node; + else if (i > 0) + temp = integer_one_node; + else + temp = integer_zero_node; + return convert (TREE_TYPE (exp), temp); + } + + return 0; +} + /* Used by constant folding to eliminate some builtin calls early. EXP is the CALL_EXPR of a call to a builtin function. */ tree -fold_builtin (exp) - tree exp; +fold_builtin (tree exp) { - tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); - enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) return 0; - switch (fcode) + switch (DECL_FUNCTION_CODE (fndecl)) { case BUILT_IN_CONSTANT_P: return fold_builtin_constant_p (arglist); @@ -4305,7 +6523,7 @@ fold_builtin (exp) case BUILT_IN_STRLEN: if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) { - tree len = c_strlen (TREE_VALUE (arglist)); + tree len = c_strlen (TREE_VALUE (arglist), 0); if (len) { /* Convert from the internal "sizetype" type to "size_t". */ @@ -4316,25 +6534,389 @@ fold_builtin (exp) } break; + case BUILT_IN_FABS: + case BUILT_IN_FABSF: + case BUILT_IN_FABSL: + if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + return fold (build1 (ABS_EXPR, type, TREE_VALUE (arglist))); + break; + + case BUILT_IN_CABS: + case BUILT_IN_CABSF: + case BUILT_IN_CABSL: + return fold_builtin_cabs (fndecl, arglist, type); + + case BUILT_IN_SQRT: + case BUILT_IN_SQRTF: + case BUILT_IN_SQRTL: + if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + { + enum built_in_function fcode; + tree arg = TREE_VALUE (arglist); + + /* Optimize sqrt of constant value. */ + if (TREE_CODE (arg) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg)) + { + REAL_VALUE_TYPE r, x; + + x = TREE_REAL_CST (arg); + if (real_sqrt (&r, TYPE_MODE (type), &x) + || (!flag_trapping_math && !flag_errno_math)) + return build_real (type, r); + } + + /* Optimize sqrt(exp(x)) = exp(x*0.5). */ + fcode = builtin_mathfn_code (arg); + if (flag_unsafe_math_optimizations + && (fcode == BUILT_IN_EXP + || fcode == BUILT_IN_EXPF + || fcode == BUILT_IN_EXPL)) + { + tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); + arg = fold (build (MULT_EXPR, type, + TREE_VALUE (TREE_OPERAND (arg, 1)), + build_real (type, dconsthalf))); + arglist = build_tree_list (NULL_TREE, arg); + return build_function_call_expr (expfn, arglist); + } + + /* Optimize sqrt(pow(x,y)) = pow(x,y*0.5). */ + if (flag_unsafe_math_optimizations + && (fcode == BUILT_IN_POW + || fcode == BUILT_IN_POWF + || fcode == BUILT_IN_POWL)) + { + 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 (build (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); + } + } + break; + + case BUILT_IN_SIN: + case BUILT_IN_SINF: + case BUILT_IN_SINL: + if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + { + tree arg = TREE_VALUE (arglist); + + /* Optimize sin(0.0) = 0.0. */ + if (real_zerop (arg)) + return arg; + } + break; + + case BUILT_IN_COS: + case BUILT_IN_COSF: + case BUILT_IN_COSL: + if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + { + tree arg = TREE_VALUE (arglist); + + /* Optimize cos(0.0) = 1.0. */ + if (real_zerop (arg)) + return build_real (type, dconst1); + + /* Optimize cos(-x) into cos(x). */ + if (TREE_CODE (arg) == NEGATE_EXPR) + { + tree arglist = build_tree_list (NULL_TREE, + TREE_OPERAND (arg, 0)); + return build_function_call_expr (fndecl, arglist); + } + } + break; + + case BUILT_IN_EXP: + case BUILT_IN_EXPF: + case BUILT_IN_EXPL: + return fold_builtin_exponent (exp, &dconste); + case BUILT_IN_EXP2: + case BUILT_IN_EXP2F: + case BUILT_IN_EXP2L: + return fold_builtin_exponent (exp, &dconst2); + case BUILT_IN_EXP10: + case BUILT_IN_EXP10F: + case BUILT_IN_EXP10L: + case BUILT_IN_POW10: + case BUILT_IN_POW10F: + case BUILT_IN_POW10L: + return fold_builtin_exponent (exp, &dconst10); + case BUILT_IN_LOG: + case BUILT_IN_LOGF: + case BUILT_IN_LOGL: + return fold_builtin_logarithm (exp, &dconste); + break; + case BUILT_IN_LOG2: + case BUILT_IN_LOG2F: + case BUILT_IN_LOG2L: + return fold_builtin_logarithm (exp, &dconst2); + break; + case BUILT_IN_LOG10: + case BUILT_IN_LOG10F: + case BUILT_IN_LOG10L: + return fold_builtin_logarithm (exp, &dconst10); + break; + + case BUILT_IN_TAN: + case BUILT_IN_TANF: + case BUILT_IN_TANL: + if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + { + enum built_in_function fcode; + tree arg = TREE_VALUE (arglist); + + /* Optimize tan(0.0) = 0.0. */ + if (real_zerop (arg)) + return arg; + + /* Optimize tan(atan(x)) = x. */ + fcode = builtin_mathfn_code (arg); + if (flag_unsafe_math_optimizations + && (fcode == BUILT_IN_ATAN + || fcode == BUILT_IN_ATANF + || fcode == BUILT_IN_ATANL)) + return TREE_VALUE (TREE_OPERAND (arg, 1)); + } + break; + + case BUILT_IN_ATAN: + case BUILT_IN_ATANF: + case BUILT_IN_ATANL: + if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + { + tree arg = TREE_VALUE (arglist); + + /* Optimize atan(0.0) = 0.0. */ + if (real_zerop (arg)) + return arg; + + /* Optimize atan(1.0) = pi/4. */ + if (real_onep (arg)) + { + REAL_VALUE_TYPE cst; + + real_convert (&cst, TYPE_MODE (type), &dconstpi); + cst.exp -= 2; + return build_real (type, cst); + } + } + break; + + case BUILT_IN_POW: + case BUILT_IN_POWF: + case BUILT_IN_POWL: + if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + { + enum built_in_function fcode; + tree arg0 = TREE_VALUE (arglist); + tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + + /* Optimize pow(1.0,y) = 1.0. */ + if (real_onep (arg0)) + return omit_one_operand (type, build_real (type, dconst1), arg1); + + if (TREE_CODE (arg1) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg1)) + { + REAL_VALUE_TYPE c; + c = TREE_REAL_CST (arg1); + + /* Optimize pow(x,0.0) = 1.0. */ + if (REAL_VALUES_EQUAL (c, dconst0)) + return omit_one_operand (type, build_real (type, dconst1), + arg0); + + /* Optimize pow(x,1.0) = x. */ + if (REAL_VALUES_EQUAL (c, dconst1)) + return arg0; + + /* Optimize pow(x,-1.0) = 1.0/x. */ + if (REAL_VALUES_EQUAL (c, dconstm1)) + return fold (build (RDIV_EXPR, type, + build_real (type, dconst1), + arg0)); + + /* Optimize pow(x,0.5) = sqrt(x). */ + if (flag_unsafe_math_optimizations + && REAL_VALUES_EQUAL (c, dconsthalf)) + { + tree sqrtfn; + + fcode = DECL_FUNCTION_CODE (fndecl); + if (fcode == BUILT_IN_POW) + sqrtfn = implicit_built_in_decls[BUILT_IN_SQRT]; + else if (fcode == BUILT_IN_POWF) + sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTF]; + else if (fcode == BUILT_IN_POWL) + sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTL]; + else + sqrtfn = NULL_TREE; + + if (sqrtfn != NULL_TREE) + { + tree arglist = build_tree_list (NULL_TREE, arg0); + return build_function_call_expr (sqrtfn, arglist); + } + } + + /* Attempt to evaluate pow at compile-time. */ + if (TREE_CODE (arg0) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg0)) + { + 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)) + { + REAL_VALUE_TYPE x; + bool inexact; + + x = TREE_REAL_CST (arg0); + inexact = real_powi (&x, TYPE_MODE (type), &x, n); + if (flag_unsafe_math_optimizations || !inexact) + return build_real (type, x); + } + } + } + + /* Optimize pow(exp(x),y) = exp(x*y). */ + fcode = builtin_mathfn_code (arg0); + if (flag_unsafe_math_optimizations + && (fcode == BUILT_IN_EXP + || fcode == BUILT_IN_EXPF + || fcode == BUILT_IN_EXPL)) + { + tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0); + tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1)); + arg = fold (build (MULT_EXPR, type, arg, arg1)); + arglist = build_tree_list (NULL_TREE, arg); + return build_function_call_expr (expfn, arglist); + } + + /* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */ + if (flag_unsafe_math_optimizations + && (fcode == BUILT_IN_SQRT + || fcode == BUILT_IN_SQRTF + || fcode == BUILT_IN_SQRTL)) + { + tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1)); + tree narg1 = fold (build (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(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)) + { + tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1)); + tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1))); + tree narg1 = fold (build (MULT_EXPR, type, arg01, arg1)); + arglist = tree_cons (NULL_TREE, arg00, + build_tree_list (NULL_TREE, narg1)); + return build_function_call_expr (fndecl, arglist); + } + } + break; + case BUILT_IN_INF: case BUILT_IN_INFF: case BUILT_IN_INFL: - return fold_builtin_inf (TREE_TYPE (TREE_TYPE (fndecl)), true); + return fold_builtin_inf (type, true); case BUILT_IN_HUGE_VAL: case BUILT_IN_HUGE_VALF: case BUILT_IN_HUGE_VALL: - return fold_builtin_inf (TREE_TYPE (TREE_TYPE (fndecl)), false); + return fold_builtin_inf (type, false); case BUILT_IN_NAN: case BUILT_IN_NANF: case BUILT_IN_NANL: - return fold_builtin_nan (arglist, TREE_TYPE (TREE_TYPE (fndecl)), true); + return fold_builtin_nan (arglist, type, true); case BUILT_IN_NANS: case BUILT_IN_NANSF: case BUILT_IN_NANSL: - return fold_builtin_nan (arglist, TREE_TYPE (TREE_TYPE (fndecl)), false); + return fold_builtin_nan (arglist, type, false); + + case BUILT_IN_FLOOR: + case BUILT_IN_FLOORF: + case BUILT_IN_FLOORL: + return fold_builtin_floor (exp); + + case BUILT_IN_CEIL: + case BUILT_IN_CEILF: + case BUILT_IN_CEILL: + return fold_builtin_ceil (exp); + + case BUILT_IN_TRUNC: + case BUILT_IN_TRUNCF: + case BUILT_IN_TRUNCL: + return fold_builtin_trunc (exp); + + case BUILT_IN_ROUND: + case BUILT_IN_ROUNDF: + case BUILT_IN_ROUNDL: + case BUILT_IN_NEARBYINT: + case BUILT_IN_NEARBYINTF: + case BUILT_IN_NEARBYINTL: + return fold_trunc_transparent_mathfn (exp); + + case BUILT_IN_FFS: + case BUILT_IN_FFSL: + case BUILT_IN_FFSLL: + case BUILT_IN_CLZ: + case BUILT_IN_CLZL: + case BUILT_IN_CLZLL: + case BUILT_IN_CTZ: + case BUILT_IN_CTZL: + case BUILT_IN_CTZLL: + case BUILT_IN_POPCOUNT: + case BUILT_IN_POPCOUNTL: + case BUILT_IN_POPCOUNTLL: + case BUILT_IN_PARITY: + case BUILT_IN_PARITYL: + case BUILT_IN_PARITYLL: + return fold_builtin_bitop (exp); + + case BUILT_IN_MEMCPY: + return fold_builtin_memcpy (exp); + + case BUILT_IN_MEMPCPY: + return fold_builtin_mempcpy (exp); + + case BUILT_IN_MEMMOVE: + return fold_builtin_memmove (exp); + + case BUILT_IN_STRCPY: + return fold_builtin_strcpy (exp); + + case BUILT_IN_STRNCPY: + return fold_builtin_strncpy (exp); + + case BUILT_IN_MEMCMP: + return fold_builtin_memcmp (exp); + + case BUILT_IN_STRCMP: + return fold_builtin_strcmp (exp); + + case BUILT_IN_STRNCMP: + return fold_builtin_strncmp (exp); default: break; @@ -4343,16 +6925,16 @@ fold_builtin (exp) return 0; } -static tree -build_function_call_expr (fn, arglist) - tree fn, arglist; +/* Conveniently construct a function call expression. */ + +tree +build_function_call_expr (tree fn, tree arglist) { tree call_expr; call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), call_expr, arglist); - TREE_SIDE_EFFECTS (call_expr) = 1; return fold (call_expr); } @@ -4362,13 +6944,13 @@ build_function_call_expr (fn, arglist) ellipses, otherwise the last specifier must be a VOID_TYPE. */ static int -validate_arglist VPARAMS ((tree arglist, ...)) +validate_arglist (tree arglist, ...) { enum tree_code code; int res = 0; + va_list ap; - VA_OPEN (ap, arglist); - VA_FIXEDARG (ap, tree, arglist); + va_start (ap, arglist); do { @@ -4400,27 +6982,58 @@ validate_arglist VPARAMS ((tree arglist, ...)) /* We need gotos here since we can only have one VA_CLOSE in a function. */ end: ; - VA_CLOSE (ap); + va_end (ap); return res; } -/* Default version of target-specific builtin setup that does nothing. */ +/* Default target-specific builtin expander that does nothing. */ + +rtx +default_expand_builtin (tree exp ATTRIBUTE_UNUSED, + rtx target ATTRIBUTE_UNUSED, + rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED) +{ + return NULL_RTX; +} + +/* Instantiate all remaining CONSTANT_P_RTX nodes. */ void -default_init_builtins () +purge_builtin_constant_p (void) { + rtx insn, set, arg, new, note; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (INSN_P (insn) + && (set = single_set (insn)) != NULL_RTX + && (GET_CODE (arg = SET_SRC (set)) == CONSTANT_P_RTX + || (GET_CODE (arg) == SUBREG + && (GET_CODE (arg = SUBREG_REG (arg)) + == CONSTANT_P_RTX)))) + { + arg = XEXP (arg, 0); + new = CONSTANT_P (arg) ? const1_rtx : const0_rtx; + validate_change (insn, &SET_SRC (set), new, 0); + + /* Remove the REG_EQUAL note from the insn. */ + if ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0) + remove_note (insn, note); + } } -/* Default target-specific builtin expander that does nothing. */ +/* Returns true is EXP represents data that would potentially reside + in a readonly section. */ -rtx -default_expand_builtin (exp, target, subtarget, mode, ignore) - tree exp ATTRIBUTE_UNUSED; - rtx target ATTRIBUTE_UNUSED; - rtx subtarget ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; - int ignore ATTRIBUTE_UNUSED; +static bool +readonly_data_expr (tree exp) { - return NULL_RTX; + STRIP_NOPS (exp); + + if (TREE_CODE (exp) == ADDR_EXPR) + return decl_readonly_section (TREE_OPERAND (exp, 0), 0); + else + return false; } |