summaryrefslogtreecommitdiff
path: root/gcc/builtins.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r--gcc/builtins.c1521
1 files changed, 1441 insertions, 80 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 33fca2ab32e..b0375c377aa 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -68,11 +68,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 +95,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);
@@ -163,6 +161,22 @@ static tree fold_builtin_strcmp (tree);
static tree fold_builtin_strncmp (tree);
static tree fold_builtin_signbit (tree);
+static tree simplify_builtin_memcmp (tree);
+static tree simplify_builtin_strcmp (tree);
+static tree simplify_builtin_strncmp (tree);
+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.
The alignment returned is, by default, the alignment of the thing that
@@ -247,7 +261,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 +421,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). */
@@ -739,6 +768,79 @@ expand_builtin_longjmp (rtx buf_addr, rtx value)
}
}
+/* 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_save_area = expand_expr (t_save_area, NULL_RTX, VOIDmode, 0);
+ 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 (GET_CODE (insn) == JUMP_INSN)
+ {
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO,
+ const0_rtx, REG_NOTES (insn));
+ break;
+ }
+ else if (GET_CODE (insn) == CALL_INSN)
+ break;
+ }
+
+ return const0_rtx;
+}
+
/* __builtin_update_setjmp_buf is passed a pointer to an array of five words
(not all will be used on all machines) that was passed to __builtin_setjmp.
It updates the stack pointer in that block to correspond to the current
@@ -1230,7 +1332,7 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
}
/* 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 +1486,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 +1758,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);
@@ -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);
@@ -3488,8 +3564,8 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
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);
@@ -3659,9 +3735,9 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
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 +3807,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 =
@@ -4401,6 +4477,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;
@@ -4748,6 +4830,7 @@ build_string_literal (int len, const char *str)
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;
@@ -5041,6 +5124,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 (GET_CODE (this) == MEM)
+ 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
@@ -5453,13 +5641,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:
@@ -5481,6 +5670,18 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
return target;
break;
+ case BUILT_IN_STACK_ALLOC:
+ expand_stack_alloc (TREE_VALUE (arglist),
+ TREE_VALUE (TREE_CHAIN (arglist)));
+ return const0_rtx;
+
+ 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:
@@ -5684,6 +5885,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 +5925,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 +5998,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:
@@ -5927,6 +6143,44 @@ 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
@@ -6196,13 +6450,13 @@ 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,
@@ -6995,11 +7249,16 @@ 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 = 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));
+
+ if (in_gimple_form && !TREE_CONSTANT (arg))
+ return NULL_TREE;
+ else
+ return arg;
}
}
@@ -7038,15 +7297,19 @@ fold_builtin_isdigit (tree arglist)
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 = 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. */
-tree
-fold_builtin (tree exp)
+static tree
+fold_builtin_1 (tree exp)
{
tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
@@ -7060,6 +7323,9 @@ fold_builtin (tree exp)
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);
@@ -7557,6 +7823,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)
+{
+ exp = fold_builtin_1 (exp);
+ 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
@@ -7566,7 +7850,7 @@ build_function_call_expr (tree fn, tree arglist)
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, arglist, NULL_TREE);
return fold (call_expr);
}
@@ -7631,31 +7915,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. */
@@ -7669,3 +7928,1105 @@ readonly_data_expr (tree exp)
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 = simplify_builtin_fputs (arglist, ignore, 0, NULL_TREE);
+ break;
+ case BUILT_IN_FPUTS_UNLOCKED:
+ val = simplify_builtin_fputs (arglist, ignore, 1, 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 = simplify_builtin_strcpy (arglist, NULL_TREE);
+ break;
+ case BUILT_IN_STRNCPY:
+ val = simplify_builtin_strncpy (arglist, NULL_TREE);
+ break;
+ case BUILT_IN_STRCMP:
+ val = simplify_builtin_strcmp (arglist);
+ break;
+ case BUILT_IN_STRNCMP:
+ val = simplify_builtin_strncmp (arglist);
+ break;
+ case BUILT_IN_STRPBRK:
+ val = simplify_builtin_strpbrk (arglist);
+ break;
+ case BUILT_IN_BCMP:
+ case BUILT_IN_MEMCMP:
+ val = simplify_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 = 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);
+
+ /* Return an offset into the constant string argument. */
+ if (r == NULL)
+ return integer_zero_node;
+ else
+ return fold (build (PLUS_EXPR, TREE_TYPE (s1),
+ s1, 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_2 (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 integer_zero_node;
+
+ /* Return an offset into the constant string argument. */
+ return fold (build (PLUS_EXPR, TREE_TYPE (s1),
+ s1, 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 integer_zero_node;
+
+ /* Return an offset into the constant string argument. */
+ return fold (build (PLUS_EXPR, TREE_TYPE (s1),
+ s1, 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 integer_zero_node;
+
+ /* Return an offset into the constant string argument. */
+ return fold (build (PLUS_EXPR, TREE_TYPE (s1),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1))));
+ }
+
+ if (p2[0] == '\0')
+ {
+ /* strpbrk(x, "") == NULL.
+ Evaluate and ignore the arguments in case they had
+ side-effects. */
+ return build (COMPOUND_EXPR, integer_type_node, s1,
+ integer_zero_node);
+ }
+
+ 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_2 (p2[0], 0));
+ arglist = tree_cons (NULL_TREE, s1, arglist);
+ return build_function_call_expr (fn, arglist);
+ }
+}
+
+/* Simplify a call to the strcpy 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. */
+
+tree
+simplify_builtin_strcpy (tree arglist, tree len)
+{
+ tree fn;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+
+ fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ if (!fn)
+ return 0;
+
+ if (!len)
+ {
+ len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1);
+ if (!len)
+ return 0;
+ if (TREE_SIDE_EFFECTS (len))
+ return 0;
+ }
+
+ len = size_binop (PLUS_EXPR, len, ssize_int (1));
+ chainon (arglist, build_tree_list (NULL_TREE, len));
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Simplify a call to the strncpy 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. */
+
+tree
+simplify_builtin_strncpy (tree arglist, tree slen)
+{
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ tree fn;
+
+ /* We must be passed a constant len parameter. */
+ if (TREE_CODE (len) != INTEGER_CST)
+ return 0;
+
+ /* If the len parameter is zero, return the dst parameter. */
+ if (integer_zerop (len))
+ {
+ /* Evaluate and ignore the src argument in case it has
+ side-effects and return the dst parameter. */
+ return build (COMPOUND_EXPR, TREE_TYPE (TREE_VALUE (arglist)),
+ TREE_VALUE (TREE_CHAIN (arglist)),
+ TREE_VALUE (arglist));
+ }
+
+ if (!slen)
+ slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 0);
+
+ /* 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 build_function_call_expr (fn, arglist);
+ }
+}
+
+/* Simplify a call to the memcmp 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_memcmp (tree arglist)
+{
+ tree arg1, arg2, len;
+ const char *p1, *p2;
+
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ arg1 = TREE_VALUE (arglist);
+ arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* If the len parameter is zero, return zero. */
+ if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+ {
+ /* Evaluate and ignore arg1 and arg2 in case they have
+ side-effects. */
+ return build (COMPOUND_EXPR, integer_type_node, arg1,
+ build (COMPOUND_EXPR, integer_type_node,
+ arg2, integer_zero_node));
+ }
+
+ 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));
+
+ return (r < 0
+ ? integer_minus_one_node
+ : (r > 0 ? integer_one_node : 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 (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg1))));
+ tree ind2 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg2))));
+ return fold (build (MINUS_EXPR, integer_type_node, ind1, ind2));
+ }
+
+ return 0;
+}
+
+/* Simplify a call to the strcmp 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_strcmp (tree arglist)
+{
+ tree arg1, arg2;
+ const char *p1, *p2;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+
+ arg1 = TREE_VALUE (arglist);
+ arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ /* If both arguments are equal (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return integer_zero_node;
+
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
+
+ if (p1 && p2)
+ {
+ const int i = strcmp (p1, p2);
+ return (i < 0
+ ? integer_minus_one_node
+ : (i > 0 ? integer_one_node : integer_zero_node));
+ }
+
+ /* If either arg is "", return an expression corresponding to
+ (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
+ if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree ind1 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg1))));
+ tree ind2 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg2))));
+ return fold (build (MINUS_EXPR, integer_type_node, ind1, ind2));
+ }
+
+ return 0;
+}
+
+/* Simplify a call to the strncmp 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_strncmp (tree arglist)
+{
+ tree arg1, arg2, arg3;
+ const char *p1, *p2;
+
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ arg1 = TREE_VALUE (arglist);
+ arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* If the len parameter is zero, return zero. */
+ if (integer_zerop (arg3))
+ {
+ /* Evaluate and ignore arg1 and arg2 in case they have
+ side-effects. */
+ return build (COMPOUND_EXPR, integer_type_node, arg1,
+ build (COMPOUND_EXPR, integer_type_node,
+ arg2, integer_zero_node));
+ }
+
+ /* If arg1 and arg2 are equal (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ {
+ /* Evaluate and ignore arg3 in case it has side-effects. */
+ return build (COMPOUND_EXPR, integer_type_node, arg3, integer_zero_node);
+ }
+
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
+
+ /* If all arguments are constant, evaluate at compile-time. */
+ if (host_integerp (arg3, 1) && p1 && p2)
+ {
+ const int r = strncmp (p1, p2, tree_low_cst (arg3, 1));
+ return (r < 0
+ ? integer_minus_one_node
+ : (r > 0 ? integer_one_node : integer_zero_node));
+ }
+
+ /* If len == 1 or (either string parameter is "" and (len >= 1)),
+ return (*(const u_char*)arg1 - *(const u_char*)arg2). */
+ if (host_integerp (arg3, 1)
+ && (tree_low_cst (arg3, 1) == 1
+ || (tree_low_cst (arg3, 1) > 1
+ && ((p1 && *p1 == '\0') || (p2 && *p2 == '\0')))))
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree ind1 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg1))));
+ tree ind2 =
+ fold (build1 (CONVERT_EXPR, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ build1 (NOP_EXPR, cst_uchar_ptr_node, arg2))));
+ return fold (build (MINUS_EXPR, integer_type_node, ind1, ind2));
+ }
+
+ return 0;
+}
+
+/* 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 build (COMPOUND_EXPR, TREE_TYPE (dst), src,
+ build (COMPOUND_EXPR, integer_type_node, len, dst));
+
+ /* 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 build (COMPOUND_EXPR, integer_type_node, s1,
+ build (COMPOUND_EXPR, integer_type_node,
+ s2, integer_zero_node));
+ }
+ 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 build (COMPOUND_EXPR, integer_type_node,
+ s2, integer_zero_node);
+ }
+
+ /* 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;
+ }
+}
+
+/* Simplify a call to the fputs 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.
+
+ If KNOWN_LEN is non-NULL, it represents the known length of the string.
+ This is determined by SSA-CCP in cases where the string itself is not
+ known to be constant but its length is always the same constant. */
+
+tree
+simplify_builtin_fputs (tree arglist, int ignore, int unlocked, tree known_len)
+{
+ tree len, 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;
+
+ len = (known_len) ? known_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 build (COMPOUND_EXPR, integer_type_node,
+ TREE_VALUE (TREE_CHAIN (arglist)), integer_zero_node);
+ }
+ 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_2 (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 ();
+ }
+
+ 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 <stdarg.h>; 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_2 (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 build (COMPOUND_EXPR, TREE_TYPE (retval), call, retval);
+ }
+ else
+ return call;
+}