diff options
author | jakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-06-27 12:17:39 +0000 |
---|---|---|
committer | jakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-06-27 12:17:39 +0000 |
commit | 0a39fd5408943300a03d40afe1f6e5b59ae31816 (patch) | |
tree | bb3939873bf2cf5cb7bd56f678141658affb0021 /gcc/builtins.c | |
parent | f68ab9c535c26d62063f272878415869effc0ef6 (diff) | |
download | gcc-0a39fd5408943300a03d40afe1f6e5b59ae31816.tar.gz |
* builtin-attrs.def (DEF_ATTR_FOR_INT): Add for 5 and 6.
(DEF_LIST_INT_INT): Add for 4,0, 4,5, 5,0, 5,6.
(ATTR_NOTHROW_NONNULL_4, ATTR_NOTHROW_NONNULL_5): Define.
(ATTR_FORMAT_PRINTF_4_0, ATTR_FORMAT_PRINTF_4_5,
ATTR_FORMAT_PRINTF_5_0, ATTR_FORMAT_PRINTF_5_6): Define.
* builtins.c: Include tree-flow.h.
(expand_builtin_mempcpy, expand_builtin_memmove): Comment fixes.
(expand_builtin_object_size, expand_builtin_memory_chk,
maybe_emit_chk_warning, maybe_emit_sprintf_chk_warning,
compute_object_offset, compute_builtin_object_size,
fold_builtin_object_size): New functions.
(expand_builtin): Handle BUILT_IN_OBJECT_SIZE and BUILT_IN_*_CHK.
(fold_builtin_1): Likewise. Handle BUILT_IN_{,V}{,F}PRINTF
and BUILT_IN_{,F}PRINTF_UNLOCKED.
(fold_builtin_memory_chk, fold_builtin_stxcpy_chk,
fold_builtin_strncpy_chk, fold_builtin_strcat_chk,
fold_builtin_strncat_chk, fold_builtin_sprintf_chk,
fold_builtin_snprintf_chk, fold_builtin_printf, fold_builtin_fprintf):
New functions.
* builtins.def (BUILT_IN_OBJECT_SIZE, BUILT_IN_MEMCPY_CHK,
BUILT_IN_MEMMOVE_CHK, BUILT_IN_MEMPCPY_CHK, BUILT_IN_MEMSET_CHK,
BUILT_IN_STPCPY_CHK, BUILT_IN_STRCAT_CHK, BUILT_IN_STRCPY_CHK,
BUILT_IN_STRNCAT_CHK, BUILT_IN_STRNCPY_CHK, BUILT_IN_SNPRINTF_CHK,
BUILT_IN_SPRINTF_CHK, BUILT_IN_VSNPRINTF_CHK, BUILT_IN_VSPRINTF_CHK,
BUILT_IN_FPRINTF_CHK, BUILT_IN_PRINTF_CHK, BUILT_IN_VFPRINTF_CHK,
BUILT_IN_VPRINTF_CHK): New builtins.
* builtin-types.def (DEF_FUNCTION_TYPE_5, DEF_FUNCTION_TYPE_VAR_4):
Document.
(BT_FN_SIZE_CONST_PTR_INT, BT_FN_INT_INT_CONST_STRING_VALIST_ARG,
BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, BT_FN_PTR_PTR_INT_SIZE_SIZE,
BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE,
BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG,
BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG,
BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG,
BT_FN_INT_INT_CONST_STRING_VAR, BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR,
BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR,
BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR): New types.
* c-common.c (DEF_FUNCTION_TYPE_5, DEF_FUNCTION_TYPE_6,
DEF_FUNCTION_TYPE_VAR_4, DEF_FUNCTION_TYPE_VAR_5): Define.
* Makefile.in (OBJS-common): Add tree-object-size.o.
(tree-object-size.o): Add dependencies.
* tree-pass.h (pass_object_sizes): Add.
* tree-optimize.c (init_tree_optimization_passes): Add
pass_object_sizes.
* tree-object-size.c: New file.
* tree.h (fold_builtin_memory_chk, fold_builtin_stxcpy_chk,
fold_builtin_strncpy_chk, fold_builtin_snprintf_chk,
compute_builtin_object_size, init_object_sizes, fini_object_sizes):
New prototypes.
* tree-ssa-ccp.c (get_strlen): Rename to ...
(get_maxval_strlen): ...this function. Handle also computing of maximum
string length and maximum integral value.
(ccp_fold_builtin): Handle BUILT_IN_*_CHK. Use get_maxval_strlen
instead of get_strlen. Pass CALLEE and ARGLIST variables to the
folding functions instead of computing them again.
(execute_fold_all_builtins): Retry ccp_fold_builtin if a builtin changed
into some other builtin.
* doc/extend.texi (Object Size Checking): Document.
* gcc.c-torture/execute/builtins/lib/main.c (abort): Add prototype.
* gcc.c-torture/execute/builtins/lib/strncat.c (strncat): Avoid
testing uninitialized var.
* gcc.c-torture/execute/builtins/chk.h: New.
* gcc.c-torture/execute/builtins/lib/chk.c: New.
* gcc.c-torture/execute/builtins/memcpy-chk.c: New test.
* gcc.c-torture/execute/builtins/memcpy-chk-lib.c: New.
* gcc.c-torture/execute/builtins/memmove-chk.c: New test.
* gcc.c-torture/execute/builtins/memmove-chk-lib.c: New.
* gcc.c-torture/execute/builtins/mempcpy-chk.c: New test.
* gcc.c-torture/execute/builtins/mempcpy-chk-lib.c: New.
* gcc.c-torture/execute/builtins/memset-chk.c: New test.
* gcc.c-torture/execute/builtins/memset-chk-lib.c: New.
* gcc.c-torture/execute/builtins/snprintf-chk.c: New test.
* gcc.c-torture/execute/builtins/snprintf-chk-lib.c: New.
* gcc.c-torture/execute/builtins/sprintf-chk.c: New test.
* gcc.c-torture/execute/builtins/sprintf-chk-lib.c: New.
* gcc.c-torture/execute/builtins/stpcpy-chk.c: New test.
* gcc.c-torture/execute/builtins/stpcpy-chk-lib.c: New.
* gcc.c-torture/execute/builtins/strcat-chk.c: New test.
* gcc.c-torture/execute/builtins/strcat-chk-lib.c: New.
* gcc.c-torture/execute/builtins/strcpy-chk.c: New test.
* gcc.c-torture/execute/builtins/strcpy-chk-lib.c: New.
* gcc.c-torture/execute/builtins/strncat-chk.c: New test.
* gcc.c-torture/execute/builtins/strncat-chk-lib.c: New.
* gcc.c-torture/execute/builtins/strncpy-chk.c: New test.
* gcc.c-torture/execute/builtins/strncpy-chk-lib.c: New.
* gcc.c-torture/execute/builtins/vsnprintf-chk.c: New test.
* gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c: New.
* gcc.c-torture/execute/builtins/vsprintf-chk.c: New test.
* gcc.c-torture/execute/builtins/vsprintf-chk-lib.c: New.
* gcc.dg/builtin-object-size-1.c: New test.
* gcc.dg/builtin-object-size-2.c: New test.
* gcc.dg/builtin-object-size-3.c: New test.
* gcc.dg/builtin-object-size-4.c: New test.
* gcc.dg/builtin-object-size-5.c: New test.
* gcc.dg/builtin-stringop-chk-1.c: New test.
* gcc.dg/builtin-stringop-chk-2.c: New test.
* gcc.dg/tree-ssa/builtin-fprintf-1.c: New test.
* gcc.dg/tree-ssa/builtin-fprintf-chk-1.c: New test.
* gcc.dg/tree-ssa/builtin-printf-1.c: New test.
* gcc.dg/tree-ssa/builtin-printf-chk-1.c: New test.
* gcc.dg/tree-ssa/builtin-vfprintf-1.c: New test.
* gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c: New test.
* gcc.dg/tree-ssa/builtin-vprintf-1.c: New test.
* gcc.dg/tree-ssa/builtin-vprintf-chk-1.c: New test.
* gcc.c-torture/execute/printf-1.c: New test.
* gcc.c-torture/execute/fprintf-1.c: New test.
* gcc.c-torture/execute/vprintf-1.c: New test.
* gcc.c-torture/execute/vfprintf-1.c: New test.
* gcc.c-torture/execute/printf-chk-1.c: New test.
* gcc.c-torture/execute/fprintf-chk-1.c: New test.
* gcc.c-torture/execute/vprintf-chk-1.c: New test.
* gcc.c-torture/execute/vfprintf-chk-1.c: New test.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@101352 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r-- | gcc/builtins.c | 1229 |
1 files changed, 1225 insertions, 4 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c index c966c0e0da3..313170bbfbb 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -188,6 +188,18 @@ static tree fold_builtin_strspn (tree); static tree fold_builtin_strcspn (tree); static tree fold_builtin_sprintf (tree, int); +static rtx expand_builtin_object_size (tree); +static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode, + enum built_in_function); +static void maybe_emit_chk_warning (tree, enum built_in_function); +static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function); +static tree fold_builtin_object_size (tree); +static tree fold_builtin_strcat_chk (tree, tree); +static tree fold_builtin_strncat_chk (tree, tree); +static tree fold_builtin_sprintf_chk (tree, enum built_in_function); +static tree fold_builtin_printf (tree, tree, bool, enum built_in_function); +static tree fold_builtin_fprintf (tree, tree, bool, enum built_in_function); + /* Return true if NODE should be considered for inline expansion regardless of the optimization level. This means whenever a function is invoked with its "internal" name, which normally contains the prefix "__builtin". */ @@ -2821,7 +2833,7 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode) } /* Expand a call to the mempcpy builtin, with arguments in ARGLIST. - Return 0 if we failed the caller should emit a normal call, + Return 0 if we failed; the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). If ENDP is 0 return the destination pointer, if ENDP is 1 return the end pointer ala @@ -2912,7 +2924,7 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m } /* Expand expression EXP, which is a call to the memmove builtin. Return 0 - if we failed the caller should emit a normal call. */ + if we failed; the caller should emit a normal call. */ static rtx expand_builtin_memmove (tree arglist, tree type, rtx target, @@ -6238,6 +6250,32 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_builtin_synchronize (); return const0_rtx; + case BUILT_IN_OBJECT_SIZE: + return expand_builtin_object_size (exp); + + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + target = expand_builtin_memory_chk (exp, target, mode, fcode); + if (target) + return target; + break; + + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_STRCAT_CHK: + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + maybe_emit_chk_warning (exp, fcode); + break; + + case BUILT_IN_SPRINTF_CHK: + case BUILT_IN_VSPRINTF_CHK: + maybe_emit_sprintf_chk_warning (exp, fcode); + break; + default: /* just do library call, if unknown builtin */ break; } @@ -8787,6 +8825,48 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore) case BUILT_IN_VA_START: break; + case BUILT_IN_OBJECT_SIZE: + return fold_builtin_object_size (arglist); + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + return fold_builtin_memory_chk (fndecl, arglist, NULL_TREE, ignore, + DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + return fold_builtin_stxcpy_chk (fndecl, arglist, NULL_TREE, ignore, + DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_STRNCPY_CHK: + return fold_builtin_strncpy_chk (arglist, NULL_TREE); + case BUILT_IN_STRCAT_CHK: + return fold_builtin_strcat_chk (fndecl, arglist); + case BUILT_IN_STRNCAT_CHK: + return fold_builtin_strncat_chk (fndecl, arglist); + case BUILT_IN_SPRINTF_CHK: + case BUILT_IN_VSPRINTF_CHK: + return fold_builtin_sprintf_chk (arglist, DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + return fold_builtin_snprintf_chk (arglist, NULL_TREE, + DECL_FUNCTION_CODE (fndecl)); + + case BUILT_IN_PRINTF: + case BUILT_IN_PRINTF_UNLOCKED: + case BUILT_IN_VPRINTF: + case BUILT_IN_PRINTF_CHK: + case BUILT_IN_VPRINTF_CHK: + return fold_builtin_printf (fndecl, arglist, ignore, + DECL_FUNCTION_CODE (fndecl)); + + case BUILT_IN_FPRINTF: + case BUILT_IN_FPRINTF_UNLOCKED: + case BUILT_IN_VFPRINTF: + case BUILT_IN_FPRINTF_CHK: + case BUILT_IN_VFPRINTF_CHK: + return fold_builtin_fprintf (fndecl, arglist, ignore, + DECL_FUNCTION_CODE (fndecl)); + default: break; } @@ -9238,7 +9318,7 @@ fold_builtin_strncat (tree arglist) const char *p = c_getstr (src); /* If the requested length is zero, or the src parameter string - length is zero, return the dst parameter. */ + length is zero, return the dst parameter. */ if (integer_zerop (len) || (p && *p == '\0')) return omit_two_operands (TREE_TYPE (dst), dst, src, len); @@ -9535,7 +9615,7 @@ fold_builtin_sprintf (tree arglist, int ignored) 'sprintf (dest, "%s", orig)'. */ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE) && !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE, - VOID_TYPE)) + VOID_TYPE)) return NULL_TREE; /* Get the destination string and the format specifier. */ @@ -9599,3 +9679,1144 @@ fold_builtin_sprintf (tree arglist, int ignored) else return call; } + +/* Expand a call to __builtin_object_size. */ + +rtx +expand_builtin_object_size (tree exp) +{ + tree ost; + int object_size_type; + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + location_t locus = EXPR_LOCATION (exp); + + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + { + error ("%Hfirst argument of %D must be a pointer, second integer constant", + &locus, fndecl); + expand_builtin_trap (); + return const0_rtx; + } + + ost = TREE_VALUE (TREE_CHAIN (arglist)); + STRIP_NOPS (ost); + + if (TREE_CODE (ost) != INTEGER_CST + || tree_int_cst_sgn (ost) < 0 + || compare_tree_int (ost, 3) > 0) + { + error ("%Hlast argument of %D is not integer constant between 0 and 3", + &locus, fndecl); + expand_builtin_trap (); + return const0_rtx; + } + + object_size_type = tree_low_cst (ost, 0); + + return object_size_type < 2 ? constm1_rtx : const0_rtx; +} + +/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin. + FCODE is the BUILT_IN_* to use. + Return 0 if we failed; the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in + mode MODE if that's convenient). */ + +static rtx +expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, + enum built_in_function fcode) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, src, len, size; + + if (!validate_arglist (arglist, + POINTER_TYPE, + fcode == BUILT_IN_MEMSET_CHK + ? INTEGER_TYPE : POINTER_TYPE, + INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + if (! host_integerp (size, 1)) + return 0; + + if (host_integerp (len, 1) || integer_all_onesp (size)) + { + tree fn; + + if (! integer_all_onesp (size) && tree_int_cst_lt (size, len)) + { + location_t locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + fn = NULL_TREE; + /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume + mem{cpy,pcpy,move,set} is available. */ + switch (fcode) + { + case BUILT_IN_MEMCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMCPY]; + break; + case BUILT_IN_MEMPCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMPCPY]; + break; + case BUILT_IN_MEMMOVE_CHK: + fn = built_in_decls[BUILT_IN_MEMMOVE]; + break; + case BUILT_IN_MEMSET_CHK: + fn = built_in_decls[BUILT_IN_MEMSET]; + break; + default: + break; + } + + if (! fn) + return 0; + + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); + } + else if (fcode == BUILT_IN_MEMSET_CHK) + return 0; + else + { + unsigned int dest_align + = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + + /* If DEST is not a pointer type, call the normal function. */ + if (dest_align == 0) + return 0; + + /* If SRC and DEST are the same (and not volatile), do nothing. */ + if (operand_equal_p (src, dest, 0)) + { + tree expr; + + if (fcode != BUILT_IN_MEMPCPY_CHK) + { + /* Evaluate and ignore LEN in case it has side-effects. */ + expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); + return expand_expr (dest, target, mode, EXPAND_NORMAL); + } + + len = fold_convert (TREE_TYPE (dest), len); + expr = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len); + return expand_expr (expr, target, mode, EXPAND_NORMAL); + } + + /* __memmove_chk special case. */ + if (fcode == BUILT_IN_MEMMOVE_CHK) + { + unsigned int src_align + = get_pointer_alignment (src, BIGGEST_ALIGNMENT); + + if (src_align == 0) + return 0; + + /* If src is categorized for a readonly section we can use + normal __memcpy_chk. */ + if (readonly_data_expr (src)) + { + tree fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); + } + } + return 0; + } +} + +/* Emit warning if a buffer overflow is detected at compile time. */ + +static void +maybe_emit_chk_warning (tree exp, enum built_in_function fcode) +{ + int arg_mask, is_strlen = 0; + tree arglist = TREE_OPERAND (exp, 1), a; + tree len, size; + location_t locus; + + switch (fcode) + { + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + /* For __strcat_chk the warning will be emitted only if overflowing + by at least strlen (dest) + 1 bytes. */ + case BUILT_IN_STRCAT_CHK: + arg_mask = 6; + is_strlen = 1; + break; + case BUILT_IN_STRNCPY_CHK: + arg_mask = 12; + break; + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + arg_mask = 10; + break; + default: + gcc_unreachable (); + } + + len = NULL_TREE; + size = NULL_TREE; + for (a = arglist; a && arg_mask; a = TREE_CHAIN (a), arg_mask >>= 1) + if (arg_mask & 1) + { + if (len) + size = a; + else + len = a; + } + + if (!len || !size) + return; + + len = TREE_VALUE (len); + size = TREE_VALUE (size); + + if (! host_integerp (size, 1) || integer_all_onesp (size)) + return; + + if (is_strlen) + { + len = c_strlen (len, 1); + if (! len || ! host_integerp (len, 1) || tree_int_cst_lt (len, size)) + return; + } + else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len)) + return; + + locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); +} + +/* Emit warning if a buffer overflow is detected at compile time + in __sprintf_chk/__vsprintf_chk calls. */ + +static void +maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, size, len, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return; + dest = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + flag = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + size = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + fmt = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1) || integer_all_onesp (size)) + return; + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return; + + /* If the format doesn't contain % args or %%, we know its size. */ + if (strchr (fmt_str, '%') == 0) + len = build_int_cstu (size_type_node, strlen (fmt_str)); + /* If the format is "%s" and first ... argument is a string literal, + we know it too. */ + else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, "%s") == 0) + { + tree arg; + + if (! arglist) + return; + arg = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (arg))) + return; + + len = c_strlen (arg, 1); + if (!len || ! host_integerp (len, 1)) + return; + } + else + return; + + if (! tree_int_cst_lt (len, size)) + { + location_t locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); + } +} + +/* Fold a call to __builtin_object_size, if possible. */ + +tree +fold_builtin_object_size (tree arglist) +{ + tree ptr, ost, ret = 0; + int object_size_type; + + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + ptr = TREE_VALUE (arglist); + ost = TREE_VALUE (TREE_CHAIN (arglist)); + STRIP_NOPS (ost); + + if (TREE_CODE (ost) != INTEGER_CST + || tree_int_cst_sgn (ost) < 0 + || compare_tree_int (ost, 3) > 0) + return 0; + + object_size_type = tree_low_cst (ost, 0); + + /* __builtin_object_size doesn't evaluate side-effects in its arguments; + if there are any side-effects, it returns (size_t) -1 for types 0 and 1 + and (size_t) 0 for types 2 and 3. */ + if (TREE_SIDE_EFFECTS (ptr)) + return fold_convert (size_type_node, + object_size_type < 2 + ? integer_minus_one_node : integer_zero_node); + + if (TREE_CODE (ptr) == ADDR_EXPR) + ret = build_int_cstu (size_type_node, + compute_builtin_object_size (ptr, object_size_type)); + + else if (TREE_CODE (ptr) == SSA_NAME) + { + unsigned HOST_WIDE_INT bytes; + + /* If object size is not known yet, delay folding until + later. Maybe subsequent passes will help determining + it. */ + bytes = compute_builtin_object_size (ptr, object_size_type); + if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 + ? -1 : 0)) + ret = build_int_cstu (size_type_node, bytes); + } + + if (ret) + { + ret = force_fit_type (ret, -1, false, false); + if (TREE_CONSTANT_OVERFLOW (ret)) + ret = 0; + } + + return ret; +} + +/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin. + IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* + code of the builtin. If MAXLEN is not NULL, it is maximum length + passed as third argument. */ + +tree +fold_builtin_memory_chk (tree fndecl, tree arglist, tree maxlen, bool ignore, + enum built_in_function fcode) +{ + tree dest, src, len, size, fn; + + if (!validate_arglist (arglist, + POINTER_TYPE, + fcode == BUILT_IN_MEMSET_CHK + ? INTEGER_TYPE : POINTER_TYPE, + INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + /* Actually val for __memset_chk, but it doesn't matter. */ + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + /* If SRC and DEST are the same (and not volatile), return DEST + (resp. DEST+LEN for __mempcpy_chk). */ + if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0)) + { + if (fcode != BUILT_IN_MEMPCPY_CHK) + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); + else + { + tree temp = fold_convert (TREE_TYPE (dest), len); + temp = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), temp); + } + } + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + { + if (fcode == BUILT_IN_MEMPCPY_CHK && ignore) + { + /* (void) __mempcpy_chk () can be optimized into + (void) __memcpy_chk (). */ + fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); + } + return 0; + } + len = maxlen; + } + + if (tree_int_cst_lt (size, len)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + fn = NULL_TREE; + /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume + mem{cpy,pcpy,move,set} is available. */ + switch (fcode) + { + case BUILT_IN_MEMCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMCPY]; + break; + case BUILT_IN_MEMPCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMPCPY]; + break; + case BUILT_IN_MEMMOVE_CHK: + fn = built_in_decls[BUILT_IN_MEMMOVE]; + break; + case BUILT_IN_MEMSET_CHK: + fn = built_in_decls[BUILT_IN_MEMSET]; + break; + default: + break; + } + + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __st[rp]cpy_chk builtin. + IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* + code of the builtin. If MAXLEN is not NULL, it is maximum length of + strings passed as second argument. */ + +tree +fold_builtin_stxcpy_chk (tree fndecl, tree arglist, tree maxlen, bool ignore, + enum built_in_function fcode) +{ + tree dest, src, size, len, fn; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* If SRC and DEST are the same (and not volatile), return DEST. */ + if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0)) + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + len = c_strlen (src, 1); + if (! len || ! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + { + if (fcode == BUILT_IN_STPCPY_CHK) + { + if (! ignore) + return 0; + + /* If return value of __stpcpy_chk is ignored, + optimize into __strcpy_chk. */ + fn = built_in_decls[BUILT_IN_STRCPY_CHK]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); + } + + if (! len || TREE_SIDE_EFFECTS (len)) + return 0; + + /* If c_strlen returned something, but not a constant, + transform __strcpy_chk into __memcpy_chk. */ + fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + + len = size_binop (PLUS_EXPR, len, ssize_int (1)); + arglist = build_tree_list (NULL_TREE, size); + arglist = tree_cons (NULL_TREE, len, arglist); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), + build_function_call_expr (fn, arglist)); + } + len = maxlen; + } + + if (! tree_int_cst_lt (len, size)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, src); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */ + fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK + ? BUILT_IN_STPCPY : BUILT_IN_STRCPY]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strncpy_chk builtin. + If MAXLEN is not NULL, it is maximum length passed as third argument. */ + +tree +fold_builtin_strncpy_chk (tree arglist, tree maxlen) +{ + tree dest, src, size, len, fn; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + return 0; + len = maxlen; + } + + if (tree_int_cst_lt (size, len)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strncpy_chk is used, assume strncpy is available. */ + fn = built_in_decls[BUILT_IN_STRNCPY]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strcat_chk builtin FNDECL with ARGLIST. */ + +static tree +fold_builtin_strcat_chk (tree fndecl, tree arglist) +{ + tree dest, src, size, fn; + const char *p; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + p = c_getstr (src); + /* If the SRC parameter is "", return DEST. */ + if (p && *p == '\0') + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); + + if (! host_integerp (size, 1) || ! integer_all_onesp (size)) + return 0; + + arglist = build_tree_list (NULL_TREE, src); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strcat_chk is used, assume strcat is available. */ + fn = built_in_decls[BUILT_IN_STRCAT]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strncat_chk builtin EXP. */ + +static tree +fold_builtin_strncat_chk (tree fndecl, tree arglist) +{ + tree dest, src, size, len, fn; + const char *p; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + p = c_getstr (src); + /* If the SRC parameter is "" or if LEN is 0, return DEST. */ + if (p && *p == '\0') + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); + else if (integer_zerop (len)) + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + tree src_len = c_strlen (src, 1); + if (src_len + && host_integerp (src_len, 1) + && host_integerp (len, 1) + && ! tree_int_cst_lt (len, src_len)) + { + /* If LEN >= strlen (SRC), optimize into __strcat_chk. */ + fn = built_in_decls[BUILT_IN_STRCAT_CHK]; + if (!fn) + return 0; + + arglist = build_tree_list (NULL_TREE, size); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + return build_function_call_expr (fn, arglist); + } + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strncat_chk is used, assume strncat is available. */ + fn = built_in_decls[BUILT_IN_STRNCAT]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to __{,v}sprintf_chk with argument list ARGLIST. Return 0 if + a normal call should be emitted rather than expanding the function + inline. FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK. */ + +static tree +fold_builtin_sprintf_chk (tree arglist, enum built_in_function fcode) +{ + tree dest, size, len, fn, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + dest = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (dest))) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + size = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1)) + return 0; + + len = NULL_TREE; + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str != NULL) + { + /* If the format doesn't contain % args or %%, we know the size. */ + if (strchr (fmt_str, '%') == 0) + { + if (fcode != BUILT_IN_SPRINTF_CHK || arglist == NULL_TREE) + len = build_int_cstu (size_type_node, strlen (fmt_str)); + } + /* If the format is "%s" and first ... argument is a string literal, + we know the size too. */ + else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, "%s") == 0) + { + tree arg; + + if (arglist && !TREE_CHAIN (arglist)) + { + arg = TREE_VALUE (arglist); + if (POINTER_TYPE_P (TREE_TYPE (arg))) + { + len = c_strlen (arg, 1); + if (! len || ! host_integerp (len, 1)) + len = NULL_TREE; + } + } + } + } + + if (! integer_all_onesp (size)) + { + if (! len || ! tree_int_cst_lt (len, size)) + return 0; + } + + /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0 + or if format doesn't contain % chars or is "%s". */ + if (! integer_zerop (flag)) + { + if (fmt_str == NULL) + return 0; + if (strchr (fmt_str, '%') != NULL && strcmp (fmt_str, "%s")) + return 0; + } + + arglist = tree_cons (NULL_TREE, fmt, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */ + fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK + ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to {,v}snprintf with argument list ARGLIST. Return 0 if + a normal call should be emitted rather than expanding the function + inline. FCODE is either BUILT_IN_SNPRINTF_CHK or + BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length + passed as second argument. */ + +tree +fold_builtin_snprintf_chk (tree arglist, tree maxlen, + enum built_in_function fcode) +{ + tree dest, size, len, fn, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + dest = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (dest))) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + len = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + size = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + return 0; + len = maxlen; + } + + if (tree_int_cst_lt (size, len)) + return 0; + } + + /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0 + or if format doesn't contain % chars or is "%s". */ + if (! integer_zerop (flag)) + { + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return 0; + if (strchr (fmt_str, '%') != NULL && strcmp (fmt_str, "%s")) + return 0; + } + + arglist = tree_cons (NULL_TREE, fmt, arglist); + arglist = tree_cons (NULL_TREE, len, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is + available. */ + fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK + ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. FCODE is the BUILT_IN_* + code of the function to be simplified. */ + +static tree +fold_builtin_printf (tree fndecl, tree arglist, bool ignore, + enum built_in_function fcode) +{ + tree fmt, fn = NULL_TREE, fn_putchar, fn_puts, arg, call; + const char *fmt_str = NULL; + + /* If the return value is used, don't do the transformation. */ + if (! ignore) + return 0; + + /* Verify the required arguments in the original call. */ + if (fcode == BUILT_IN_PRINTF_CHK || fcode == BUILT_IN_VPRINTF_CHK) + { + tree flag; + + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE + || TREE_SIDE_EFFECTS (flag)) + return 0; + arglist = TREE_CHAIN (arglist); + } + + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return NULL_TREE; + + if (fcode == BUILT_IN_PRINTF_UNLOCKED) + { + fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]; + fn_puts = implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED]; + } + else + { + fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR]; + fn_puts = implicit_built_in_decls[BUILT_IN_PUTS]; + } + + if (strcmp (fmt_str, "%s") == 0 || strchr (fmt_str, '%') == NULL) + { + const char *str; + + if (strcmp (fmt_str, "%s") == 0) + { + if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) + return 0; + + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + + str = c_getstr (TREE_VALUE (arglist)); + if (str == NULL) + return 0; + } + else + { + /* The format specifier doesn't contain any '%' characters. */ + if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK + && arglist) + return 0; + str = fmt_str; + } + + /* If the string was "", printf does nothing. */ + if (str[0] == '\0') + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); + + /* If the string has length of 1, call putchar. */ + if (str[1] == '\0') + { + /* Given printf("c"), (where c is any one character,) + convert "c"[0] to an int and pass that to the replacement + function. */ + arg = build_int_cst (NULL_TREE, str[0]); + arglist = build_tree_list (NULL_TREE, arg); + fn = fn_putchar; + } + else + { + /* If the string was "string\n", call puts("string"). */ + size_t len = strlen (str); + if (str[len - 1] == '\n') + { + /* Create a NUL-terminated string that's one char shorter + than the original, stripping off the trailing '\n'. */ + char *newstr = alloca (len); + memcpy (newstr, str, len - 1); + newstr[len - 1] = 0; + + arg = build_string_literal (len, newstr); + arglist = build_tree_list (NULL_TREE, arg); + fn = fn_puts; + } + else + /* We'd like to arrange to call fputs(string,stdout) here, + but we need stdout and don't have a way to get it yet. */ + return 0; + } + } + + /* The other optimizations can be done only on the non-va_list variants. */ + else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) + return 0; + + /* If the format specifier was "%s\n", call __builtin_puts(arg). */ + else if (strcmp (fmt_str, "%s\n") == 0) + { + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + fn = fn_puts; + } + + /* If the format specifier was "%c", call __builtin_putchar(arg). */ + else if (strcmp (fmt_str, "%c") == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE + || TREE_CHAIN (arglist)) + return 0; + fn = fn_putchar; + } + + if (!fn) + return 0; + + call = build_function_call_expr (fn, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call); +} + +/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. FCODE is the BUILT_IN_* + code of the function to be simplified. */ + +static tree +fold_builtin_fprintf (tree fndecl, tree arglist, bool ignore, + enum built_in_function fcode) +{ + tree fp, fmt, fn = NULL_TREE, fn_fputc, fn_fputs, arg, call; + const char *fmt_str = NULL; + + /* If the return value is used, don't do the transformation. */ + if (! ignore) + return 0; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + fp = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fp))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (fcode == BUILT_IN_FPRINTF_CHK || fcode == BUILT_IN_VFPRINTF_CHK) + { + tree flag; + + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE + || TREE_SIDE_EFFECTS (flag)) + return 0; + arglist = TREE_CHAIN (arglist); + } + + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return NULL_TREE; + + if (fcode == BUILT_IN_FPRINTF_UNLOCKED) + { + fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]; + fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED]; + } + else + { + fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC]; + fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS]; + } + + /* If the format doesn't contain % args or %%, use strcpy. */ + if (strchr (fmt_str, '%') == NULL) + { + if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK + && arglist) + return 0; + + /* If the format specifier was "", fprintf does nothing. */ + if (fmt_str[0] == '\0') + { + /* If FP has side-effects, just wait until gimplification is + done. */ + if (TREE_SIDE_EFFECTS (fp)) + return 0; + + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); + } + + /* When "string" doesn't contain %, replace all cases of + fprintf (fp, string) with fputs (string, fp). The fputs + builtin will take care of special cases like length == 1. */ + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, fmt, arglist); + fn = fn_fputs; + } + + /* The other optimizations can be done only on the non-va_list variants. */ + else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK) + return 0; + + /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */ + else if (strcmp (fmt_str, "%s") == 0) + { + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, arg, arglist); + fn = fn_fputs; + } + + /* If the format specifier was "%c", call __builtin_fputc (arg, fp). */ + else if (strcmp (fmt_str, "%c") == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE + || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, arg, arglist); + fn = fn_fputc; + } + + if (!fn) + return 0; + + call = build_function_call_expr (fn, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call); +} |