X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fbuiltins.c;h=65028cf51d96fa08a354e76f24db9ade83209e1c;hb=f0ed0e8a52bc2aada0e3f4b71c157aa6442cc43c;hp=33fca2ab32eaa56ebc6674b86c676863811b3ecd;hpb=f474cd93a740e70d615b025d4d43a46b88fd004d;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/builtins.c b/gcc/builtins.c index 33fca2ab32e..65028cf51d9 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -27,6 +27,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "real.h" #include "rtl.h" #include "tree.h" +#include "tree-gimple.h" #include "flags.h" #include "regs.h" #include "hard-reg-set.h" @@ -44,6 +45,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "tm_p.h" #include "target.h" #include "langhooks.h" +#include "basic-block.h" #define CALLED_AS_BUILT_IN(NODE) \ (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10)) @@ -68,11 +70,10 @@ const char *const built_in_names[(int) END_BUILTINS] = tree built_in_decls[(int) END_BUILTINS]; /* 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. */ + 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 *); @@ -96,7 +97,6 @@ static void expand_errno_check (tree, rtx); static rtx expand_builtin_mathfn (tree, rtx, rtx); static rtx expand_builtin_mathfn_2 (tree, rtx, rtx); static rtx expand_builtin_mathfn_3 (tree, rtx, rtx); -static rtx expand_builtin_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); @@ -156,12 +156,32 @@ 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_strchr (tree, bool); static tree fold_builtin_memcmp (tree); static tree fold_builtin_strcmp (tree); static tree fold_builtin_strncmp (tree); static tree fold_builtin_signbit (tree); +static tree fold_builtin_copysign (tree, tree); +static tree fold_builtin_isascii (tree); +static tree fold_builtin_toascii (tree); +static tree fold_builtin_isdigit (tree); +static tree fold_builtin_fabs (tree, tree); +static tree fold_builtin_abs (tree, tree); +static tree fold_builtin_unordered_cmp (tree, enum tree_code, enum tree_code); +static tree fold_builtin_1 (tree, bool); + +static tree simplify_builtin_strpbrk (tree); +static tree simplify_builtin_strstr (tree); +static tree simplify_builtin_strchr (tree); +static tree simplify_builtin_strrchr (tree); +static tree simplify_builtin_strcat (tree); +static tree simplify_builtin_strncat (tree); +static tree simplify_builtin_strspn (tree); +static tree simplify_builtin_strcspn (tree); +static void simplify_builtin_next_arg (tree); +static void simplify_builtin_va_start (tree); +static tree simplify_builtin_sprintf (tree, int); + /* Return the alignment in bits of EXP, a pointer valued expression. But don't return more than MAX_ALIGN no matter what. @@ -247,7 +267,7 @@ get_pointer_alignment (tree exp, unsigned int max_align) Unfortunately, string_constant can't access the values of const char arrays with initializers, so neither can we do so here. */ -static tree +tree c_strlen (tree src, int only_value) { tree offset_node; @@ -407,6 +427,21 @@ target_char_cast (tree cst, char *p) return 0; } +/* Similar to save_expr, but assumes that arbitrary code is not executed + in between the multiple evaluations. In particular, we assume that a + non-addressable local variable will not be modified. */ + +static tree +builtin_save_expr (tree exp) +{ + if (TREE_ADDRESSABLE (exp) == 0 + && (TREE_CODE (exp) == PARM_DECL + || (TREE_CODE (exp) == VAR_DECL && !TREE_STATIC (exp)))) + return exp; + + return save_expr (exp); +} + /* Given TEM, a pointer to a stack frame, follow the dynamic chain COUNT times to get the address of either a higher stack frame, or a return address located within it (depending on FNDECL_CODE). */ @@ -486,8 +521,6 @@ expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) buf_addr = force_reg (Pmode, force_operand (buf_addr, NULL_RTX)); - emit_queue (); - /* We store the frame pointer and the address of receiver_label in the buffer and use the rest of it for the stack save area, which is machine-dependent. */ @@ -538,8 +571,7 @@ expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED) emit_insn (gen_rtx_CLOBBER (VOIDmode, static_chain_rtx)); /* Now put in the code to restore the frame pointer, and argument - pointer, if needed. The code below is from expand_end_bindings - in stmt.c; see detailed documentation there. */ + pointer, if needed. */ #ifdef HAVE_nonlocal_goto if (! HAVE_nonlocal_goto) #endif @@ -606,7 +638,7 @@ expand_builtin_setjmp (tree arglist, rtx target) if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) return NULL_RTX; - if (target == 0 || GET_CODE (target) != REG + if (target == 0 || !REG_P (target) || REGNO (target) < FIRST_PSEUDO_REGISTER) target = gen_reg_rtx (TYPE_MODE (integer_type_node)); @@ -728,15 +760,90 @@ expand_builtin_longjmp (rtx buf_addr, rtx value) { if (insn == last) abort (); - if (GET_CODE (insn) == JUMP_INSN) + if (JUMP_P (insn)) { REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO, const0_rtx, REG_NOTES (insn)); break; } - else if (GET_CODE (insn) == CALL_INSN) + else if (CALL_P (insn)) + break; + } +} + +/* Expand a call to __builtin_nonlocal_goto. We're passed the target label + and the address of the save area. */ + +static rtx +expand_builtin_nonlocal_goto (tree arglist) +{ + tree t_label, t_save_area; + rtx r_label, r_save_area, r_fp, r_sp, insn; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return NULL_RTX; + + t_label = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + t_save_area = TREE_VALUE (arglist); + + r_label = expand_expr (t_label, NULL_RTX, VOIDmode, 0); + r_label = convert_memory_address (Pmode, r_label); + r_save_area = expand_expr (t_save_area, NULL_RTX, VOIDmode, 0); + r_save_area = convert_memory_address (Pmode, r_save_area); + r_fp = gen_rtx_MEM (Pmode, r_save_area); + r_sp = gen_rtx_MEM (STACK_SAVEAREA_MODE (SAVE_NONLOCAL), + plus_constant (r_save_area, GET_MODE_SIZE (Pmode))); + + current_function_has_nonlocal_goto = 1; + +#if HAVE_nonlocal_goto + /* ??? We no longer need to pass the static chain value, afaik. */ + if (HAVE_nonlocal_goto) + emit_insn (gen_nonlocal_goto (const0_rtx, r_label, r_sp, r_fp)); + else +#endif + { + r_label = copy_to_reg (r_label); + + 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))); + + /* Restore frame pointer for containing function. + This sets the actual hard register used for the frame pointer + to the location of the function's incoming static chain info. + The non-local goto handler will then adjust it to contain the + proper value and reload the argument pointer, if needed. */ + emit_move_insn (hard_frame_pointer_rtx, r_fp); + emit_stack_restore (SAVE_NONLOCAL, r_sp, NULL_RTX); + + /* USE of hard_frame_pointer_rtx added for consistency; + not clear if really needed. */ + emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); + emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx)); + emit_indirect_jump (r_label); + } + + /* Search backwards to the jump insn and mark it as a + non-local goto. */ + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) + { + if (JUMP_P (insn)) + { + REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO, + const0_rtx, REG_NOTES (insn)); + break; + } + else if (CALL_P (insn)) break; } + + return const0_rtx; } /* __builtin_update_setjmp_buf is passed a pointer to an array of five words @@ -796,12 +903,12 @@ expand_builtin_prefetch (tree arglist) if (TREE_CHAIN (TREE_CHAIN (arglist))) arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); else - arg2 = build_int_2 (3, 0); + arg2 = build_int_cst (NULL_TREE, 3, 0); } else { arg1 = integer_zero_node; - arg2 = build_int_2 (3, 0); + arg2 = build_int_cst (NULL_TREE, 3, 0); } /* Argument 0 is an address. */ @@ -848,17 +955,16 @@ expand_builtin_prefetch (tree arglist) } emit_insn (gen_prefetch (op0, op1, op2)); } - else #endif - op0 = protect_from_queue (op0, 0); + /* Don't do anything with direct references to volatile memory, but generate code to handle other side effects. */ - if (GET_CODE (op0) != MEM && side_effects_p (op0)) + if (!MEM_P (op0) && side_effects_p (op0)) emit_insn (op0); } /* Get a MEM rtx for expression EXP which is the address of an operand - to be used to be used in a string instruction (cmpstrsi, movstrsi, ..). */ + to be used to be used in a string instruction (cmpstrsi, movmemsi, ..). */ static rtx get_memory_rtx (tree exp) @@ -1127,7 +1233,7 @@ expand_builtin_apply_args (void) chain current, so the code is placed at the start of the function. */ push_topmost_sequence (); - emit_insn_before (seq, NEXT_INSN (get_insns ())); + emit_insn_before (seq, NEXT_INSN (entry_of_function ())); pop_topmost_sequence (); return temp; } @@ -1159,9 +1265,6 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize) incoming_args, 0, OPTAB_LIB_WIDEN); #endif - /* Perform postincrements before actually calling the function. */ - emit_queue (); - /* Push a new argument block and copy the arguments. Do not allow the (potential) memcpy call below to interfere with our stack manipulations. */ @@ -1224,13 +1327,13 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize) rtx value = gen_reg_rtx (Pmode); emit_move_insn (value, adjust_address (arguments, Pmode, size)); emit_move_insn (struct_value, value); - if (GET_CODE (struct_value) == REG) + if (REG_P (struct_value)) use_reg (&call_fusage, struct_value); size += GET_MODE_SIZE (Pmode); } /* All arguments and registers used for the call are set up by now! */ - function = prepare_call_address (function, NULL_TREE, &call_fusage, 0, 0); + function = prepare_call_address (function, NULL, &call_fusage, 0, 0); /* Ensure address is valid. SYMBOL_REF is already valid, so no need, and we don't want to load it into a register as an optimization, @@ -1384,32 +1487,6 @@ expand_builtin_classify_type (tree arglist) return GEN_INT (no_type_class); } -/* Expand expression EXP, which is a call to __builtin_constant_p. */ - -static rtx -expand_builtin_constant_p (tree arglist, enum machine_mode target_mode) -{ - rtx tmp; - - if (arglist == 0) - return const0_rtx; - 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. - 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 (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 @@ -1682,7 +1759,7 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) /* 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); + narg = builtin_save_expr (arg); if (narg != arg) { arglist = build_tree_list (NULL_TREE, arg); @@ -1691,7 +1768,6 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) op0 = expand_expr (arg, subtarget, VOIDmode, 0); - emit_queue (); start_sequence (); /* Compute into TARGET. @@ -1745,7 +1821,7 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0); /* Check operand is a register with expected mode. */ if (operand - && GET_CODE (operand) == REG + && REG_P (operand) && GET_MODE (operand) == mode) { /* Replace the REG_EQUAL note with a SQRT rtx. */ @@ -1821,8 +1897,8 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) if (! flag_errno_math || ! HONOR_NANS (mode)) errno_set = false; - /* Alway stabilize the argument list. */ - narg = save_expr (arg1); + /* Always stabilize the argument list. */ + narg = builtin_save_expr (arg1); if (narg != arg1) { temp = build_tree_list (NULL_TREE, narg); @@ -1831,7 +1907,7 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) else temp = TREE_CHAIN (arglist); - narg = save_expr (arg0); + narg = builtin_save_expr (arg0); if (narg != arg0) { arglist = tree_cons (NULL_TREE, narg, temp); @@ -1846,7 +1922,6 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) op0 = expand_expr (arg0, subtarget, VOIDmode, 0); op1 = expand_expr (arg1, 0, VOIDmode, 0); - emit_queue (); start_sequence (); /* Compute into TARGET. @@ -1917,7 +1992,7 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) errno_set = false; /* Check if sincos insn is available, otherwise fallback - to sin or cos insn. */ + to sin or cos insn. */ if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) { switch (DECL_FUNCTION_CODE (fndecl)) { @@ -1951,7 +2026,6 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) op0 = expand_expr (arg, subtarget, VOIDmode, 0); - emit_queue (); start_sequence (); /* Compute into TARGET. @@ -1963,7 +2037,7 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) case BUILT_IN_SIN: case BUILT_IN_SINF: case BUILT_IN_SINL: - if (!expand_twoval_unop (builtin_optab, op0, 0, target, 0)) + if (!expand_twoval_unop (builtin_optab, op0, 0, target, 0)) abort(); break; case BUILT_IN_COS: @@ -2318,7 +2392,7 @@ expand_builtin_strlen (tree arglist, rtx target, /* Make a place to write the result of the instruction. */ result = target; if (! (result != 0 - && GET_CODE (result) == REG + && REG_P (result) && GET_MODE (result) == insn_mode && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); @@ -2382,7 +2456,7 @@ expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode) else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); - tree fn; + tree fn, tmp; const char *p1, *p2; p2 = c_getstr (s2); @@ -2398,10 +2472,10 @@ expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode) return const0_rtx; /* Return an offset into the constant string argument. */ - return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), s1, - fold_convert (TREE_TYPE (s1), - ssize_int (r - p1)))), - target, mode, EXPAND_NORMAL); + tmp = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), s1, + fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + return expand_expr (tmp, target, mode, EXPAND_NORMAL); } if (p2[0] == '\0') @@ -2416,8 +2490,8 @@ expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode) /* New argument list transforming strstr(s1, s2) to strchr(s1, s2[0]). */ - arglist = - build_tree_list (NULL_TREE, build_int_2 (p2[0], 0)); + arglist = build_tree_list (NULL_TREE, + build_int_cst (NULL_TREE, p2[0], 0)); arglist = tree_cons (NULL_TREE, s1, arglist); return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); @@ -2446,6 +2520,7 @@ expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode) { char c; const char *r; + tree tmp; if (target_char_cast (s2, &c)) return 0; @@ -2456,10 +2531,10 @@ expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode) return const0_rtx; /* Return an offset into the constant string argument. */ - return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), s1, - fold_convert (TREE_TYPE (s1), - ssize_int (r - p1)))), - target, mode, EXPAND_NORMAL); + tmp = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), s1, + fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + return expand_expr (tmp, target, mode, EXPAND_NORMAL); } /* FIXME: Should use here strchrM optab so that ports can optimize @@ -2480,7 +2555,7 @@ expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode) else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); - tree fn; + tree fn, tmp; const char *p1; if (TREE_CODE (s2) != INTEGER_CST) @@ -2501,10 +2576,10 @@ expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode) return const0_rtx; /* Return an offset into the constant string argument. */ - return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), s1, - fold_convert (TREE_TYPE (s1), - ssize_int (r - p1)))), - target, mode, EXPAND_NORMAL); + tmp = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), s1, + fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + return expand_expr (tmp, target, mode, EXPAND_NORMAL); } if (! integer_zerop (s2)) @@ -2532,7 +2607,7 @@ expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode) else { tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); - tree fn; + tree fn, tmp; const char *p1, *p2; p2 = c_getstr (s2); @@ -2548,10 +2623,10 @@ expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode) return const0_rtx; /* Return an offset into the constant string argument. */ - return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1), s1, - fold_convert (TREE_TYPE (s1), - ssize_int (r - p1)))), - target, mode, EXPAND_NORMAL); + tmp = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), s1, + fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + return expand_expr (tmp, target, mode, EXPAND_NORMAL); } if (p2[0] == '\0') @@ -2572,8 +2647,8 @@ expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode) /* New argument list transforming strpbrk(s1, s2) to strchr(s1, s2[0]). */ - arglist = - build_tree_list (NULL_TREE, build_int_2 (p2[0], 0)); + arglist = build_tree_list (NULL_TREE, + build_int_cst (NULL_TREE, p2[0], 0)); arglist = tree_cons (NULL_TREE, s1, arglist); return expand_expr (build_function_call_expr (fn, arglist), target, mode, EXPAND_NORMAL); @@ -2736,10 +2811,10 @@ expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode, } if (endp == 2) - len = fold (build (MINUS_EXPR, TREE_TYPE (len), dest, - integer_one_node)); + len = fold (build2 (MINUS_EXPR, TREE_TYPE (len), len, + integer_one_node)); len = fold_convert (TREE_TYPE (dest), len); - expr = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len)); + expr = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len)); return expand_expr (expr, target, mode, EXPAND_NORMAL); } @@ -2889,6 +2964,72 @@ expand_builtin_bcopy (tree arglist) return expand_builtin_memmove (newarglist, const0_rtx, VOIDmode); } +#ifndef HAVE_movstr +# define HAVE_movstr 0 +# define CODE_FOR_movstr CODE_FOR_nothing +#endif + +/* Expand into a movstr instruction, if one is available. Return 0 if + we failed, the caller should emit a normal call, otherwise try to + get the result in TARGET, if 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_movstr (tree dest, tree src, rtx target, int endp) +{ + rtx end; + rtx dest_mem; + rtx src_mem; + rtx insn; + const struct insn_data * data; + + if (!HAVE_movstr) + return 0; + + dest_mem = get_memory_rtx (dest); + src_mem = get_memory_rtx (src); + if (!endp) + { + target = force_reg (Pmode, XEXP (dest_mem, 0)); + dest_mem = replace_equiv_address (dest_mem, target); + end = gen_reg_rtx (Pmode); + } + else + { + if (target == 0 || target == const0_rtx) + { + end = gen_reg_rtx (Pmode); + if (target == 0) + target = end; + } + else + end = target; + } + + data = insn_data + CODE_FOR_movstr; + + if (data->operand[0].mode != VOIDmode) + end = gen_lowpart (data->operand[0].mode, end); + + insn = data->genfun (end, dest_mem, src_mem); + + if (insn == 0) + abort (); + + emit_insn (insn); + + /* movstr is supposed to set end to the address of the NUL + terminator. If the caller requested a mempcpy-like return value, + adjust it. */ + if (endp == 1 && target != const0_rtx) + emit_move_insn (target, plus_constant (gen_lowpart (GET_MODE (target), + end), 1)); + + return target; +} + /* 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 @@ -2909,12 +3050,14 @@ expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode) 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; - len = c_strlen (src, 1); if (len == 0 || TREE_SIDE_EFFECTS (len)) + return expand_movstr (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist)), + target, /*endp=*/0); + + fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + if (!fn) return 0; len = size_binop (PLUS_EXPR, len, ssize_int (1)); @@ -2933,22 +3076,24 @@ expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode) static rtx expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode) { + /* 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); + } + 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); - } + tree dst, src, len, lenp1; + tree narglist; + rtx ret; /* Ensure we get an actual string whose length can be evaluated at compile-time, not an expression containing a string. This is @@ -2956,14 +3101,54 @@ expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode) when used to produce the return value. */ src = TREE_VALUE (TREE_CHAIN (arglist)); if (! c_getstr (src) || ! (len = c_strlen (src, 0))) - return 0; + return expand_movstr (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist)), + target, /*endp=*/2); 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); + lenp1 = size_binop (PLUS_EXPR, len, ssize_int (1)); + narglist = build_tree_list (NULL_TREE, lenp1); + narglist = tree_cons (NULL_TREE, src, narglist); + narglist = tree_cons (NULL_TREE, dst, narglist); + ret = expand_builtin_mempcpy (narglist, target, mode, /*endp=*/2); + + if (ret) + return ret; + + if (TREE_CODE (len) == INTEGER_CST) + { + rtx len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0); + + if (GET_CODE (len_rtx) == CONST_INT) + { + ret = expand_builtin_strcpy (arglist, target, mode); + + if (ret) + { + if (! target) + { + if (mode != VOIDmode) + target = gen_reg_rtx (mode); + else + target = gen_reg_rtx (GET_MODE (ret)); + } + if (GET_MODE (target) != GET_MODE (ret)) + ret = gen_lowpart (GET_MODE (target), ret); + + ret = emit_move_insn (target, + plus_constant (ret, + INTVAL (len_rtx))); + if (! ret) + abort (); + + return target; + } + } + } + + return expand_movstr (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist)), + target, /*endp=*/2); } } @@ -3293,7 +3478,7 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, arg2)))); - tree result = fold (build (MINUS_EXPR, integer_type_node, ind1, ind2)); + tree result = fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2)); return expand_expr (result, target, mode, EXPAND_NORMAL); } @@ -3328,7 +3513,7 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target, /* Make a place to write the result of the instruction. */ result = target; if (! (result != 0 - && GET_CODE (result) == REG && GET_MODE (result) == insn_mode + && REG_P (result) && GET_MODE (result) == insn_mode && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); @@ -3421,7 +3606,7 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode) fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, arg2)))); - tree result = fold (build (MINUS_EXPR, integer_type_node, ind1, ind2)); + tree result = fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2)); return expand_expr (result, target, mode, EXPAND_NORMAL); } @@ -3483,13 +3668,13 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode) /* Make a place to write the result of the instruction. */ result = target; if (! (result != 0 - && GET_CODE (result) == REG && GET_MODE (result) == insn_mode + && REG_P (result) && GET_MODE (result) == insn_mode && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); /* Stabilize the arguments in case gen_cmpstrsi fails. */ - arg1 = save_expr (arg1); - arg2 = save_expr (arg2); + arg1 = builtin_save_expr (arg1); + arg2 = builtin_save_expr (arg2); arg1_rtx = get_memory_rtx (arg1); arg2_rtx = get_memory_rtx (arg2); @@ -3586,7 +3771,7 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) fold (build1 (CONVERT_EXPR, integer_type_node, build1 (INDIRECT_REF, cst_uchar_node, fold_convert (cst_uchar_ptr_node, arg2)))); - tree result = fold (build (MINUS_EXPR, integer_type_node, ind1, ind2)); + tree result = fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2)); return expand_expr (result, target, mode, EXPAND_NORMAL); } @@ -3645,7 +3830,8 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) return 0; /* The actual new length parameter is MIN(len,arg3). */ - len = fold (build (MIN_EXPR, TREE_TYPE (len), len, arg3)); + len = fold (build2 (MIN_EXPR, TREE_TYPE (len), len, + fold_convert (TREE_TYPE (len), arg3))); /* If we don't have POINTER_TYPE, call the function. */ if (arg1_align == 0 || arg2_align == 0) @@ -3654,14 +3840,14 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode) /* Make a place to write the result of the instruction. */ result = target; if (! (result != 0 - && GET_CODE (result) == REG && GET_MODE (result) == insn_mode + && REG_P (result) && GET_MODE (result) == insn_mode && REGNO (result) >= FIRST_PSEUDO_REGISTER)) result = gen_reg_rtx (insn_mode); /* Stabilize the arguments in case gen_cmpstrsi fails. */ - arg1 = save_expr (arg1); - arg2 = save_expr (arg2); - len = save_expr (len); + arg1 = builtin_save_expr (arg1); + arg2 = builtin_save_expr (arg2); + len = builtin_save_expr (len); arg1_rtx = get_memory_rtx (arg1); arg2_rtx = get_memory_rtx (arg2); @@ -3731,7 +3917,7 @@ expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode) arglist = tree_cons (NULL_TREE, src, arglist); /* We're going to use dst more than once. */ - dst = save_expr (dst); + dst = builtin_save_expr (dst); /* Create strlen (dst). */ newdst = @@ -3739,7 +3925,7 @@ expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode) build_tree_list (NULL_TREE, dst))); /* Create (dst + strlen (dst)). */ - newdst = fold (build (PLUS_EXPR, TREE_TYPE (dst), dst, newdst)); + newdst = fold (build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst)); /* Prepend the new dst argument. */ arglist = tree_cons (NULL_TREE, newdst, arglist); @@ -3923,7 +4109,7 @@ expand_builtin_saveregs (void) is inside a start_sequence, make the outer-level insn chain current, so the code is placed at the start of the function. */ push_topmost_sequence (); - emit_insn_after (seq, get_insns ()); + emit_insn_after (seq, entry_of_function ()); pop_topmost_sequence (); return val; @@ -4023,10 +4209,7 @@ stabilize_va_list (tree valist, int needs_lvalue) if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE) { tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node)); - tree p2 = build_pointer_type (va_list_type_node); - - valist = build1 (ADDR_EXPR, p2, valist); - valist = fold_convert (p1, valist); + valist = build_fold_addr_expr_with_type (valist, p1); } } else @@ -4045,8 +4228,7 @@ stabilize_va_list (tree valist, int needs_lvalue) if (TREE_SIDE_EFFECTS (valist)) valist = save_expr (valist); - valist = fold (build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), - valist)); + valist = build_fold_indirect_ref (valist); } return valist; @@ -4068,8 +4250,8 @@ std_expand_builtin_va_start (tree valist, rtx nextarg) { tree t; - t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, - make_tree (ptr_type_node, nextarg)); + t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, + make_tree (ptr_type_node, nextarg)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); @@ -4103,96 +4285,106 @@ expand_builtin_va_start (tree arglist) /* The "standard" implementation of va_arg: read the value from the current (padded) address and increment by the (padded) size. */ -rtx -std_expand_builtin_va_arg (tree valist, tree type) +tree +std_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) { - tree addr_tree, t, type_size = NULL; - tree align, alignm1; - tree rounded_size; - rtx addr; - HOST_WIDE_INT boundary; + tree addr, t, type_size, rounded_size, valist_tmp; + unsigned HOST_WIDE_INT align, boundary; + bool indirect; + +#ifdef ARGS_GROW_DOWNWARD + /* All of the alignment and movement below is for args-grow-up machines. + As of 2004, there are only 3 ARGS_GROW_DOWNWARD targets, and they all + implement their own specialized gimplify_va_arg_expr routines. */ + abort (); +#endif - /* 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); + indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false); + if (indirect) + type = build_pointer_type (type); + + align = PARM_BOUNDARY / BITS_PER_UNIT; + boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type) / BITS_PER_UNIT; + + /* Hoist the valist value into a temporary for the moment. */ + valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL); /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually requires greater alignment, we must perform dynamic alignment. */ - - if (boundary > PARM_BOUNDARY) + if (boundary > align) { - 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); + t = fold_convert (TREE_TYPE (valist), size_int (boundary - 1)); + t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, + build2 (PLUS_EXPR, TREE_TYPE (valist), valist_tmp, t)); + gimplify_and_add (t, pre_p); + + t = fold_convert (TREE_TYPE (valist), size_int (-boundary)); + t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, + build2 (BIT_AND_EXPR, TREE_TYPE (valist), valist_tmp, t)); + gimplify_and_add (t, pre_p); } - if (type == error_mark_node - || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL - || TREE_OVERFLOW (type_size)) - rounded_size = size_zero_node; - else - rounded_size = fold (build (MULT_EXPR, sizetype, - fold (build (TRUNC_DIV_EXPR, sizetype, - fold (build (PLUS_EXPR, sizetype, - type_size, alignm1)), - align)), - align)); + + /* Compute the rounded size of the type. */ + type_size = size_in_bytes (type); + rounded_size = round_up (type_size, align); + + /* Reduce rounded_size so it's sharable with the postqueue. */ + gimplify_expr (&rounded_size, pre_p, post_p, is_gimple_val, fb_rvalue); /* Get AP. */ - addr_tree = valist; - if (PAD_VARARGS_DOWN && ! integer_zerop (rounded_size)) + addr = valist_tmp; + if (PAD_VARARGS_DOWN && !integer_zerop (rounded_size)) { /* Small args are padded downward. */ - addr_tree = fold (build (PLUS_EXPR, TREE_TYPE (addr_tree), addr_tree, - fold (build (COND_EXPR, sizetype, - fold (build (GT_EXPR, sizetype, - rounded_size, - align)), - size_zero_node, - fold (build (MINUS_EXPR, sizetype, - rounded_size, - type_size)))))); + t = fold (build2 (GT_EXPR, sizetype, rounded_size, size_int (align))); + t = fold (build3 (COND_EXPR, sizetype, t, size_zero_node, + size_binop (MINUS_EXPR, rounded_size, type_size))); + t = fold_convert (TREE_TYPE (addr), t); + addr = fold (build2 (PLUS_EXPR, TREE_TYPE (addr), addr, t)); } - addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL); - addr = copy_to_reg (addr); - /* Compute new value for AP. */ - if (! integer_zerop (rounded_size)) - { - t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, - build (PLUS_EXPR, TREE_TYPE (valist), valist, - rounded_size)); - TREE_SIDE_EFFECTS (t) = 1; - expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); - } + t = fold_convert (TREE_TYPE (valist), rounded_size); + t = build2 (PLUS_EXPR, TREE_TYPE (valist), valist_tmp, t); + t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, t); + gimplify_and_add (t, pre_p); + + addr = fold_convert (build_pointer_type (type), addr); - return addr; + if (indirect) + addr = build_fold_indirect_ref (addr); + + return build_fold_indirect_ref (addr); } -/* Expand __builtin_va_arg, which is not really a builtin function, but - a very special sort of operator. */ +/* Return a dummy expression of type TYPE in order to keep going after an + error. */ -rtx -expand_builtin_va_arg (tree valist, tree type) +static tree +dummy_object (tree type) +{ + tree t = convert (build_pointer_type (type), null_pointer_node); + return build1 (INDIRECT_REF, type, t); +} + +/* Gimplify __builtin_va_arg, aka VA_ARG_EXPR, which is not really a + builtin function, but a very special sort of operator. */ + +enum gimplify_status +gimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p) { - rtx addr, result; tree promoted_type, want_va_type, have_va_type; + tree valist = TREE_OPERAND (*expr_p, 0); + tree type = TREE_TYPE (*expr_p); + tree t; /* Verify that valist is of the proper type. */ - want_va_type = va_list_type_node; have_va_type = TREE_TYPE (valist); + + if (have_va_type == error_mark_node) + return GS_ERROR; + if (TREE_CODE (want_va_type) == ARRAY_TYPE) { /* If va_list is an array type, the argument may have decayed @@ -4206,10 +4398,11 @@ expand_builtin_va_arg (tree valist, tree type) have_va_type = TREE_TYPE (have_va_type); } } + if (TYPE_MAIN_VARIANT (want_va_type) != TYPE_MAIN_VARIANT (have_va_type)) { error ("first argument to `va_arg' not of type `va_list'"); - addr = const0_rtx; + return GS_ERROR; } /* Generate a diagnostic for requesting data of a type that cannot @@ -4217,66 +4410,59 @@ expand_builtin_va_arg (tree valist, tree type) else if ((promoted_type = lang_hooks.types.type_promotes_to (type)) != type) { - const char *name = "", *pname = 0; static bool gave_help; - if (TYPE_NAME (type)) - { - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - name = IDENTIFIER_POINTER (TYPE_NAME (type)); - else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_NAME (TYPE_NAME (type))) - name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); - } - if (TYPE_NAME (promoted_type)) - { - if (TREE_CODE (TYPE_NAME (promoted_type)) == IDENTIFIER_NODE) - pname = IDENTIFIER_POINTER (TYPE_NAME (promoted_type)); - else if (TREE_CODE (TYPE_NAME (promoted_type)) == TYPE_DECL - && DECL_NAME (TYPE_NAME (promoted_type))) - pname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (promoted_type))); - } - /* Unfortunately, this is merely undefined, rather than a constraint violation, so we cannot make this an error. If this call is never executed, the program is still strictly conforming. */ - warning ("`%s' is promoted to `%s' when passed through `...'", - name, pname); + warning ("`%T' is promoted to `%T' when passed through `...'", + type, promoted_type); if (! gave_help) { gave_help = true; - warning ("(so you should pass `%s' not `%s' to `va_arg')", - pname, name); + warning ("(so you should pass `%T' not `%T' to `va_arg')", + promoted_type, 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 (); + t = build_function_call_expr (implicit_built_in_decls[BUILT_IN_TRAP], + NULL); + append_to_statement_list (t, pre_p); /* This is dead code, but go ahead and finish so that the mode of the result comes out right. */ - addr = const0_rtx; + *expr_p = dummy_object (type); + return GS_ALL_DONE; } else { /* Make it easier for the backends by protecting the valist argument from multiple evaluations. */ - valist = stabilize_va_list (valist, 0); - -#ifdef EXPAND_BUILTIN_VA_ARG - addr = EXPAND_BUILTIN_VA_ARG (valist, type); -#else - addr = std_expand_builtin_va_arg (valist, type); -#endif - } - - addr = convert_memory_address (Pmode, addr); + if (TREE_CODE (va_list_type_node) == ARRAY_TYPE) + { + /* For this case, the backends will be expecting a pointer to + TREE_TYPE (va_list_type_node), but it's possible we've + actually been given an array (an actual va_list_type_node). + So fix it. */ + if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE) + { + tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node)); + valist = build_fold_addr_expr_with_type (valist, p1); + } + gimplify_expr (&valist, pre_p, post_p, is_gimple_val, fb_rvalue); + } + else + gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue); - result = gen_rtx_MEM (TYPE_MODE (type), addr); - set_mem_alias_set (result, get_varargs_alias_set ()); + if (!targetm.gimplify_va_arg_expr) + /* Once most targets are converted this should abort. */ + return GS_ALL_DONE; - return result; + *expr_p = targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p); + return GS_OK; + } } /* Expand ARGLIST, from a call to __builtin_va_end. */ @@ -4311,7 +4497,7 @@ expand_builtin_va_copy (tree arglist) if (TREE_CODE (va_list_type_node) != ARRAY_TYPE) { - t = build (MODIFY_EXPR, va_list_type_node, dst, src); + t = build2 (MODIFY_EXPR, va_list_type_node, dst, src); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } @@ -4384,7 +4570,7 @@ expand_builtin_frame_address (tree fndecl, tree arglist) if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) return tem; - if (GET_CODE (tem) != REG + if (!REG_P (tem) && ! CONSTANT_P (tem)) tem = copy_to_mode_reg (Pmode, tem); return tem; @@ -4401,6 +4587,12 @@ expand_builtin_alloca (tree arglist, rtx target) rtx op0; rtx result; + /* In -fmudflap-instrumented code, alloca() and __builtin_alloca() + should always expand to function calls. These can be intercepted + in libmudflap. */ + if (flag_mudflap) + return 0; + if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) return 0; @@ -4484,10 +4676,11 @@ expand_builtin_fputs (tree arglist, rtx target, bool unlocked) { /* New argument list transforming fputs(string, stream) to fputc(string[0], stream). */ - arglist = - build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist))); - arglist = - tree_cons (NULL_TREE, build_int_2 (p[0], 0), arglist); + arglist = build_tree_list (NULL_TREE, + TREE_VALUE (TREE_CHAIN (arglist))); + arglist = tree_cons (NULL_TREE, + build_int_cst (NULL_TREE, p[0], 0), + arglist); fn = fn_fputc; break; } @@ -4587,9 +4780,9 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) ret = get_insns (); drop_through_label = get_last_insn (); - if (drop_through_label && GET_CODE (drop_through_label) == NOTE) + if (drop_through_label && NOTE_P (drop_through_label)) drop_through_label = prev_nonnote_insn (drop_through_label); - if (drop_through_label && GET_CODE (drop_through_label) != CODE_LABEL) + if (drop_through_label && !LABEL_P (drop_through_label)) drop_through_label = NULL_RTX; end_sequence (); @@ -4604,7 +4797,7 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) { rtx next = NEXT_INSN (insn); - if (GET_CODE (insn) == JUMP_INSN && any_condjump_p (insn)) + if (JUMP_P (insn) && any_condjump_p (insn)) { rtx ifelse = SET_SRC (pc_set (insn)); rtx then_dest = XEXP (ifelse, 1); @@ -4627,10 +4820,10 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) /* Otherwise check where we drop through. */ else if (else_dest == pc_rtx) { - if (next && GET_CODE (next) == NOTE) + if (next && NOTE_P (next)) next = next_nonnote_insn (next); - if (next && GET_CODE (next) == JUMP_INSN + if (next && JUMP_P (next) && any_uncondjump_p (next)) temp = XEXP (SET_SRC (pc_set (next)), 0); else @@ -4645,10 +4838,10 @@ expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) } else if (then_dest == pc_rtx) { - if (next && GET_CODE (next) == NOTE) + if (next && NOTE_P (next)) next = next_nonnote_insn (next); - if (next && GET_CODE (next) == JUMP_INSN + if (next && JUMP_P (next) && any_uncondjump_p (next)) temp = XEXP (SET_SRC (pc_set (next)), 0); else @@ -4744,10 +4937,11 @@ build_string_literal (int len, const char *str) t = build_string (len, str); elem = build_type_variant (char_type_node, 1, 0); - index = build_index_type (build_int_2 (len - 1, 0)); + index = build_index_type (build_int_cst (NULL_TREE, len - 1, 0)); type = build_array_type (elem, index); TREE_TYPE (t) = type; TREE_CONSTANT (t) = 1; + TREE_INVARIANT (t) = 1; TREE_READONLY (t) = 1; TREE_STATIC (t) = 1; @@ -4829,7 +5023,7 @@ expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode, /* 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); + arg = build_int_cst (NULL_TREE, fmt_str[0], 0); arglist = build_tree_list (NULL_TREE, arg); fn = fn_putchar; } @@ -5000,8 +5194,7 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) const0_rtx, VOIDmode, EXPAND_NORMAL); if (target == const0_rtx) return const0_rtx; - exp = build_int_2 (strlen (fmt_str), 0); - exp = fold_convert (integer_type_node, exp); + exp = build_int_cst (NULL_TREE, strlen (fmt_str), 0); return expand_expr (exp, target, mode, EXPAND_NORMAL); } /* If the format is "%s", use strcpy if the result isn't used. */ @@ -5041,6 +5234,111 @@ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode) return 0; } +/* Expand a call to either the entry or exit function profiler. */ + +static rtx +expand_builtin_profile_func (bool exitp) +{ + rtx this, which; + + this = DECL_RTL (current_function_decl); + if (MEM_P (this)) + this = XEXP (this, 0); + else + abort (); + + if (exitp) + which = profile_function_exit_libfunc; + else + which = profile_function_entry_libfunc; + + emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this, Pmode, + expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, + 0, hard_frame_pointer_rtx), + Pmode); + + return const0_rtx; +} + +/* Given a trampoline address, make sure it satisfies TRAMPOLINE_ALIGNMENT. */ + +static rtx +round_trampoline_addr (rtx tramp) +{ + rtx temp, addend, mask; + + /* If we don't need too much alignment, we'll have been guaranteed + proper alignment by get_trampoline_type. */ + if (TRAMPOLINE_ALIGNMENT <= STACK_BOUNDARY) + return tramp; + + /* Round address up to desired boundary. */ + temp = gen_reg_rtx (Pmode); + addend = GEN_INT (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1); + mask = GEN_INT (-TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT); + + temp = expand_simple_binop (Pmode, PLUS, tramp, addend, + temp, 0, OPTAB_LIB_WIDEN); + tramp = expand_simple_binop (Pmode, AND, temp, mask, + temp, 0, OPTAB_LIB_WIDEN); + + return tramp; +} + +static rtx +expand_builtin_init_trampoline (tree arglist) +{ + tree t_tramp, t_func, t_chain; + rtx r_tramp, r_func, r_chain; +#ifdef TRAMPOLINE_TEMPLATE + rtx blktramp; +#endif + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, + POINTER_TYPE, VOID_TYPE)) + return NULL_RTX; + + t_tramp = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + t_func = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + t_chain = TREE_VALUE (arglist); + + r_tramp = expand_expr (t_tramp, NULL_RTX, VOIDmode, 0); + r_func = expand_expr (t_func, NULL_RTX, VOIDmode, 0); + r_chain = expand_expr (t_chain, NULL_RTX, VOIDmode, 0); + + /* Generate insns to initialize the trampoline. */ + r_tramp = round_trampoline_addr (r_tramp); +#ifdef TRAMPOLINE_TEMPLATE + blktramp = gen_rtx_MEM (BLKmode, r_tramp); + set_mem_align (blktramp, TRAMPOLINE_ALIGNMENT); + emit_block_move (blktramp, assemble_trampoline_template (), + GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL); +#endif + trampolines_created = 1; + INITIALIZE_TRAMPOLINE (r_tramp, r_func, r_chain); + + return const0_rtx; +} + +static rtx +expand_builtin_adjust_trampoline (tree arglist) +{ + rtx tramp; + + if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) + return NULL_RTX; + + tramp = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); + tramp = round_trampoline_addr (tramp); +#ifdef TRAMPOLINE_ADJUST_ADDRESS + TRAMPOLINE_ADJUST_ADDRESS (tramp); +#endif + + return tramp; +} + /* Expand a call to the built-in signbit, signbitf or signbitl function. Return NULL_RTX 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 @@ -5073,8 +5371,8 @@ expand_builtin_signbit (tree exp, rtx target) if (fmt->has_signed_zero && HONOR_SIGNED_ZEROS (fmode)) return 0; - arg = fold (build (LT_EXPR, TREE_TYPE (exp), arg, - build_real (TREE_TYPE (arg), dconst0))); + arg = fold (build2 (LT_EXPR, TREE_TYPE (exp), arg, + build_real (TREE_TYPE (arg), dconst0))); return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL); } @@ -5107,8 +5405,7 @@ expand_builtin_signbit (tree exp, rtx target) bitpos = GET_MODE_BITSIZE (imode) - 1 - bitpos; temp = copy_to_mode_reg (imode, temp); temp = extract_bit_field (temp, 1, bitpos, 1, - NULL_RTX, rmode, rmode, - GET_MODE_SIZE (imode)); + NULL_RTX, rmode, rmode); } else { @@ -5211,9 +5508,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, 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. */ - emit_queue (); - if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) return targetm.expand_builtin (exp, target, subtarget, mode, ignore); @@ -5257,13 +5551,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, switch (fcode) { - case BUILT_IN_ABS: - 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: @@ -5283,19 +5570,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, } break; - case BUILT_IN_CONJ: - case BUILT_IN_CONJF: - case BUILT_IN_CONJL: - case BUILT_IN_CREAL: - case BUILT_IN_CREALF: - case BUILT_IN_CREALL: - case BUILT_IN_CIMAG: - case BUILT_IN_CIMAGF: - case BUILT_IN_CIMAGL: - /* expand_tree_builtin changes these into CONJ_EXPR, REALPART_EXPR - and IMAGPART_EXPR. */ - abort (); - case BUILT_IN_EXP: case BUILT_IN_EXPF: case BUILT_IN_EXPL: @@ -5453,13 +5727,14 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, /* Return the address of the first anonymous stack arg. */ case BUILT_IN_NEXT_ARG: + simplify_builtin_next_arg (arglist); return expand_builtin_next_arg (arglist); case BUILT_IN_CLASSIFY_TYPE: return expand_builtin_classify_type (arglist); case BUILT_IN_CONSTANT_P: - return expand_builtin_constant_p (arglist, target_mode); + return const0_rtx; case BUILT_IN_FRAME_ADDRESS: case BUILT_IN_RETURN_ADDRESS: @@ -5470,7 +5745,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: if (arglist != 0 || ! AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))) - || GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) != MEM) + || !MEM_P (DECL_RTL (DECL_RESULT (current_function_decl)))) return const0_rtx; else return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0); @@ -5481,9 +5756,17 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - case BUILT_IN_FFS: - case BUILT_IN_FFSL: + case BUILT_IN_STACK_SAVE: + return expand_stack_save (); + + case BUILT_IN_STACK_RESTORE: + expand_stack_restore (TREE_VALUE (arglist)); + return const0_rtx; + + case BUILT_IN_FFS: + case BUILT_IN_FFSL: case BUILT_IN_FFSLL: + case BUILT_IN_FFSIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, ffs_optab); if (target) @@ -5493,6 +5776,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, case BUILT_IN_CLZ: case BUILT_IN_CLZL: case BUILT_IN_CLZLL: + case BUILT_IN_CLZIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, clz_optab); if (target) @@ -5502,6 +5786,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, case BUILT_IN_CTZ: case BUILT_IN_CTZL: case BUILT_IN_CTZLL: + case BUILT_IN_CTZIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, ctz_optab); if (target) @@ -5511,6 +5796,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, case BUILT_IN_POPCOUNT: case BUILT_IN_POPCOUNTL: case BUILT_IN_POPCOUNTLL: + case BUILT_IN_POPCOUNTIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, popcount_optab); if (target) @@ -5520,6 +5806,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, case BUILT_IN_PARITY: case BUILT_IN_PARITYL: case BUILT_IN_PARITYLL: + case BUILT_IN_PARITYIMAX: target = expand_builtin_unop (target_mode, arglist, target, subtarget, parity_optab); if (target) @@ -5684,6 +5971,12 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return const0_rtx; } + case BUILT_IN_NONLOCAL_GOTO: + target = expand_builtin_nonlocal_goto (arglist); + if (target) + return target; + break; + /* This updates the setjmp buffer that is its argument with the value of the current stack pointer. */ case BUILT_IN_UPDATE_SETJMP_BUF: @@ -5718,7 +6011,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, if (target) return target; break; - case BUILT_IN_FPUTS_UNLOCKED: target = expand_builtin_fputs (arglist, target, true); if (target) @@ -5792,6 +6084,16 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_builtin_prefetch (arglist); return const0_rtx; + case BUILT_IN_PROFILE_FUNC_ENTER: + return expand_builtin_profile_func (false); + case BUILT_IN_PROFILE_FUNC_EXIT: + return expand_builtin_profile_func (true); + + case BUILT_IN_INIT_TRAMPOLINE: + return expand_builtin_init_trampoline (arglist); + case BUILT_IN_ADJUST_TRAMPOLINE: + return expand_builtin_adjust_trampoline (arglist); + case BUILT_IN_FORK: case BUILT_IN_EXECL: case BUILT_IN_EXECV: @@ -5805,9 +6107,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; default: /* just do library call, if unknown builtin */ - if (!DECL_ASSEMBLER_NAME_SET_P (fndecl)) - error ("built-in function `%s' not currently supported", - IDENTIFIER_POINTER (DECL_NAME (fndecl))); + break; } /* The switch statement above can drop through to cause the function @@ -5927,15 +6227,54 @@ fold_builtin_constant_p (tree arglist) return 0; } +/* Fold a call to __builtin_expect, if we expect that a comparison against + the argument will fold to a constant. In practice, this means a true + constant or the address of a non-weak symbol. ARGLIST is the argument + list of the call. */ + +static tree +fold_builtin_expect (tree arglist) +{ + tree arg, inner; + + if (arglist == 0) + return 0; + + arg = TREE_VALUE (arglist); + + /* If the argument isn't invariant, then there's nothing we can do. */ + if (!TREE_INVARIANT (arg)) + return 0; + + /* If we're looking at an address of a weak decl, then do not fold. */ + inner = arg; + STRIP_NOPS (inner); + if (TREE_CODE (inner) == ADDR_EXPR) + { + do + { + inner = TREE_OPERAND (inner, 0); + } + while (TREE_CODE (inner) == COMPONENT_REF + || TREE_CODE (inner) == ARRAY_REF); + if (DECL_P (inner) && DECL_WEAK (inner)) + return 0; + } + + /* Otherwise, ARG already has the proper type for the return value. */ + return arg; +} + /* Fold a call to __builtin_classify_type. */ static tree fold_builtin_classify_type (tree arglist) { if (arglist == 0) - return build_int_2 (no_type_class, 0); + return build_int_cst (NULL_TREE, no_type_class, 0); - return build_int_2 (type_to_class (TREE_TYPE (TREE_VALUE (arglist))), 0); + return build_int_cst (NULL_TREE, + type_to_class (TREE_TYPE (TREE_VALUE (arglist))), 0); } /* Fold a call to __builtin_inf or __builtin_huge_val. */ @@ -6196,19 +6535,19 @@ fold_builtin_cabs (tree arglist, tree type) { tree rpart, ipart, result, arglist; - arg = save_expr (arg); + arg = builtin_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); + rpart = builtin_save_expr (rpart); + ipart = builtin_save_expr (ipart); - result = fold (build (PLUS_EXPR, type, - fold (build (MULT_EXPR, type, - rpart, rpart)), - fold (build (MULT_EXPR, type, - ipart, ipart)))); + result = fold (build2 (PLUS_EXPR, type, + fold (build2 (MULT_EXPR, type, + rpart, rpart)), + fold (build2 (MULT_EXPR, type, + ipart, ipart)))); arglist = build_tree_list (NULL_TREE, result); return build_function_call_expr (sqrtfn, arglist); @@ -6368,7 +6707,7 @@ fold_builtin_lround (tree exp) real_round (&r, TYPE_MODE (ftype), &x); REAL_VALUE_TO_INT (&lo, &hi, r); - result = build_int_2 (lo, hi); + result = build_int_cst (NULL_TREE, lo, hi); if (int_fits_type_p (result, itype)) return fold_convert (itype, result); } @@ -6397,7 +6736,7 @@ fold_builtin_bitop (tree exp) { HOST_WIDE_INT hi, width, result; unsigned HOST_WIDE_INT lo; - tree type, t; + tree type; type = TREE_TYPE (arg); width = TYPE_PRECISION (type); @@ -6477,9 +6816,7 @@ fold_builtin_bitop (tree exp) abort(); } - t = build_int_2 (result, 0); - TREE_TYPE (t) = TREE_TYPE (exp); - return t; + return build_int_cst (TREE_TYPE (exp), result, 0); } return NULL_TREE; @@ -6610,7 +6947,7 @@ fold_builtin_logarithm (tree exp, const REAL_VALUE_TYPE *value) 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 fold (build2 (MULT_EXPR, type, exponent, logfn)); } } } @@ -6745,7 +7082,7 @@ fold_builtin_mempcpy (tree exp) if (operand_equal_p (src, dest, 0)) { tree temp = fold_convert (TREE_TYPE (dest), len); - temp = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, temp)); + temp = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp)); return fold_convert (TREE_TYPE (exp), temp); } @@ -6780,14 +7117,15 @@ fold_builtin_memmove (tree exp) return 0; } -/* Fold function call to builtin strcpy. Return - NULL_TREE if no simplification can be made. */ +/* Fold function call to builtin strcpy. If LEN is not NULL, it represents + the length of the string to be copied. Return NULL_TREE if no + simplification can be made. */ -static tree -fold_builtin_strcpy (tree exp) +tree +fold_builtin_strcpy (tree exp, tree len) { tree arglist = TREE_OPERAND (exp, 1); - tree dest, src; + tree dest, src, fn; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) @@ -6800,17 +7138,37 @@ fold_builtin_strcpy (tree exp) if (operand_equal_p (src, dest, 0)) return fold_convert (TREE_TYPE (exp), dest); - return 0; + if (optimize_size) + return 0; + + fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + if (!fn) + return 0; + + if (!len) + { + len = c_strlen (src, 1); + if (! len || TREE_SIDE_EFFECTS (len)) + return 0; + } + + len = size_binop (PLUS_EXPR, len, ssize_int (1)); + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + return fold_convert (TREE_TYPE (exp), + build_function_call_expr (fn, arglist)); } -/* Fold function call to builtin strncpy. Return - NULL_TREE if no simplification can be made. */ +/* Fold function call to builtin strncpy. If SLEN is not NULL, it represents + the length of the source string. Return NULL_TREE if no simplification + can be made. */ -static tree -fold_builtin_strncpy (tree exp) +tree +fold_builtin_strncpy (tree exp, tree slen) { tree arglist = TREE_OPERAND (exp, 1); - tree dest, src, len; + tree dest, src, len, fn; if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -6824,17 +7182,98 @@ fold_builtin_strncpy (tree exp) if (integer_zerop (len)) return omit_one_operand (TREE_TYPE (exp), dest, src); - return 0; + /* We can't compare slen with len as constants below if len is not a + constant. */ + if (len == 0 || TREE_CODE (len) != INTEGER_CST) + return 0; + + if (!slen) + slen = c_strlen (src, 1); + + /* Now, we must be passed a constant src ptr parameter. */ + if (slen == 0 || TREE_CODE (slen) != INTEGER_CST) + return 0; + + slen = size_binop (PLUS_EXPR, slen, ssize_int (1)); + + /* We do not support simplification of this case, though we do + support it when expanding trees into RTL. */ + /* FIXME: generate a call to __builtin_memset. */ + if (tree_int_cst_lt (slen, len)) + return 0; + + /* OK transform into builtin memcpy. */ + fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + if (!fn) + return 0; + return fold_convert (TREE_TYPE (exp), + build_function_call_expr (fn, arglist)); +} + +/* Fold function call to builtin strchr and strrchr. + Return NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_strchr (tree exp, bool actually_strrchr) +{ + tree arglist = TREE_OPERAND (exp, 1); + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + else + { + tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); + const char *p1; + + if (TREE_CODE (s2) != INTEGER_CST) + return 0; + + p1 = c_getstr (s1); + if (p1 != NULL) + { + char c; + const char *r; + + if (target_char_cast (s2, &c)) + return 0; + + r = actually_strrchr ? strrchr (p1, c) : strchr (p1, c); + + if (r == NULL) + return fold_convert (TREE_TYPE (s1), integer_zero_node); + + /* Return an offset into the constant string argument. */ + return fold (build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + } + + if (actually_strrchr) + { + tree fn; + + if (!integer_zerop (s2)) + return 0; + + fn = implicit_built_in_decls[BUILT_IN_STRCHR]; + if (!fn) + return 0; + + /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */ + return build_function_call_expr (fn, arglist); + } + + return 0; + } } /* Fold function call to builtin memcmp. Return NULL_TREE if no simplification can be made. */ static tree -fold_builtin_memcmp (tree exp) +fold_builtin_memcmp (tree arglist) { - 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)) @@ -6846,14 +7285,48 @@ fold_builtin_memcmp (tree exp) /* 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); - } + return omit_two_operands (integer_type_node, integer_zero_node, + arg1, arg2); /* 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 omit_one_operand (integer_type_node, integer_zero_node, len); + + p1 = c_getstr (arg1); + p2 = c_getstr (arg2); + + /* If all arguments are constant, and the value of len is not greater + than the lengths of arg1 and arg2, evaluate at compile-time. */ + if (host_integerp (len, 1) && p1 && p2 + && compare_tree_int (len, strlen (p1) + 1) <= 0 + && compare_tree_int (len, strlen (p2) + 1) <= 0) + { + const int r = memcmp (p1, p2, tree_low_cst (len, 1)); + + if (r > 0) + return integer_one_node; + else if (r < 0) + return integer_minus_one_node; + else + return integer_zero_node; + } + + /* 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) + { + tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); + tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree ind1 = fold_convert (integer_type_node, + build1 (INDIRECT_REF, cst_uchar_node, + fold_convert (cst_uchar_ptr_node, + arg1))); + tree ind2 = fold_convert (integer_type_node, + build1 (INDIRECT_REF, cst_uchar_node, + fold_convert (cst_uchar_ptr_node, + arg2))); + return fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2)); + } return 0; } @@ -6862,14 +7335,12 @@ fold_builtin_memcmp (tree exp) NULL_TREE if no simplification can be made. */ static tree -fold_builtin_strcmp (tree exp) +fold_builtin_strcmp (tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2; const char *p1, *p2; - if (!validate_arglist (arglist, - POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return 0; arg1 = TREE_VALUE (arglist); @@ -6877,22 +7348,43 @@ fold_builtin_strcmp (tree exp) /* If ARG1 and ARG2 are the same (and not volatile), return zero. */ if (operand_equal_p (arg1, arg2, 0)) - return fold_convert (TREE_TYPE (exp), integer_zero_node); + return 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; + return integer_minus_one_node; else if (i > 0) - temp = integer_one_node; + return integer_one_node; else - temp = integer_zero_node; - return fold_convert (TREE_TYPE (exp), temp); + return integer_zero_node; + } + + /* If the second arg is "", return *(const unsigned char*)arg1. */ + if (p2 && *p2 == '\0') + { + tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); + tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + return fold_convert (integer_type_node, + build1 (INDIRECT_REF, cst_uchar_node, + fold_convert (cst_uchar_ptr_node, + arg1))); + } + + /* If the first arg is "", return -*(const unsigned char*)arg2. */ + if (p1 && *p1 == '\0') + { + tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); + tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree temp = fold_convert (integer_type_node, + build1 (INDIRECT_REF, cst_uchar_node, + fold_convert (cst_uchar_ptr_node, + arg2))); + return fold (build1 (NEGATE_EXPR, integer_type_node, temp)); } return 0; @@ -6902,9 +7394,8 @@ fold_builtin_strcmp (tree exp) NULL_TREE if no simplification can be made. */ static tree -fold_builtin_strncmp (tree exp) +fold_builtin_strncmp (tree arglist) { - tree arglist = TREE_OPERAND (exp, 1); tree arg1, arg2, len; const char *p1, *p2; @@ -6918,29 +7409,71 @@ fold_builtin_strncmp (tree exp) /* 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); - } + return omit_two_operands (integer_type_node, integer_zero_node, + arg1, arg2); /* 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 omit_one_operand (integer_type_node, 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; + if (i > 0) + return integer_one_node; + else if (i < 0) + return integer_minus_one_node; else - temp = integer_zero_node; - return fold_convert (TREE_TYPE (exp), temp); + return integer_zero_node; + } + + /* If the second arg is "", and the length is greater than zero, + return *(const unsigned char*)arg1. */ + if (p2 && *p2 == '\0' + && TREE_CODE (len) == INTEGER_CST + && tree_int_cst_sgn (len) == 1) + { + tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); + tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + return fold_convert (integer_type_node, + build1 (INDIRECT_REF, cst_uchar_node, + fold_convert (cst_uchar_ptr_node, + arg1))); + } + + /* If the first arg is "", and the length is greater than zero, + return -*(const unsigned char*)arg2. */ + if (p1 && *p1 == '\0' + && TREE_CODE (len) == INTEGER_CST + && tree_int_cst_sgn (len) == 1) + { + tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); + tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree temp = fold_convert (integer_type_node, + build1 (INDIRECT_REF, cst_uchar_node, + fold_convert (cst_uchar_ptr_node, + arg2))); + return fold (build1 (NEGATE_EXPR, integer_type_node, temp)); + } + + /* 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) + { + tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0); + tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node); + tree ind1 = fold_convert (integer_type_node, + build1 (INDIRECT_REF, cst_uchar_node, + fold_convert (cst_uchar_ptr_node, + arg1))); + tree ind2 = fold_convert (integer_type_node, + build1 (INDIRECT_REF, cst_uchar_node, + fold_convert (cst_uchar_ptr_node, + arg2))); + return fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2)); } return 0; @@ -6977,8 +7510,51 @@ fold_builtin_signbit (tree exp) /* If ARG's format doesn't have signed zeros, return "arg < 0.0". */ if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg)))) - return fold (build (LT_EXPR, TREE_TYPE (exp), arg, - build_real (TREE_TYPE (arg), dconst0))); + return fold (build2 (LT_EXPR, TREE_TYPE (exp), arg, + build_real (TREE_TYPE (arg), dconst0))); + + return NULL_TREE; +} + +/* Fold function call to builtin copysign, copysignf or copysignl. + Return NULL_TREE if no simplification can be made. */ + +static tree +fold_builtin_copysign (tree arglist, tree type) +{ + tree arg1, arg2; + + if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + return NULL_TREE; + + arg1 = TREE_VALUE (arglist); + arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + + /* copysign(X,X) is X. */ + if (operand_equal_p (arg1, arg2, 0)) + return fold_convert (type, arg1); + + /* If ARG1 and ARG2 are compile-time constants, determine the result. */ + if (TREE_CODE (arg1) == REAL_CST + && TREE_CODE (arg2) == REAL_CST + && !TREE_CONSTANT_OVERFLOW (arg1) + && !TREE_CONSTANT_OVERFLOW (arg2)) + { + REAL_VALUE_TYPE c1, c2; + + c1 = TREE_REAL_CST (arg1); + c2 = TREE_REAL_CST (arg2); + real_copysign (&c1, &c2); + return build_real (type, c1); + c1.sign = c2.sign; + } + + /* copysign(X, Y) is fabs(X) when Y is always non-negative. + Remember to evaluate Y for side-effects. */ + if (tree_expr_nonnegative_p (arg2)) + return omit_one_operand (type, + fold (build1 (ABS_EXPR, type, arg1)), + arg2); return NULL_TREE; } @@ -6994,12 +7570,18 @@ fold_builtin_isascii (tree arglist) { /* Transform isascii(c) -> ((c & ~0x7f) == 0). */ tree arg = TREE_VALUE (arglist); - - return fold (build (EQ_EXPR, integer_type_node, - build (BIT_AND_EXPR, integer_type_node, arg, - build_int_2 (~ (unsigned HOST_WIDE_INT) 0x7f, - ~ (HOST_WIDE_INT) 0)), - integer_zero_node)); + + arg = build2 (BIT_AND_EXPR, integer_type_node, arg, + build_int_cst (NULL_TREE, + ~ (unsigned HOST_WIDE_INT) 0x7f, + ~ (HOST_WIDE_INT) 0)); + arg = fold (build2 (EQ_EXPR, integer_type_node, + arg, integer_zero_node)); + + if (in_gimple_form && !TREE_CONSTANT (arg)) + return NULL_TREE; + else + return arg; } } @@ -7014,9 +7596,9 @@ fold_builtin_toascii (tree arglist) { /* Transform toascii(c) -> (c & 0x7f). */ tree arg = TREE_VALUE (arglist); - - return fold (build (BIT_AND_EXPR, integer_type_node, arg, - build_int_2 (0x7f, 0))); + + return fold (build2 (BIT_AND_EXPR, integer_type_node, arg, + build_int_cst (NULL_TREE, 0x7f, 0))); } } @@ -7033,71 +7615,308 @@ fold_builtin_isdigit (tree arglist) /* According to the C standard, isdigit is unaffected by locale. */ tree arg = TREE_VALUE (arglist); arg = fold_convert (unsigned_type_node, arg); - arg = build (MINUS_EXPR, unsigned_type_node, arg, - fold_convert (unsigned_type_node, - build_int_2 (TARGET_DIGIT0, 0))); - arg = build (LE_EXPR, integer_type_node, arg, - fold_convert (unsigned_type_node, build_int_2 (9, 0))); - return fold (arg); + arg = build2 (MINUS_EXPR, unsigned_type_node, arg, + build_int_cst (unsigned_type_node, TARGET_DIGIT0, 0)); + arg = build2 (LE_EXPR, integer_type_node, arg, + build_int_cst (unsigned_type_node, 9, 0)); + arg = fold (arg); + if (in_gimple_form && !TREE_CONSTANT (arg)) + return NULL_TREE; + else + return arg; } } -/* Used by constant folding to eliminate some builtin calls early. EXP is - the CALL_EXPR of a call to a builtin function. */ +/* Fold a call to fabs, fabsf or fabsl. */ -tree -fold_builtin (tree exp) +static tree +fold_builtin_fabs (tree arglist, tree type) { - tree fndecl = get_callee_fndecl (exp); - tree arglist = TREE_OPERAND (exp, 1); - tree type = TREE_TYPE (TREE_TYPE (fndecl)); + tree arg; - if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) + if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return 0; - switch (DECL_FUNCTION_CODE (fndecl)) - { - case BUILT_IN_CONSTANT_P: - return fold_builtin_constant_p (arglist); + arg = TREE_VALUE (arglist); + if (TREE_CODE (arg) == REAL_CST) + return fold_abs_const (arg, type); + return fold (build1 (ABS_EXPR, type, arg)); +} - case BUILT_IN_CLASSIFY_TYPE: - return fold_builtin_classify_type (arglist); +/* Fold a call to abs, labs, llabs or imaxabs. */ - case BUILT_IN_STRLEN: - if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) - { - tree len = c_strlen (TREE_VALUE (arglist), 0); - if (len) - { - /* Convert from the internal "sizetype" type to "size_t". */ - if (size_type_node) - len = fold_convert (size_type_node, len); - return len; - } - } - break; +static tree +fold_builtin_abs (tree arglist, tree type) +{ + tree arg; - 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; + if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) + return 0; - case BUILT_IN_CABS: - case BUILT_IN_CABSF: - case BUILT_IN_CABSL: - return fold_builtin_cabs (arglist, type); + arg = TREE_VALUE (arglist); + if (TREE_CODE (arg) == INTEGER_CST) + return fold_abs_const (arg, type); + return fold (build1 (ABS_EXPR, type, arg)); +} - 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); +/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite. + EXP is the CALL_EXPR for the call. */ - /* Optimize sqrt of constant value. */ +static tree +fold_builtin_classify (tree exp, int builtin_index) +{ + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); + tree arg; + REAL_VALUE_TYPE r; + + if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + { + /* Check that we have exactly one argument. */ + if (arglist == 0) + { + error ("too few arguments to function `%s'", + IDENTIFIER_POINTER (DECL_NAME (fndecl))); + return error_mark_node; + } + else if (TREE_CHAIN (arglist) != 0) + { + error ("too many arguments to function `%s'", + IDENTIFIER_POINTER (DECL_NAME (fndecl))); + return error_mark_node; + } + else + { + error ("non-floating-point argument to function `%s'", + IDENTIFIER_POINTER (DECL_NAME (fndecl))); + return error_mark_node; + } + } + + arg = TREE_VALUE (arglist); + switch (builtin_index) + { + case BUILT_IN_ISINF: + if (!MODE_HAS_INFINITIES (TYPE_MODE (TREE_TYPE (arg)))) + return omit_one_operand (type, integer_zero_node, arg); + + if (TREE_CODE (arg) == REAL_CST) + { + r = TREE_REAL_CST (arg); + if (real_isinf (&r)) + return real_compare (GT_EXPR, &r, &dconst0) + ? integer_one_node : integer_minus_one_node; + else + return integer_zero_node; + } + + return NULL_TREE; + + case BUILT_IN_FINITE: + if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg))) + && !MODE_HAS_INFINITIES (TYPE_MODE (TREE_TYPE (arg)))) + return omit_one_operand (type, integer_zero_node, arg); + + if (TREE_CODE (arg) == REAL_CST) + { + r = TREE_REAL_CST (arg); + return real_isinf (&r) || real_isnan (&r) + ? integer_zero_node : integer_one_node; + } + + return NULL_TREE; + + case BUILT_IN_ISNAN: + if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg)))) + return omit_one_operand (type, integer_zero_node, arg); + + if (TREE_CODE (arg) == REAL_CST) + { + r = TREE_REAL_CST (arg); + return real_isnan (&r) ? integer_one_node : integer_zero_node; + } + + arg = builtin_save_expr (arg); + return fold (build2 (UNORDERED_EXPR, type, arg, arg)); + + default: + abort (); + } +} + +/* Fold a call to an unordered comparison function such as + __builtin_isgreater(). EXP is the CALL_EXPR for the call. + UNORDERED_CODE and ORDERED_CODE are comparison codes that give + the opposite of the desired result. UNORDERED_CODE is used + for modes that can hold NaNs and ORDERED_CODE is used for + the rest. */ + +static tree +fold_builtin_unordered_cmp (tree exp, + enum tree_code unordered_code, + enum tree_code ordered_code) +{ + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); + enum tree_code code; + tree arg0, arg1; + + if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + { + enum tree_code code0, code1; + tree type0, type1; + tree cmp_type = 0; + + /* Check that we have exactly two arguments. */ + if (arglist == 0 || TREE_CHAIN (arglist) == 0) + { + error ("too few arguments to function `%s'", + IDENTIFIER_POINTER (DECL_NAME (fndecl))); + return error_mark_node; + } + else if (TREE_CHAIN (TREE_CHAIN (arglist)) != 0) + { + error ("too many arguments to function `%s'", + IDENTIFIER_POINTER (DECL_NAME (fndecl))); + return error_mark_node; + } + + arg0 = TREE_VALUE (arglist); + arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + + type0 = TREE_TYPE (arg0); + type1 = TREE_TYPE (arg1); + + code0 = TREE_CODE (type0); + code1 = TREE_CODE (type1); + + if (code0 == REAL_TYPE && code1 == REAL_TYPE) + /* Choose the wider of two real types. */ + cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1) + ? type0 : type1; + else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE) + cmp_type = type0; + else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE) + cmp_type = type1; + else + { + error ("non-floating-point argument to function `%s'", + IDENTIFIER_POINTER (DECL_NAME (fndecl))); + return error_mark_node; + } + + arg0 = fold_convert (cmp_type, arg0); + arg1 = fold_convert (cmp_type, arg1); + } + else + { + arg0 = TREE_VALUE (arglist); + arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + } + + if (unordered_code == UNORDERED_EXPR) + { + if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0)))) + return omit_two_operands (type, integer_zero_node, arg0, arg1); + return fold (build2 (UNORDERED_EXPR, type, arg0, arg1)); + } + + code = MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0))) ? unordered_code + : ordered_code; + return fold (build1 (TRUTH_NOT_EXPR, type, + fold (build2 (code, type, arg0, arg1)))); +} + +/* Used by constant folding to simplify calls to builtin functions. EXP is + the CALL_EXPR of a call to a builtin function. IGNORE is true if the + result of the function call is ignored. This function returns NULL_TREE + if no simplification was possible. */ + +static tree +fold_builtin_1 (tree exp, bool ignore) +{ + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + tree type = TREE_TYPE (TREE_TYPE (fndecl)); + + if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) + return 0; + + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_CONSTANT_P: + return fold_builtin_constant_p (arglist); + + case BUILT_IN_EXPECT: + return fold_builtin_expect (arglist); + + case BUILT_IN_CLASSIFY_TYPE: + return fold_builtin_classify_type (arglist); + + case BUILT_IN_STRLEN: + if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) + { + tree len = c_strlen (TREE_VALUE (arglist), 0); + if (len) + { + /* Convert from the internal "sizetype" type to "size_t". */ + if (size_type_node) + len = fold_convert (size_type_node, len); + return len; + } + } + break; + + case BUILT_IN_FABS: + case BUILT_IN_FABSF: + case BUILT_IN_FABSL: + return fold_builtin_fabs (arglist, type); + + case BUILT_IN_ABS: + case BUILT_IN_LABS: + case BUILT_IN_LLABS: + case BUILT_IN_IMAXABS: + return fold_builtin_abs (arglist, type); + + case BUILT_IN_CONJ: + case BUILT_IN_CONJF: + case BUILT_IN_CONJL: + if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) + return fold (build1 (CONJ_EXPR, type, TREE_VALUE (arglist))); + break; + + case BUILT_IN_CREAL: + case BUILT_IN_CREALF: + case BUILT_IN_CREALL: + if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) + return non_lvalue (fold (build1 (REALPART_EXPR, type, + TREE_VALUE (arglist)))); + break; + + case BUILT_IN_CIMAG: + case BUILT_IN_CIMAGF: + case BUILT_IN_CIMAGL: + if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE)) + return non_lvalue (fold (build1 (IMAGPART_EXPR, type, + TREE_VALUE (arglist)))); + break; + + case BUILT_IN_CABS: + case BUILT_IN_CABSF: + case BUILT_IN_CABSL: + return fold_builtin_cabs (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)) { @@ -7114,9 +7933,9 @@ fold_builtin (tree exp) if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode)) { 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))); + arg = fold (build2 (MULT_EXPR, type, + TREE_VALUE (TREE_OPERAND (arg, 1)), + build_real (type, dconsthalf))); arglist = build_tree_list (NULL_TREE, arg); return build_function_call_expr (expfn, arglist); } @@ -7125,7 +7944,7 @@ fold_builtin (tree exp) if (flag_unsafe_math_optimizations && BUILTIN_ROOT_P (fcode)) { tree powfn = mathfn_built_in (type, BUILT_IN_POW); - + if (powfn) { tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1)); @@ -7133,7 +7952,7 @@ fold_builtin (tree exp) /* The inner root was either sqrt or cbrt. */ REAL_VALUE_TYPE dconstroot = BUILTIN_SQRT_P (fcode) ? dconsthalf : dconstthird; - + /* Adjust for the outer root. */ SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1); dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot); @@ -7153,8 +7972,8 @@ fold_builtin (tree exp) 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))); + tree narg1 = fold (build2 (MULT_EXPR, type, arg1, + build_real (type, dconsthalf))); arglist = tree_cons (NULL_TREE, arg0, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (powfn, arglist); @@ -7180,9 +7999,9 @@ fold_builtin (tree exp) tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); const REAL_VALUE_TYPE third_trunc = real_value_truncate (TYPE_MODE (type), dconstthird); - arg = fold (build (MULT_EXPR, type, - TREE_VALUE (TREE_OPERAND (arg, 1)), - build_real (type, third_trunc))); + arg = fold (build2 (MULT_EXPR, type, + TREE_VALUE (TREE_OPERAND (arg, 1)), + build_real (type, third_trunc))); arglist = build_tree_list (NULL_TREE, arg); return build_function_call_expr (expfn, arglist); } @@ -7207,7 +8026,7 @@ fold_builtin (tree exp) build_tree_list (NULL_TREE, tree_root)); return build_function_call_expr (powfn, arglist); } - + } } break; @@ -7250,10 +8069,12 @@ fold_builtin (tree 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: @@ -7261,21 +8082,21 @@ fold_builtin (tree exp) 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: @@ -7352,9 +8173,8 @@ fold_builtin (tree exp) /* 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)); + return fold (build2 (RDIV_EXPR, type, + build_real (type, dconst1), arg0)); /* Optimize pow(x,0.5) = sqrt(x). */ if (flag_unsafe_math_optimizations @@ -7398,7 +8218,7 @@ fold_builtin (tree exp) { 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)); + arg = fold (build2 (MULT_EXPR, type, arg, arg1)); arglist = build_tree_list (NULL_TREE, arg); return build_function_call_expr (expfn, arglist); } @@ -7407,8 +8227,8 @@ fold_builtin (tree exp) if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode)) { tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1)); - tree narg1 = fold (build (MULT_EXPR, type, arg1, - build_real (type, dconsthalf))); + tree narg1 = fold (build2 (MULT_EXPR, type, arg1, + build_real (type, dconsthalf))); arglist = tree_cons (NULL_TREE, narg0, build_tree_list (NULL_TREE, narg1)); @@ -7423,7 +8243,7 @@ fold_builtin (tree exp) { 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)); + tree narg1 = fold (build2 (MULT_EXPR, type, arg01, arg1)); arglist = tree_cons (NULL_TREE, arg00, build_tree_list (NULL_TREE, narg1)); return build_function_call_expr (fndecl, arglist); @@ -7522,19 +8342,27 @@ fold_builtin (tree exp) return fold_builtin_memmove (exp); case BUILT_IN_STRCPY: - return fold_builtin_strcpy (exp); + return fold_builtin_strcpy (exp, NULL_TREE); case BUILT_IN_STRNCPY: - return fold_builtin_strncpy (exp); + return fold_builtin_strncpy (exp, NULL_TREE); + + case BUILT_IN_INDEX: + case BUILT_IN_STRCHR: + return fold_builtin_strchr (exp, false); + + case BUILT_IN_RINDEX: + case BUILT_IN_STRRCHR: + return fold_builtin_strchr (exp, true); case BUILT_IN_MEMCMP: - return fold_builtin_memcmp (exp); + return fold_builtin_memcmp (arglist); case BUILT_IN_STRCMP: - return fold_builtin_strcmp (exp); + return fold_builtin_strcmp (arglist); case BUILT_IN_STRNCMP: - return fold_builtin_strncmp (exp); + return fold_builtin_strncmp (arglist); case BUILT_IN_SIGNBIT: case BUILT_IN_SIGNBITF: @@ -7550,6 +8378,45 @@ fold_builtin (tree exp) case BUILT_IN_ISDIGIT: return fold_builtin_isdigit (arglist); + case BUILT_IN_COPYSIGN: + case BUILT_IN_COPYSIGNF: + case BUILT_IN_COPYSIGNL: + return fold_builtin_copysign (arglist, type); + + case BUILT_IN_FINITE: + case BUILT_IN_FINITEF: + case BUILT_IN_FINITEL: + return fold_builtin_classify (exp, BUILT_IN_FINITE); + + case BUILT_IN_ISINF: + case BUILT_IN_ISINFF: + case BUILT_IN_ISINFL: + return fold_builtin_classify (exp, BUILT_IN_ISINF); + + case BUILT_IN_ISNAN: + case BUILT_IN_ISNANF: + case BUILT_IN_ISNANL: + return fold_builtin_classify (exp, BUILT_IN_ISNAN); + + case BUILT_IN_ISGREATER: + return fold_builtin_unordered_cmp (exp, UNLE_EXPR, LE_EXPR); + case BUILT_IN_ISGREATEREQUAL: + return fold_builtin_unordered_cmp (exp, UNLT_EXPR, LT_EXPR); + case BUILT_IN_ISLESS: + return fold_builtin_unordered_cmp (exp, UNGE_EXPR, GE_EXPR); + case BUILT_IN_ISLESSEQUAL: + return fold_builtin_unordered_cmp (exp, UNGT_EXPR, GT_EXPR); + case BUILT_IN_ISLESSGREATER: + return fold_builtin_unordered_cmp (exp, UNEQ_EXPR, EQ_EXPR); + case BUILT_IN_ISUNORDERED: + return fold_builtin_unordered_cmp (exp, UNORDERED_EXPR, NOP_EXPR); + + case BUILT_IN_FPUTS: + return fold_builtin_fputs (arglist, ignore, false, NULL_TREE); + + case BUILT_IN_FPUTS_UNLOCKED: + return fold_builtin_fputs (arglist, ignore, true, NULL_TREE); + default: break; } @@ -7557,6 +8424,24 @@ fold_builtin (tree exp) return 0; } +/* A wrapper function for builtin folding that prevents warnings for + "statement without effect" and the like, caused by removing the + call node earlier than the warning is generated. */ + +tree +fold_builtin (tree exp, bool ignore) +{ + exp = fold_builtin_1 (exp, ignore); + if (exp) + { + /* ??? Don't clobber shared nodes such as integer_zero_node. */ + if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c') + exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp); + TREE_NO_WARNING (exp) = 1; + } + return exp; +} + /* Conveniently construct a function call expression. */ tree @@ -7565,8 +8450,8 @@ build_function_call_expr (tree fn, tree arglist) tree call_expr; call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); - call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), - call_expr, arglist); + call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), + call_expr, arglist, NULL_TREE); return fold (call_expr); } @@ -7631,31 +8516,6 @@ default_expand_builtin (tree exp ATTRIBUTE_UNUSED, return NULL_RTX; } -/* Instantiate all remaining CONSTANT_P_RTX nodes. */ - -void -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); - } -} - /* Returns true is EXP represents data that would potentially reside in a readonly section. */ @@ -7664,8 +8524,772 @@ readonly_data_expr (tree exp) { STRIP_NOPS (exp); - if (TREE_CODE (exp) == ADDR_EXPR) - return decl_readonly_section (TREE_OPERAND (exp, 0), 0); + if (TREE_CODE (exp) != ADDR_EXPR) + return false; + + exp = get_base_address (TREE_OPERAND (exp, 0)); + if (!exp) + return false; + + /* Make sure we call decl_readonly_section only for trees it + can handle (since it returns true for everything it doesn't + understand). */ + if (TREE_CODE (exp) == STRING_CST + || TREE_CODE (exp) == CONSTRUCTOR + || (TREE_CODE (exp) == VAR_DECL && TREE_STATIC (exp))) + return decl_readonly_section (exp, 0); else return false; } + +/* Front-end to the simplify_builtin_XXX routines. + + EXP is a call to a builtin function. If possible try to simplify + that into a constant, expression or call to a more efficient + builtin function. + + If IGNORE is nonzero, then the result of this builtin function + call is ignored. + + If simplification is possible, return the simplified tree, otherwise + return NULL_TREE. */ + +tree +simplify_builtin (tree exp, int ignore) +{ + tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + tree arglist = TREE_OPERAND (exp, 1); + enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); + tree val; + + switch (fcode) + { + case BUILT_IN_FPUTS: + val = fold_builtin_fputs (arglist, ignore, false, NULL_TREE); + break; + case BUILT_IN_FPUTS_UNLOCKED: + val = fold_builtin_fputs (arglist, ignore, true, NULL_TREE); + break; + case BUILT_IN_STRSTR: + val = simplify_builtin_strstr (arglist); + break; + case BUILT_IN_STRCAT: + val = simplify_builtin_strcat (arglist); + break; + case BUILT_IN_STRNCAT: + val = simplify_builtin_strncat (arglist); + break; + case BUILT_IN_STRSPN: + val = simplify_builtin_strspn (arglist); + break; + case BUILT_IN_STRCSPN: + val = simplify_builtin_strcspn (arglist); + break; + case BUILT_IN_STRCHR: + case BUILT_IN_INDEX: + val = simplify_builtin_strchr (arglist); + break; + case BUILT_IN_STRRCHR: + case BUILT_IN_RINDEX: + val = simplify_builtin_strrchr (arglist); + break; + case BUILT_IN_STRCPY: + val = fold_builtin_strcpy (exp, NULL_TREE); + break; + case BUILT_IN_STRNCPY: + val = fold_builtin_strncpy (exp, NULL_TREE); + break; + case BUILT_IN_STRCMP: + val = fold_builtin_strcmp (arglist); + break; + case BUILT_IN_STRNCMP: + val = fold_builtin_strncmp (arglist); + break; + case BUILT_IN_STRPBRK: + val = simplify_builtin_strpbrk (arglist); + break; + case BUILT_IN_BCMP: + case BUILT_IN_MEMCMP: + val = fold_builtin_memcmp (arglist); + break; + case BUILT_IN_VA_START: + simplify_builtin_va_start (arglist); + val = NULL_TREE; + break; + case BUILT_IN_SPRINTF: + val = simplify_builtin_sprintf (arglist, ignore); + break; + case BUILT_IN_CONSTANT_P: + val = fold_builtin_constant_p (arglist); + /* Gimplification will pull the CALL_EXPR for the builtin out of + an if condition. When not optimizing, we'll not CSE it back. + To avoid link error types of regressions, return false now. */ + if (!val && !optimize) + val = integer_zero_node; + break; + default: + val = NULL_TREE; + break; + } + + if (val) + val = fold_convert (TREE_TYPE (exp), val); + return val; +} + +/* Simplify a call to the strstr builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. + + The simplified form may be a constant or other expression which + computes the same value, but in a more efficient manner (including + calls to other builtin functions). + + The call may contain arguments which need to be evaluated, but + which are not useful to determine the result of the call. In + this case we return a chain of COMPOUND_EXPRs. The LHS of each + COMPOUND_EXPR will be an argument which must be evaluated. + COMPOUND_EXPRs are chained through their RHS. The RHS of the last + COMPOUND_EXPR in the chain will contain the tree for the simplified + form of the builtin function call. */ + +static tree +simplify_builtin_strstr (tree arglist) +{ + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + else + { + tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); + tree fn; + const char *p1, *p2; + + p2 = c_getstr (s2); + if (p2 == NULL) + return 0; + + p1 = c_getstr (s1); + if (p1 != NULL) + { + const char *r = strstr (p1, p2); + + if (r == NULL) + return fold_convert (TREE_TYPE (s1), integer_zero_node); + + /* Return an offset into the constant string argument. */ + return fold (build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + } + + if (p2[0] == '\0') + return s1; + + if (p2[1] != '\0') + return 0; + + fn = implicit_built_in_decls[BUILT_IN_STRCHR]; + if (!fn) + return 0; + + /* New argument list transforming strstr(s1, s2) to + strchr(s1, s2[0]). */ + arglist = build_tree_list (NULL_TREE, + build_int_cst (NULL_TREE, p2[0], 0)); + arglist = tree_cons (NULL_TREE, s1, arglist); + return build_function_call_expr (fn, arglist); + } +} + +/* Simplify a call to the strstr builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. + + The simplified form may be a constant or other expression which + computes the same value, but in a more efficient manner (including + calls to other builtin functions). + + The call may contain arguments which need to be evaluated, but + which are not useful to determine the result of the call. In + this case we return a chain of COMPOUND_EXPRs. The LHS of each + COMPOUND_EXPR will be an argument which must be evaluated. + COMPOUND_EXPRs are chained through their RHS. The RHS of the last + COMPOUND_EXPR in the chain will contain the tree for the simplified + form of the builtin function call. */ + +static tree +simplify_builtin_strchr (tree arglist) +{ + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + else + { + tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); + const char *p1; + + if (TREE_CODE (s2) != INTEGER_CST) + return 0; + + p1 = c_getstr (s1); + if (p1 != NULL) + { + char c; + const char *r; + + if (target_char_cast (s2, &c)) + return 0; + + r = strchr (p1, c); + + if (r == NULL) + return fold_convert (TREE_TYPE (s1), integer_zero_node); + + /* Return an offset into the constant string argument. */ + return fold (build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + } + + /* FIXME: Should use here strchrM optab so that ports can optimize + this. */ + return 0; + } +} + +/* Simplify a call to the strrchr builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. + + The simplified form may be a constant or other expression which + computes the same value, but in a more efficient manner (including + calls to other builtin functions). + + The call may contain arguments which need to be evaluated, but + which are not useful to determine the result of the call. In + this case we return a chain of COMPOUND_EXPRs. The LHS of each + COMPOUND_EXPR will be an argument which must be evaluated. + COMPOUND_EXPRs are chained through their RHS. The RHS of the last + COMPOUND_EXPR in the chain will contain the tree for the simplified + form of the builtin function call. */ + +static tree +simplify_builtin_strrchr (tree arglist) +{ + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + else + { + tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); + tree fn; + const char *p1; + + if (TREE_CODE (s2) != INTEGER_CST) + return 0; + + p1 = c_getstr (s1); + if (p1 != NULL) + { + char c; + const char *r; + + if (target_char_cast (s2, &c)) + return 0; + + r = strrchr (p1, c); + + if (r == NULL) + return fold_convert (TREE_TYPE (s1), integer_zero_node); + + /* Return an offset into the constant string argument. */ + return fold (build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + } + + if (! integer_zerop (s2)) + return 0; + + fn = implicit_built_in_decls[BUILT_IN_STRCHR]; + if (!fn) + return 0; + + /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */ + return build_function_call_expr (fn, arglist); + } +} + +/* Simplify a call to the strpbrk builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. + + The simplified form may be a constant or other expression which + computes the same value, but in a more efficient manner (including + calls to other builtin functions). + + The call may contain arguments which need to be evaluated, but + which are not useful to determine the result of the call. In + this case we return a chain of COMPOUND_EXPRs. The LHS of each + COMPOUND_EXPR will be an argument which must be evaluated. + COMPOUND_EXPRs are chained through their RHS. The RHS of the last + COMPOUND_EXPR in the chain will contain the tree for the simplified + form of the builtin function call. */ + +static tree +simplify_builtin_strpbrk (tree arglist) +{ + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + else + { + tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); + tree fn; + const char *p1, *p2; + + p2 = c_getstr (s2); + if (p2 == NULL) + return 0; + + p1 = c_getstr (s1); + if (p1 != NULL) + { + const char *r = strpbrk (p1, p2); + + if (r == NULL) + return fold_convert (TREE_TYPE (s1), integer_zero_node); + + /* Return an offset into the constant string argument. */ + return fold (build2 (PLUS_EXPR, TREE_TYPE (s1), + s1, fold_convert (TREE_TYPE (s1), + ssize_int (r - p1)))); + } + + if (p2[0] == '\0') + /* strpbrk(x, "") == NULL. + Evaluate and ignore s1 in case it had side-effects. */ + return omit_one_operand (TREE_TYPE (s1), integer_zero_node, s1); + + if (p2[1] != '\0') + return 0; /* Really call strpbrk. */ + + fn = implicit_built_in_decls[BUILT_IN_STRCHR]; + if (!fn) + return 0; + + /* New argument list transforming strpbrk(s1, s2) to + strchr(s1, s2[0]). */ + arglist = build_tree_list (NULL_TREE, + build_int_cst (NULL_TREE, p2[0], 0)); + arglist = tree_cons (NULL_TREE, s1, arglist); + return build_function_call_expr (fn, arglist); + } +} + +/* Simplify a call to the strcat builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. + + The simplified form may be a constant or other expression which + computes the same value, but in a more efficient manner (including + calls to other builtin functions). + + The call may contain arguments which need to be evaluated, but + which are not useful to determine the result of the call. In + this case we return a chain of COMPOUND_EXPRs. The LHS of each + COMPOUND_EXPR will be an argument which must be evaluated. + COMPOUND_EXPRs are chained through their RHS. The RHS of the last + COMPOUND_EXPR in the chain will contain the tree for the simplified + form of the builtin function call. */ + +static tree +simplify_builtin_strcat (tree arglist) +{ + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + else + { + tree dst = TREE_VALUE (arglist), + 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 dst; + + return 0; + } +} + +/* Simplify a call to the strncat builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. + + The simplified form may be a constant or other expression which + computes the same value, but in a more efficient manner (including + calls to other builtin functions). + + The call may contain arguments which need to be evaluated, but + which are not useful to determine the result of the call. In + this case we return a chain of COMPOUND_EXPRs. The LHS of each + COMPOUND_EXPR will be an argument which must be evaluated. + COMPOUND_EXPRs are chained through their RHS. The RHS of the last + COMPOUND_EXPR in the chain will contain the tree for the simplified + form of the builtin function call. */ + +static tree +simplify_builtin_strncat (tree arglist) +{ + if (!validate_arglist (arglist, + POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + else + { + tree dst = TREE_VALUE (arglist); + tree src = TREE_VALUE (TREE_CHAIN (arglist)); + tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + const char *p = c_getstr (src); + + /* If the requested length is zero, or the src parameter string + length is zero, return the dst parameter. */ + if (integer_zerop (len) || (p && *p == '\0')) + return omit_two_operands (TREE_TYPE (dst), dst, src, len); + + /* If the requested len is greater than or equal to the string + length, call strcat. */ + if (TREE_CODE (len) == INTEGER_CST && p + && compare_tree_int (len, strlen (p)) >= 0) + { + tree newarglist + = tree_cons (NULL_TREE, dst, build_tree_list (NULL_TREE, src)); + tree fn = implicit_built_in_decls[BUILT_IN_STRCAT]; + + /* If the replacement _DECL isn't initialized, don't do the + transformation. */ + if (!fn) + return 0; + + return build_function_call_expr (fn, newarglist); + } + return 0; + } +} + +/* Simplify a call to the strspn builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. + + The simplified form may be a constant or other expression which + computes the same value, but in a more efficient manner (including + calls to other builtin functions). + + The call may contain arguments which need to be evaluated, but + which are not useful to determine the result of the call. In + this case we return a chain of COMPOUND_EXPRs. The LHS of each + COMPOUND_EXPR will be an argument which must be evaluated. + COMPOUND_EXPRs are chained through their RHS. The RHS of the last + COMPOUND_EXPR in the chain will contain the tree for the simplified + form of the builtin function call. */ + +static tree +simplify_builtin_strspn (tree arglist) +{ + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + else + { + tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); + const char *p1 = c_getstr (s1), *p2 = c_getstr (s2); + + /* If both arguments are constants, evaluate at compile-time. */ + if (p1 && p2) + { + const size_t r = strspn (p1, p2); + return size_int (r); + } + + /* If either argument is "", return 0. */ + if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0')) + /* Evaluate and ignore both arguments in case either one has + side-effects. */ + return omit_two_operands (integer_type_node, integer_zero_node, + s1, s2); + return 0; + } +} + +/* Simplify a call to the strcspn builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. + + The simplified form may be a constant or other expression which + computes the same value, but in a more efficient manner (including + calls to other builtin functions). + + The call may contain arguments which need to be evaluated, but + which are not useful to determine the result of the call. In + this case we return a chain of COMPOUND_EXPRs. The LHS of each + COMPOUND_EXPR will be an argument which must be evaluated. + COMPOUND_EXPRs are chained through their RHS. The RHS of the last + COMPOUND_EXPR in the chain will contain the tree for the simplified + form of the builtin function call. */ + +static tree +simplify_builtin_strcspn (tree arglist) +{ + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + else + { + tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist)); + const char *p1 = c_getstr (s1), *p2 = c_getstr (s2); + + /* If both arguments are constants, evaluate at compile-time. */ + if (p1 && p2) + { + const size_t r = strcspn (p1, p2); + return size_int (r); + } + + /* If the first argument is "", return 0. */ + if (p1 && *p1 == '\0') + { + /* Evaluate and ignore argument s2 in case it has + side-effects. */ + return omit_one_operand (integer_type_node, + integer_zero_node, s2); + } + + /* If the second argument is "", return __builtin_strlen(s1). */ + if (p2 && *p2 == '\0') + { + tree newarglist = build_tree_list (NULL_TREE, s1), + fn = implicit_built_in_decls[BUILT_IN_STRLEN]; + + /* If the replacement _DECL isn't initialized, don't do the + transformation. */ + if (!fn) + return 0; + + return build_function_call_expr (fn, newarglist); + } + return 0; + } +} + +/* Fold a call to the fputs builtin. IGNORE is true if the value returned + by the builtin will be ignored. UNLOCKED is true is true if this + actually a call to fputs_unlocked. If LEN in non-NULL, it represents + the known length of the string. Return NULL_TREE if no simplification + was possible. */ + +tree +fold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len) +{ + tree fn; + tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED] + : 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) + return 0; + + /* Verify the arguments in the original call. */ + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return 0; + + if (! len) + len = c_strlen (TREE_VALUE (arglist), 0); + + /* Get the length of the string passed to fputs. If the length + can't be determined, punt. */ + if (!len + || TREE_CODE (len) != INTEGER_CST) + return 0; + + switch (compare_tree_int (len, 1)) + { + case -1: /* length is 0, delete the call entirely . */ + return omit_one_operand (integer_type_node, integer_zero_node, + TREE_VALUE (TREE_CHAIN (arglist))); + + case 0: /* length is 1, call fputc. */ + { + const char *p = c_getstr (TREE_VALUE (arglist)); + + if (p != NULL) + { + /* New argument list transforming fputs(string, stream) to + fputc(string[0], stream). */ + arglist = build_tree_list (NULL_TREE, + TREE_VALUE (TREE_CHAIN (arglist))); + arglist = tree_cons (NULL_TREE, + build_int_cst (NULL_TREE, p[0], 0), + arglist); + fn = fn_fputc; + break; + } + } + /* FALLTHROUGH */ + case 1: /* length is greater than 1, call fwrite. */ + { + tree string_arg; + + /* If optimizing for size keep fputs. */ + if (optimize_size) + return 0; + string_arg = TREE_VALUE (arglist); + /* New argument list transforming fputs(string, stream) to + fwrite(string, 1, len, stream). */ + arglist = build_tree_list (NULL_TREE, + TREE_VALUE (TREE_CHAIN (arglist))); + arglist = tree_cons (NULL_TREE, len, arglist); + arglist = tree_cons (NULL_TREE, size_one_node, arglist); + arglist = tree_cons (NULL_TREE, string_arg, arglist); + fn = fn_fwrite; + break; + } + default: + abort (); + } + + /* These optimizations are only performed when the result is ignored, + hence there's no need to cast the result to integer_type_node. */ + return build_function_call_expr (fn, arglist); +} + +static void +simplify_builtin_va_start (tree arglist) +{ + tree chain = TREE_CHAIN (arglist); + + if (TREE_CHAIN (chain)) + error ("too many arguments to function `va_start'"); + + simplify_builtin_next_arg (chain); +} + +static void +simplify_builtin_next_arg (tree arglist) +{ + tree fntype = TREE_TYPE (current_function_decl); + + if (TYPE_ARG_TYPES (fntype) == 0 + || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) + == void_type_node)) + error ("`va_start' used in function with fixed args"); + else if (arglist) + { + tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl)); + tree arg = TREE_VALUE (arglist); + + /* Strip off all nops for the sake of the comparison. This + is not quite the same as STRIP_NOPS. It does more. + We must also strip off INDIRECT_EXPR for C++ reference + parameters. */ + while (TREE_CODE (arg) == NOP_EXPR + || TREE_CODE (arg) == CONVERT_EXPR + || TREE_CODE (arg) == NON_LVALUE_EXPR + || TREE_CODE (arg) == INDIRECT_REF) + arg = TREE_OPERAND (arg, 0); + if (arg != last_parm) + warning ("second parameter of `va_start' not last named argument"); + TREE_VALUE (arglist) = arg; + } + else + /* Evidently an out of date version of ; can't validate + va_start's second argument, but can still work as intended. */ + warning ("`__builtin_next_arg' called without an argument"); +} + + +/* Simplify a call to the sprintf builtin. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. If IGNORED is true, it means that + the caller does not use the returned value of the function. */ + +static tree +simplify_builtin_sprintf (tree arglist, int ignored) +{ + tree call, retval, dest, fmt; + const char *fmt_str = NULL; + + /* Verify the required arguments in the original call. We deal with two + types of sprintf() calls: 'sprintf (str, fmt)' and + 'sprintf (dest, "%s", orig)'. */ + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE) + && !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE, + VOID_TYPE)) + return NULL_TREE; + + /* Get the destination string and the format specifier. */ + dest = TREE_VALUE (arglist); + fmt = TREE_VALUE (TREE_CHAIN (arglist)); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return NULL_TREE; + + call = NULL_TREE; + retval = NULL_TREE; + + /* If the format doesn't contain % args or %%, use strcpy. */ + if (strchr (fmt_str, '%') == NULL) + { + tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + + if (!fn) + return NULL_TREE; + + /* Convert sprintf (str, fmt) into strcpy (str, fmt) when + 'format' is known to contain no % formats. */ + arglist = build_tree_list (NULL_TREE, fmt); + arglist = tree_cons (NULL_TREE, dest, arglist); + call = build_function_call_expr (fn, arglist); + if (!ignored) + retval = build_int_cst (NULL_TREE, strlen (fmt_str), 0); + } + + /* If the format is "%s", use strcpy if the result isn't used. */ + else if (fmt_str && strcmp (fmt_str, "%s") == 0) + { + tree fn, orig; + fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + + if (!fn) + return NULL_TREE; + + /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2). */ + orig = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + arglist = build_tree_list (NULL_TREE, orig); + arglist = tree_cons (NULL_TREE, dest, arglist); + if (!ignored) + { + retval = c_strlen (orig, 1); + if (!retval || TREE_CODE (retval) != INTEGER_CST) + return NULL_TREE; + } + call = build_function_call_expr (fn, arglist); + } + + if (call && retval) + { + retval = convert + (TREE_TYPE (TREE_TYPE (implicit_built_in_decls[BUILT_IN_SPRINTF])), + retval); + return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval); + } + else + return call; +}