diff options
author | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-01-21 22:01:24 +0000 |
---|---|---|
committer | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-01-21 22:01:24 +0000 |
commit | ffde65b31066f17eef243be882bb89a6e19370aa (patch) | |
tree | ea876d041c0a63eefccdac5416a8678e75da4cfc /gcc/builtins.c | |
parent | a8c7acc4db08ce7c8ac3ddcb943f9219e2893792 (diff) | |
download | gcc-ffde65b31066f17eef243be882bb89a6e19370aa.tar.gz |
[.]
2015-01-21 Basile Starynkevitch <basile@starynkevitch.net>
{{merged with trunk -i.e. GCC5.0 in stage4- using
svn merge -r209216:219879 svn+ssh://bstarynk@gcc.gnu.org/svn/gcc/trunk
but should probably have used
svn merge -r209216:219879 ^/trunk
we don't use svnmerge.py anymore since our svn is version 1.8.10
}}
VERY UNSTABLE
2015-01-20 Basile Starynkevitch <basile@starynkevitch.net>
Move previous topdir ChangeLog.MELT to ChangeLog.MELT.2008-2014
[contrib/]
2015-01-21 Basile Starynkevitch <basile@starynkevitch.net>
* MELT-Plugin-Makefile: Able to make upgrade-melt as a
plugin. Works for GCC 5.0. Remove GCC 4.7 old stuff.
Move previous contrib/ChangeLog.MELT to ChangeLog.MELT.2008-2014
[gcc/]
2015-01-21 Basile Starynkevitch <basile@starynkevitch.net>
{{merged with trunk -i.e. GCC5.0 in stage4- using
svn merge -r209216:219879 svn+ssh://bstarynk@gcc.gnu.org/svn/gcc/trunk
but should probably have used
svn merge -r209216:219879 ^/trunk
**@@@ UNSTABLE since libmelt-ana-gimple.melt not compiling, but
translator painfully bootstrapping!!@@@@ }}
* toplev.c: Merged manually by keeping MELT extra stuff.
* toplev.h: Likewise.
* gengtype.c: Add "melt-runtime.h" in list, but merged with trunk.
* melt-runtime.h (MELT_VERSION_STRING): Bump to "1.2-pre-merged".
(meltgc_walk_gimple_seq): Remove.
(gt_ggc_mx_gimple_statement_d): Same for GCC 4.9 & 5.0
* melt-runtime.cc: Update copyright year.
(ggc_alloc_cleared_melt_valuevector_st, melt_resize_scangcvect):
Call ggc_internal_cleared_alloc.
(melt_val2passflag): Skip TODO_verify_ssa, TODO_verify_flow,
TODO_verify_stmts, TODO_verify_rtl_sharing for GCC 5.0.
(meltgc_walkstmt_cb, meltgc_walktree_cb)
(melt_tree_walk_frame_size, meltgc_walk_gimple_seq): Remove.
(melt_gt_ggc_mx_gimple_seq_d): Call
gt_ggc_mx_gimple_statement_base.
* melt-build-script.tpl: Update copyright year. Don't symlink
meltrunsup.h anymore.
* melt-build-script.sh: Regenerate.
* melt/warmelt-base.melt: Update copyright year.
(valdesc_object, valdesc_mapobjects, valdesc_mapstrings)
(valdesc_multiple, valdesc_closure, valdesc_routine, valdesc_hook)
(valdesc_bucketlongs, valdesc_jsonobject, valdesc_string)
(valdesc_strbuf, valdesc_pair, valdesc_list, valdesc_int)
(valdesc_double, valdesc_mixint, valdesc_mixloc)
(valdesc_mixbigint, valdesc_real, valdesc_special_data): Use
ggc_internal_alloc & ggc_internal_cleared_alloc for GCC 5.0.
(json_canonical_name): Use ISUPPER, ISALPHA, TOUPPER instead of
their standard <ctype.h> lowercase macros.
* melt/warmelt-modes.melt: Update copyright year.
(generate_runtypesupport_forwcopy_fun): Emit both GCC 4.9 & 5.0
compatible code.
* melt/libmelt-ana-base.melt: Update copyright year.
* melt/libmelt-ana-gimple.melt: TO BE IMPROVED
* melt/generated/*: Painfully regenerated several times thru GCC
4.9 MELT plugin.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/melt-branch@219975 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r-- | gcc/builtins.c | 3460 |
1 files changed, 777 insertions, 2683 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c index dd57b1ae42a..bf5acbcc228 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -1,5 +1,5 @@ /* Expand builtin functions. - Copyright (C) 1988-2014 Free Software Foundation, Inc. + Copyright (C) 1988-2015 Free Software Foundation, Inc. This file is part of GCC. @@ -23,13 +23,27 @@ along with GCC; see the file COPYING3. If not see #include "tm.h" #include "machmode.h" #include "rtl.h" +#include "hash-set.h" +#include "vec.h" +#include "double-int.h" +#include "input.h" +#include "alias.h" +#include "symtab.h" +#include "wide-int.h" +#include "inchash.h" #include "tree.h" +#include "fold-const.h" #include "stringpool.h" #include "stor-layout.h" #include "calls.h" #include "varasm.h" #include "tree-object-size.h" #include "realmpfr.h" +#include "predict.h" +#include "hashtab.h" +#include "hard-reg-set.h" +#include "function.h" +#include "cfgrtl.h" #include "basic-block.h" #include "tree-ssa-alias.h" #include "internal-fn.h" @@ -38,17 +52,23 @@ along with GCC; see the file COPYING3. If not see #include "gimple.h" #include "flags.h" #include "regs.h" -#include "hard-reg-set.h" #include "except.h" -#include "function.h" #include "insn-config.h" +#include "statistics.h" +#include "real.h" +#include "fixed-value.h" +#include "expmed.h" +#include "dojump.h" +#include "explow.h" +#include "emit-rtl.h" +#include "stmt.h" #include "expr.h" +#include "insn-codes.h" #include "optabs.h" #include "libfuncs.h" #include "recog.h" #include "output.h" #include "typeclass.h" -#include "predict.h" #include "tm_p.h" #include "target.h" #include "langhooks.h" @@ -57,8 +77,14 @@ along with GCC; see the file COPYING3. If not see #include "value-prof.h" #include "diagnostic-core.h" #include "builtins.h" -#include "ubsan.h" +#include "asan.h" #include "cilk.h" +#include "ipa-ref.h" +#include "lto-streamer.h" +#include "cgraph.h" +#include "tree-chkp.h" +#include "rtl-chkp.h" +#include "gomp-constants.h" static tree do_mpc_arg1 (tree, tree, int (*)(mpc_ptr, mpc_srcptr, mpc_rnd_t)); @@ -86,8 +112,7 @@ builtin_info_type builtin_info; /* Non-zero if __builtin_constant_p should be folded right away. */ bool force_folding_builtin_constant_p; -static const char *c_getstr (tree); -static rtx c_readstr (const char *, enum machine_mode); +static rtx c_readstr (const char *, machine_mode); static int target_char_cast (tree, char *); static rtx get_memory_rtx (tree, tree); static int apply_args_size (void); @@ -117,25 +142,29 @@ static rtx expand_builtin_next_arg (void); static rtx expand_builtin_va_start (tree); static rtx expand_builtin_va_end (tree); static rtx expand_builtin_va_copy (tree); -static rtx expand_builtin_memcmp (tree, rtx, enum machine_mode); +static rtx expand_builtin_memcmp (tree, rtx, machine_mode); static rtx expand_builtin_strcmp (tree, rtx); -static rtx expand_builtin_strncmp (tree, rtx, enum machine_mode); -static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, enum machine_mode); +static rtx expand_builtin_strncmp (tree, rtx, machine_mode); +static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, machine_mode); static rtx expand_builtin_memcpy (tree, rtx); -static rtx expand_builtin_mempcpy (tree, rtx, enum machine_mode); +static rtx expand_builtin_memcpy_with_bounds (tree, rtx); +static rtx expand_builtin_memcpy_args (tree, tree, tree, rtx, tree); +static rtx expand_builtin_mempcpy (tree, rtx, machine_mode); +static rtx expand_builtin_mempcpy_with_bounds (tree, rtx, machine_mode); static rtx expand_builtin_mempcpy_args (tree, tree, tree, rtx, - enum machine_mode, int); + machine_mode, int, tree); static rtx expand_builtin_strcpy (tree, rtx); static rtx expand_builtin_strcpy_args (tree, tree, rtx); -static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode); +static rtx expand_builtin_stpcpy (tree, rtx, machine_mode); static rtx expand_builtin_strncpy (tree, rtx); -static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode); -static rtx expand_builtin_memset (tree, rtx, enum machine_mode); -static rtx expand_builtin_memset_args (tree, tree, tree, rtx, enum machine_mode, tree); +static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, machine_mode); +static rtx expand_builtin_memset (tree, rtx, machine_mode); +static rtx expand_builtin_memset_with_bounds (tree, rtx, machine_mode); +static rtx expand_builtin_memset_args (tree, tree, tree, rtx, machine_mode, tree); static rtx expand_builtin_bzero (tree); -static rtx expand_builtin_strlen (tree, rtx, enum machine_mode); +static rtx expand_builtin_strlen (tree, rtx, machine_mode); static rtx expand_builtin_alloca (tree, bool); -static rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab); +static rtx expand_builtin_unop (machine_mode, tree, rtx, rtx, optab); static rtx expand_builtin_frame_address (tree, tree); static tree stabilize_va_list_loc (location_t, tree, int); static rtx expand_builtin_expect (tree, rtx); @@ -148,7 +177,6 @@ static tree rewrite_call_expr (location_t, tree, int, tree, int, ...); static bool validate_arg (const_tree, enum tree_code code); static bool integer_valued_real_p (tree); static tree fold_trunc_transparent_mathfn (location_t, tree, tree); -static bool readonly_data_expr (tree); static rtx expand_builtin_fabs (tree, rtx, rtx); static rtx expand_builtin_signbit (tree, rtx); static tree fold_builtin_sqrt (location_t, tree, tree); @@ -164,7 +192,6 @@ static tree fold_builtin_ceil (location_t, tree, tree); static tree fold_builtin_round (location_t, tree, tree); static tree fold_builtin_int_roundingfn (location_t, tree, tree); static tree fold_builtin_bitop (tree, tree); -static tree fold_builtin_memory_op (location_t, tree, tree, tree, tree, bool, int); static tree fold_builtin_strchr (location_t, tree, tree, tree); static tree fold_builtin_memchr (location_t, tree, tree, tree, tree); static tree fold_builtin_memcmp (location_t, tree, tree, tree); @@ -179,45 +206,33 @@ static tree fold_builtin_fabs (location_t, tree, tree); static tree fold_builtin_abs (location_t, tree, tree); static tree fold_builtin_unordered_cmp (location_t, tree, tree, tree, enum tree_code, enum tree_code); -static tree fold_builtin_n (location_t, tree, tree *, int, bool); -static tree fold_builtin_0 (location_t, tree, bool); -static tree fold_builtin_1 (location_t, tree, tree, bool); -static tree fold_builtin_2 (location_t, tree, tree, tree, bool); -static tree fold_builtin_3 (location_t, tree, tree, tree, tree, bool); -static tree fold_builtin_4 (location_t, tree, tree, tree, tree, tree, bool); -static tree fold_builtin_varargs (location_t, tree, tree, bool); +static tree fold_builtin_0 (location_t, tree); +static tree fold_builtin_1 (location_t, tree, tree); +static tree fold_builtin_2 (location_t, tree, tree, tree); +static tree fold_builtin_3 (location_t, tree, tree, tree, tree); +static tree fold_builtin_varargs (location_t, tree, tree*, int); static tree fold_builtin_strpbrk (location_t, tree, tree, tree); static tree fold_builtin_strstr (location_t, tree, tree, tree); static tree fold_builtin_strrchr (location_t, tree, tree, tree); -static tree fold_builtin_strncat (location_t, tree, tree, tree); static tree fold_builtin_strspn (location_t, tree, tree); static tree fold_builtin_strcspn (location_t, tree, tree); -static tree fold_builtin_sprintf (location_t, tree, tree, tree, int); -static tree fold_builtin_snprintf (location_t, tree, tree, tree, tree, int); static rtx expand_builtin_object_size (tree); -static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode, +static rtx expand_builtin_memory_chk (tree, rtx, 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 void maybe_emit_free_warning (tree); static tree fold_builtin_object_size (tree, tree); -static tree fold_builtin_strcat_chk (location_t, tree, tree, tree, tree); -static tree fold_builtin_strncat_chk (location_t, tree, tree, tree, tree, tree); -static tree fold_builtin_sprintf_chk (location_t, tree, enum built_in_function); -static tree fold_builtin_printf (location_t, tree, tree, tree, bool, enum built_in_function); -static tree fold_builtin_fprintf (location_t, tree, tree, tree, tree, bool, - enum built_in_function); -static bool init_target_chars (void); - -static unsigned HOST_WIDE_INT target_newline; -static unsigned HOST_WIDE_INT target_percent; + +unsigned HOST_WIDE_INT target_newline; +unsigned HOST_WIDE_INT target_percent; static unsigned HOST_WIDE_INT target_c; static unsigned HOST_WIDE_INT target_s; -static char target_percent_c[3]; -static char target_percent_s[3]; -static char target_percent_s_newline[4]; +char target_percent_c[3]; +char target_percent_s[3]; +char target_percent_s_newline[4]; static tree do_mpfr_arg1 (tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mp_rnd_t), const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *, bool); static tree do_mpfr_arg2 (tree, tree, tree, @@ -259,31 +274,6 @@ is_builtin_fn (tree decl) return TREE_CODE (decl) == FUNCTION_DECL && DECL_BUILT_IN (decl); } -/* By default we assume that c99 functions are present at the runtime, - but sincos is not. */ -bool -default_libc_has_function (enum function_class fn_class) -{ - if (fn_class == function_c94 - || fn_class == function_c99_misc - || fn_class == function_c99_math_complex) - return true; - - return false; -} - -bool -gnu_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED) -{ - return true; -} - -bool -no_c99_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED) -{ - return false; -} - /* 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". */ @@ -319,7 +309,7 @@ get_object_alignment_2 (tree exp, unsigned int *alignp, { HOST_WIDE_INT bitsize, bitpos; tree offset; - enum machine_mode mode; + machine_mode mode; int unsignedp, volatilep; unsigned int align = BITS_PER_UNIT; bool known_alignment = false; @@ -413,7 +403,7 @@ get_object_alignment_2 (tree exp, unsigned int *alignp, bitpos += ptr_bitpos; if (TREE_CODE (exp) == MEM_REF || TREE_CODE (exp) == TARGET_MEM_REF) - bitpos += mem_ref_offset (exp).low * BITS_PER_UNIT; + bitpos += mem_ref_offset (exp).to_short_addr () * BITS_PER_UNIT; } } else if (TREE_CODE (exp) == STRING_CST) @@ -560,6 +550,10 @@ get_pointer_alignment (tree exp) len = c_strlen (src, 1); if (len) expand_expr (len, ...); would not evaluate the side-effects. + If ONLY_VALUE is two then we do not emit warnings about out-of-bound + accesses. Note that this implies the result is not going to be emitted + into the instruction stream. + The value returned is of type `ssizetype'. Unfortunately, string_constant can't access the values of const char @@ -634,7 +628,8 @@ c_strlen (tree src, int only_value) if (offset < 0 || offset > max) { /* Suppress multiple warnings for propagated constant strings. */ - if (! TREE_NO_WARNING (src)) + if (only_value != 2 + && !TREE_NO_WARNING (src)) { warning_at (loc, 0, "offset outside bounds of constant string"); TREE_NO_WARNING (src) = 1; @@ -654,7 +649,7 @@ c_strlen (tree src, int only_value) /* Return a char pointer for a C string if it is a string constant or sum of string constant and integer constant. */ -static const char * +const char * c_getstr (tree src) { tree offset_node; @@ -672,20 +667,24 @@ c_getstr (tree src) return TREE_STRING_POINTER (src) + tree_to_uhwi (offset_node); } -/* Return a CONST_INT or CONST_DOUBLE corresponding to target reading +/* Return a constant integer corresponding to target reading GET_MODE_BITSIZE (MODE) bits from string constant STR. */ static rtx -c_readstr (const char *str, enum machine_mode mode) +c_readstr (const char *str, machine_mode mode) { - HOST_WIDE_INT c[2]; HOST_WIDE_INT ch; unsigned int i, j; + HOST_WIDE_INT tmp[MAX_BITSIZE_MODE_ANY_INT / HOST_BITS_PER_WIDE_INT]; gcc_assert (GET_MODE_CLASS (mode) == MODE_INT); + unsigned int len = (GET_MODE_PRECISION (mode) + HOST_BITS_PER_WIDE_INT - 1) + / HOST_BITS_PER_WIDE_INT; + + gcc_assert (len <= MAX_BITSIZE_MODE_ANY_INT / HOST_BITS_PER_WIDE_INT); + for (i = 0; i < len; i++) + tmp[i] = 0; - c[0] = 0; - c[1] = 0; ch = 1; for (i = 0; i < GET_MODE_SIZE (mode); i++) { @@ -696,13 +695,14 @@ c_readstr (const char *str, enum machine_mode mode) && GET_MODE_SIZE (mode) >= UNITS_PER_WORD) j = j + UNITS_PER_WORD - 2 * (j % UNITS_PER_WORD) - 1; j *= BITS_PER_UNIT; - gcc_assert (j < HOST_BITS_PER_DOUBLE_INT); if (ch) ch = (unsigned char) str[i]; - c[j / HOST_BITS_PER_WIDE_INT] |= ch << (j % HOST_BITS_PER_WIDE_INT); + tmp[j / HOST_BITS_PER_WIDE_INT] |= ch << (j % HOST_BITS_PER_WIDE_INT); } - return immed_double_const (c[0], c[1], mode); + + wide_int c = wide_int::from_array (tmp, len, GET_MODE_PRECISION (mode)); + return immed_wide_int_const (c, mode); } /* Cast a target constant CST to target CHAR and if that value fits into @@ -718,7 +718,9 @@ target_char_cast (tree cst, char *p) || CHAR_TYPE_SIZE > HOST_BITS_PER_WIDE_INT) return 1; + /* Do not care if it fits or not right here. */ val = TREE_INT_CST_LOW (cst); + if (CHAR_TYPE_SIZE < HOST_BITS_PER_WIDE_INT) val &= (((unsigned HOST_WIDE_INT) 1) << CHAR_TYPE_SIZE) - 1; @@ -843,7 +845,7 @@ static alias_set_type setjmp_alias_set = -1; void expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) { - enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); + machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); rtx stack_save; rtx mem; @@ -985,8 +987,9 @@ expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED) static void expand_builtin_longjmp (rtx buf_addr, rtx value) { - rtx fp, lab, stack, insn, last; - enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); + rtx fp, lab, stack; + rtx_insn *insn, *last; + machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); /* DRAP is needed for stack realign if longjmp is expanded to current function */ @@ -1129,7 +1132,8 @@ static rtx expand_builtin_nonlocal_goto (tree exp) { tree t_label, t_save_area; - rtx r_label, r_save_area, r_fp, r_sp, insn; + rtx r_label, r_save_area, r_fp, r_sp; + rtx_insn *insn; if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) return NULL_RTX; @@ -1212,7 +1216,7 @@ expand_builtin_nonlocal_goto (tree exp) static void expand_builtin_update_setjmp_buf (rtx buf_addr) { - enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); + machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); rtx stack_save = gen_rtx_MEM (sa_mode, memory_address @@ -1374,7 +1378,7 @@ apply_args_size (void) static int size = -1; int align; unsigned int regno; - enum machine_mode mode; + machine_mode mode; /* The values computed by this function never change. */ if (size < 0) @@ -1416,7 +1420,7 @@ apply_result_size (void) { static int size = -1; int align, regno; - enum machine_mode mode; + machine_mode mode; /* The values computed by this function never change. */ if (size < 0) @@ -1457,7 +1461,7 @@ static rtx result_vector (int savep, rtx result) { int regno, size, align, nelts; - enum machine_mode mode; + machine_mode mode; rtx reg, mem; rtx *savevec = XALLOCAVEC (rtx, FIRST_PSEUDO_REGISTER); @@ -1487,7 +1491,7 @@ expand_builtin_apply_args_1 (void) { rtx registers, tem; int size, align, regno; - enum machine_mode mode; + machine_mode mode; rtx struct_incoming_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 1); /* Create a block where the arg-pointer, structure value address, @@ -1592,8 +1596,9 @@ static rtx expand_builtin_apply (rtx function, rtx arguments, rtx argsize) { int size, align, regno; - enum machine_mode mode; - rtx incoming_args, result, reg, dest, src, call_insn; + machine_mode mode; + rtx incoming_args, result, reg, dest, src; + rtx_call_insn *call_insn; rtx old_stack_level = 0; rtx call_fusage = 0; rtx struct_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0); @@ -1758,9 +1763,9 @@ static void expand_builtin_return (rtx result) { int size, align, regno; - enum machine_mode mode; + machine_mode mode; rtx reg; - rtx call_fusage = 0; + rtx_insn *call_fusage = 0; result = convert_memory_address (Pmode, result); @@ -1988,7 +1993,7 @@ mathfn_built_in (tree type, enum built_in_function fn) static void expand_errno_check (tree exp, rtx target) { - rtx lab = gen_label_rtx (); + rtx_code_label *lab = gen_label_rtx (); /* Test the result; if it is NaN, set errno=EDOM because the argument was not in the domain. */ @@ -2035,9 +2040,10 @@ static rtx expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) { optab builtin_optab; - rtx op0, insns; + rtx op0; + rtx_insn *insns; tree fndecl = get_callee_fndecl (exp); - enum machine_mode mode; + machine_mode mode; bool errno_set = false; bool try_widening = false; tree arg; @@ -2161,11 +2167,12 @@ static rtx expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) { optab builtin_optab; - rtx op0, op1, insns, result; + rtx op0, op1, result; + rtx_insn *insns; int op1_type = REAL_TYPE; tree fndecl = get_callee_fndecl (exp); tree arg0, arg1; - enum machine_mode mode; + machine_mode mode; bool errno_set = true; switch (DECL_FUNCTION_CODE (fndecl)) @@ -2270,10 +2277,11 @@ static rtx expand_builtin_mathfn_ternary (tree exp, rtx target, rtx subtarget) { optab builtin_optab; - rtx op0, op1, op2, insns, result; + rtx op0, op1, op2, result; + rtx_insn *insns; tree fndecl = get_callee_fndecl (exp); tree arg0, arg1, arg2; - enum machine_mode mode; + machine_mode mode; if (!validate_arglist (exp, REAL_TYPE, REAL_TYPE, REAL_TYPE, VOID_TYPE)) return NULL_RTX; @@ -2343,9 +2351,10 @@ static rtx expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) { optab builtin_optab; - rtx op0, insns; + rtx op0; + rtx_insn *insns; tree fndecl = get_callee_fndecl (exp); - enum machine_mode mode; + machine_mode mode; tree arg; if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE)) @@ -2441,7 +2450,7 @@ interclass_mathfn_icode (tree arg, tree fndecl) { bool errno_set = false; optab builtin_optab = unknown_optab; - enum machine_mode mode; + machine_mode mode; switch (DECL_FUNCTION_CODE (fndecl)) { @@ -2489,7 +2498,7 @@ expand_builtin_interclass_mathfn (tree exp, rtx target) enum insn_code icode = CODE_FOR_nothing; rtx op0; tree fndecl = get_callee_fndecl (exp); - enum machine_mode mode; + machine_mode mode; tree arg; if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE)) @@ -2502,7 +2511,7 @@ expand_builtin_interclass_mathfn (tree exp, rtx target) if (icode != CODE_FOR_nothing) { struct expand_operand ops[1]; - rtx last = get_last_insn (); + rtx_insn *last = get_last_insn (); tree orig_arg = arg; /* Wrap the computation of the argument in a SAVE_EXPR, as we may @@ -2536,7 +2545,7 @@ static rtx expand_builtin_sincos (tree exp) { rtx op0, op1, op2, target1, target2; - enum machine_mode mode; + machine_mode mode; tree arg, sinp, cosp; int result; location_t loc = EXPR_LOCATION (exp); @@ -2590,7 +2599,7 @@ expand_builtin_cexpi (tree exp, rtx target) { tree fndecl = get_callee_fndecl (exp); tree arg, type; - enum machine_mode mode; + machine_mode mode; rtx op0, op1, op2; location_t loc = EXPR_LOCATION (exp); @@ -2720,11 +2729,12 @@ static rtx expand_builtin_int_roundingfn (tree exp, rtx target) { convert_optab builtin_optab; - rtx op0, insns, tmp; + rtx op0, tmp; + rtx_insn *insns; tree fndecl = get_callee_fndecl (exp); enum built_in_function fallback_fn; tree fallback_fndecl; - enum machine_mode mode; + machine_mode mode; tree arg; if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE)) @@ -2856,10 +2866,11 @@ static rtx expand_builtin_int_roundingfn_2 (tree exp, rtx target) { convert_optab builtin_optab; - rtx op0, insns; + rtx op0; + rtx_insn *insns; tree fndecl = get_callee_fndecl (exp); tree arg; - enum machine_mode mode; + machine_mode mode; enum built_in_function fallback_fn = BUILT_IN_NONE; if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE)) @@ -2957,8 +2968,8 @@ expand_builtin_powi (tree exp, rtx target) { tree arg0, arg1; rtx op0, op1; - enum machine_mode mode; - enum machine_mode mode2; + machine_mode mode; + machine_mode mode2; if (! validate_arglist (exp, REAL_TYPE, INTEGER_TYPE, VOID_TYPE)) return NULL_RTX; @@ -2995,7 +3006,7 @@ expand_builtin_powi (tree exp, rtx target) static rtx expand_builtin_strlen (tree exp, rtx target, - enum machine_mode target_mode) + machine_mode target_mode) { if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE)) return NULL_RTX; @@ -3005,8 +3016,9 @@ expand_builtin_strlen (tree exp, rtx target, rtx pat; tree len; tree src = CALL_EXPR_ARG (exp, 0); - rtx src_reg, before_strlen; - enum machine_mode insn_mode = target_mode; + rtx src_reg; + rtx_insn *before_strlen; + machine_mode insn_mode = target_mode; enum insn_code icode = CODE_FOR_nothing; unsigned int align; @@ -3099,7 +3111,7 @@ expand_builtin_strlen (tree exp, rtx target, static rtx builtin_memcpy_read_str (void *data, HOST_WIDE_INT offset, - enum machine_mode mode) + machine_mode mode) { const char *str = (const char *) data; @@ -3128,7 +3140,7 @@ determine_block_size (tree len, rtx len_rtx, } else { - double_int min, max; + wide_int min, max; enum value_range_type range_type = VR_UNDEFINED; /* Determine bounds from the type. */ @@ -3146,18 +3158,18 @@ determine_block_size (tree len, rtx len_rtx, range_type = get_range_info (len, &min, &max); if (range_type == VR_RANGE) { - if (min.fits_uhwi () && *min_size < min.to_uhwi ()) + if (wi::fits_uhwi_p (min) && *min_size < min.to_uhwi ()) *min_size = min.to_uhwi (); - if (max.fits_uhwi () && *max_size > max.to_uhwi ()) + if (wi::fits_uhwi_p (max) && *max_size > max.to_uhwi ()) *probable_max_size = *max_size = max.to_uhwi (); } else if (range_type == VR_ANTI_RANGE) { /* Anti range 0...N lets us to determine minimal size to N+1. */ - if (min.is_zero ()) + if (min == 0) { - if ((max + double_int_one).fits_uhwi ()) - *min_size = (max + double_int_one).to_uhwi (); + if (wi::fits_uhwi_p (max) && max.to_uhwi () + 1 != 0) + *min_size = max.to_uhwi () + 1; } /* Code like @@ -3168,9 +3180,8 @@ determine_block_size (tree len, rtx len_rtx, Produce anti range allowing negative values of N. We still can use the information and make a guess that N is not negative. */ - else if (!max.ule (double_int_one.lshift (30)) - && min.fits_uhwi ()) - *probable_max_size = min.to_uhwi () - 1; + else if (!wi::leu_p (max, 1 << 30) && wi::fits_uhwi_p (min)) + *probable_max_size = min.to_uhwi () - 1; } } gcc_checking_assert (*max_size <= @@ -3178,6 +3189,81 @@ determine_block_size (tree len, rtx len_rtx, GET_MODE_MASK (GET_MODE (len_rtx))); } +/* Helper function to do the actual work for expand_builtin_memcpy. */ + +static rtx +expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp) +{ + const char *src_str; + unsigned int src_align = get_pointer_alignment (src); + unsigned int dest_align = get_pointer_alignment (dest); + rtx dest_mem, src_mem, dest_addr, len_rtx; + HOST_WIDE_INT expected_size = -1; + unsigned int expected_align = 0; + unsigned HOST_WIDE_INT min_size; + unsigned HOST_WIDE_INT max_size; + unsigned HOST_WIDE_INT probable_max_size; + + /* If DEST is not a pointer type, call the normal function. */ + if (dest_align == 0) + return NULL_RTX; + + /* If either SRC is not a pointer type, don't do this + operation in-line. */ + if (src_align == 0) + return NULL_RTX; + + if (currently_expanding_gimple_stmt) + stringop_block_profile (currently_expanding_gimple_stmt, + &expected_align, &expected_size); + + if (expected_align < dest_align) + expected_align = dest_align; + dest_mem = get_memory_rtx (dest, len); + set_mem_align (dest_mem, dest_align); + len_rtx = expand_normal (len); + determine_block_size (len, len_rtx, &min_size, &max_size, + &probable_max_size); + src_str = c_getstr (src); + + /* If SRC is a string constant and block move would be done + by pieces, we can avoid loading the string from memory + and only stored the computed constants. */ + if (src_str + && CONST_INT_P (len_rtx) + && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 + && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, + CONST_CAST (char *, src_str), + dest_align, false)) + { + dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), + builtin_memcpy_read_str, + CONST_CAST (char *, src_str), + dest_align, false, 0); + dest_mem = force_operand (XEXP (dest_mem, 0), target); + dest_mem = convert_memory_address (ptr_mode, dest_mem); + return dest_mem; + } + + src_mem = get_memory_rtx (src, len); + set_mem_align (src_mem, src_align); + + /* Copy word part most expediently. */ + dest_addr = emit_block_move_hints (dest_mem, src_mem, len_rtx, + CALL_EXPR_TAILCALL (exp) + ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL, + expected_align, expected_size, + min_size, max_size, probable_max_size); + + if (dest_addr == 0) + { + dest_addr = force_operand (XEXP (dest_mem, 0), target); + dest_addr = convert_memory_address (ptr_mode, dest_addr); + } + + return dest_addr; +} + /* Expand a call EXP to the memcpy builtin. Return NULL_RTX if we failed, the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in @@ -3194,73 +3280,38 @@ expand_builtin_memcpy (tree exp, rtx target) tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); - const char *src_str; - unsigned int src_align = get_pointer_alignment (src); - unsigned int dest_align = get_pointer_alignment (dest); - rtx dest_mem, src_mem, dest_addr, len_rtx; - HOST_WIDE_INT expected_size = -1; - unsigned int expected_align = 0; - unsigned HOST_WIDE_INT min_size; - unsigned HOST_WIDE_INT max_size; - unsigned HOST_WIDE_INT probable_max_size; - - /* If DEST is not a pointer type, call the normal function. */ - if (dest_align == 0) - return NULL_RTX; - - /* If either SRC is not a pointer type, don't do this - operation in-line. */ - if (src_align == 0) - return NULL_RTX; - - if (currently_expanding_gimple_stmt) - stringop_block_profile (currently_expanding_gimple_stmt, - &expected_align, &expected_size); - - if (expected_align < dest_align) - expected_align = dest_align; - dest_mem = get_memory_rtx (dest, len); - set_mem_align (dest_mem, dest_align); - len_rtx = expand_normal (len); - determine_block_size (len, len_rtx, &min_size, &max_size, - &probable_max_size); - src_str = c_getstr (src); - - /* If SRC is a string constant and block move would be done - by pieces, we can avoid loading the string from memory - and only stored the computed constants. */ - if (src_str - && CONST_INT_P (len_rtx) - && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 - && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, - CONST_CAST (char *, src_str), - dest_align, false)) - { - dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), - builtin_memcpy_read_str, - CONST_CAST (char *, src_str), - dest_align, false, 0); - dest_mem = force_operand (XEXP (dest_mem, 0), target); - dest_mem = convert_memory_address (ptr_mode, dest_mem); - return dest_mem; - } + return expand_builtin_memcpy_args (dest, src, len, target, exp); + } +} - src_mem = get_memory_rtx (src, len); - set_mem_align (src_mem, src_align); +/* Expand an instrumented call EXP to the memcpy builtin. + Return NULL_RTX 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). */ - /* Copy word part most expediently. */ - dest_addr = emit_block_move_hints (dest_mem, src_mem, len_rtx, - CALL_EXPR_TAILCALL (exp) - ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL, - expected_align, expected_size, - min_size, max_size, probable_max_size); +static rtx +expand_builtin_memcpy_with_bounds (tree exp, rtx target) +{ + if (!validate_arglist (exp, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + else + { + tree dest = CALL_EXPR_ARG (exp, 0); + tree src = CALL_EXPR_ARG (exp, 2); + tree len = CALL_EXPR_ARG (exp, 4); + rtx res = expand_builtin_memcpy_args (dest, src, len, target, exp); - if (dest_addr == 0) + /* Return src bounds with the result. */ + if (res) { - dest_addr = force_operand (XEXP (dest_mem, 0), target); - dest_addr = convert_memory_address (ptr_mode, dest_addr); + rtx bnd = force_reg (targetm.chkp_bound_mode (), + expand_normal (CALL_EXPR_ARG (exp, 1))); + res = chkp_join_splitted_slot (res, bnd); } - return dest_addr; + return res; } } @@ -3273,7 +3324,7 @@ expand_builtin_memcpy (tree exp, rtx target) stpcpy. */ static rtx -expand_builtin_mempcpy (tree exp, rtx target, enum machine_mode mode) +expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode) { if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -3284,7 +3335,40 @@ expand_builtin_mempcpy (tree exp, rtx target, enum machine_mode mode) tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); return expand_builtin_mempcpy_args (dest, src, len, - target, mode, /*endp=*/ 1); + target, mode, /*endp=*/ 1, + exp); + } +} + +/* Expand an instrumented call EXP to the mempcpy builtin. + Return NULL_RTX 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_mempcpy_with_bounds (tree exp, rtx target, machine_mode mode) +{ + if (!validate_arglist (exp, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + else + { + tree dest = CALL_EXPR_ARG (exp, 0); + tree src = CALL_EXPR_ARG (exp, 2); + tree len = CALL_EXPR_ARG (exp, 4); + rtx res = expand_builtin_mempcpy_args (dest, src, len, target, + mode, 1, exp); + + /* Return src bounds with the result. */ + if (res) + { + rtx bnd = force_reg (targetm.chkp_bound_mode (), + expand_normal (CALL_EXPR_ARG (exp, 1))); + res = chkp_join_splitted_slot (res, bnd); + } + return res; } } @@ -3296,10 +3380,23 @@ expand_builtin_mempcpy (tree exp, rtx target, enum machine_mode mode) static rtx expand_builtin_mempcpy_args (tree dest, tree src, tree len, - rtx target, enum machine_mode mode, int endp) + rtx target, machine_mode mode, int endp, + tree orig_exp) { + tree fndecl = get_callee_fndecl (orig_exp); + /* If return value is ignored, transform mempcpy into memcpy. */ - if (target == const0_rtx && builtin_decl_implicit_p (BUILT_IN_MEMCPY)) + if (target == const0_rtx + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK_CHKP + && builtin_decl_implicit_p (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP)) + { + tree fn = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP); + tree result = build_call_nofold_loc (UNKNOWN_LOCATION, fn, 3, + dest, src, len); + return expand_expr (result, target, mode, EXPAND_NORMAL); + } + else if (target == const0_rtx + && builtin_decl_implicit_p (BUILT_IN_MEMCPY)) { tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY); tree result = build_call_nofold_loc (UNKNOWN_LOCATION, fn, 3, @@ -3452,7 +3549,7 @@ expand_builtin_strcpy_args (tree dest, tree src, rtx target) mode MODE if that's convenient). */ static rtx -expand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode) +expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode) { tree dst, src; location_t loc = EXPR_LOCATION (exp); @@ -3484,7 +3581,8 @@ expand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode) lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1)); ret = expand_builtin_mempcpy_args (dst, src, lenp1, - target, mode, /*endp=*/2); + target, mode, /*endp=*/2, + exp); if (ret) return ret; @@ -3528,7 +3626,7 @@ expand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode) rtx builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset, - enum machine_mode mode) + machine_mode mode) { const char *str = (const char *) data; @@ -3594,7 +3692,7 @@ expand_builtin_strncpy (tree exp, rtx target) rtx builtin_memset_read_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, - enum machine_mode mode) + machine_mode mode) { const char *c = (const char *) data; char *p = XALLOCAVEC (char, GET_MODE_SIZE (mode)); @@ -3611,7 +3709,7 @@ builtin_memset_read_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, static rtx builtin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, - enum machine_mode mode) + machine_mode mode) { rtx target, coeff; size_t size; @@ -3636,7 +3734,7 @@ builtin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED, convenient). */ static rtx -expand_builtin_memset (tree exp, rtx target, enum machine_mode mode) +expand_builtin_memset (tree exp, rtx target, machine_mode mode) { if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -3650,6 +3748,36 @@ expand_builtin_memset (tree exp, rtx target, enum machine_mode mode) } } +/* Expand expression EXP, which is an instrumented call to the memset builtin. + Return NULL_RTX 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_memset_with_bounds (tree exp, rtx target, machine_mode mode) +{ + if (!validate_arglist (exp, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + else + { + tree dest = CALL_EXPR_ARG (exp, 0); + tree val = CALL_EXPR_ARG (exp, 2); + tree len = CALL_EXPR_ARG (exp, 3); + rtx res = expand_builtin_memset_args (dest, val, len, target, mode, exp); + + /* Return src bounds with the result. */ + if (res) + { + rtx bnd = force_reg (targetm.chkp_bound_mode (), + expand_normal (CALL_EXPR_ARG (exp, 1))); + res = chkp_join_splitted_slot (res, bnd); + } + return res; + } +} + /* Helper function to do the actual work for expand_builtin_memset. The arguments to the builtin_memset call DEST, VAL, and LEN are broken out so that this can also be called without constructing an actual CALL_EXPR. @@ -3658,11 +3786,11 @@ expand_builtin_memset (tree exp, rtx target, enum machine_mode mode) static rtx expand_builtin_memset_args (tree dest, tree val, tree len, - rtx target, enum machine_mode mode, tree orig_exp) + rtx target, machine_mode mode, tree orig_exp) { tree fndecl, fn; enum built_in_function fcode; - enum machine_mode val_mode; + machine_mode val_mode; char c; unsigned int dest_align; rtx dest_mem, dest_addr, len_rtx; @@ -3778,7 +3906,8 @@ expand_builtin_memset_args (tree dest, tree val, tree len, do_libcall: fndecl = get_callee_fndecl (orig_exp); fcode = DECL_FUNCTION_CODE (fndecl); - if (fcode == BUILT_IN_MEMSET) + if (fcode == BUILT_IN_MEMSET + || fcode == BUILT_IN_CHKP_MEMSET_NOBND_NOCHK_CHKP) fn = build_call_nofold_loc (EXPR_LOCATION (orig_exp), fndecl, 3, dest, val, len); else if (fcode == BUILT_IN_BZERO) @@ -3824,7 +3953,7 @@ expand_builtin_bzero (tree exp) static rtx expand_builtin_memcmp (tree exp, ATTRIBUTE_UNUSED rtx target, - ATTRIBUTE_UNUSED enum machine_mode mode) + ATTRIBUTE_UNUSED machine_mode mode) { location_t loc ATTRIBUTE_UNUSED = EXPR_LOCATION (exp); @@ -3846,7 +3975,7 @@ expand_builtin_memcmp (tree exp, ATTRIBUTE_UNUSED rtx target, unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT; unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT; - enum machine_mode insn_mode; + machine_mode insn_mode; if (HAVE_cmpmemsi) insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode; @@ -3947,7 +4076,7 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target) /* Try to call cmpstrsi. */ if (HAVE_cmpstrsi) { - enum machine_mode insn_mode + machine_mode insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode; /* Make a place to write the result of the instruction. */ @@ -3968,7 +4097,7 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target) tree len; rtx arg3_rtx; - enum machine_mode insn_mode + machine_mode insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; tree len1 = c_strlen (arg1, 1); tree len2 = c_strlen (arg2, 1); @@ -4022,7 +4151,7 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target) if (insn) { - enum machine_mode mode; + machine_mode mode; emit_insn (insn); /* Return the value in the proper mode for this function. */ @@ -4056,7 +4185,7 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target) static rtx expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target, - ATTRIBUTE_UNUSED enum machine_mode mode) + ATTRIBUTE_UNUSED machine_mode mode) { location_t loc ATTRIBUTE_UNUSED = EXPR_LOCATION (exp); @@ -4080,7 +4209,7 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target, unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT; unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT; - enum machine_mode insn_mode + machine_mode insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; len1 = c_strlen (arg1, 1); @@ -4177,7 +4306,8 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target, rtx expand_builtin_saveregs (void) { - rtx val, seq; + rtx val; + rtx_insn *seq; /* Don't do __builtin_saveregs more than once in a function. Save the result of the first call and reuse it. */ @@ -4330,6 +4460,13 @@ std_expand_builtin_va_start (tree valist, rtx nextarg) { rtx va_r = expand_expr (valist, NULL_RTX, VOIDmode, EXPAND_WRITE); convert_move (va_r, nextarg, 0); + + /* We do not have any valid bounds for the pointer, so + just store zero bounds for it. */ + if (chkp_function_instrumented_p (current_function_decl)) + chkp_expand_bounds_reset_for_mem (valist, + make_tree (TREE_TYPE (valist), + nextarg)); } /* Expand EXP, a call to __builtin_va_start. */ @@ -4518,7 +4655,7 @@ expand_builtin_alloca (tree exp, bool cannot_accumulate) SUBTARGET may be used as the target for computing one of EXP's operands. */ static rtx -expand_builtin_bswap (enum machine_mode target_mode, tree exp, rtx target, +expand_builtin_bswap (machine_mode target_mode, tree exp, rtx target, rtx subtarget) { tree arg; @@ -4548,7 +4685,7 @@ expand_builtin_bswap (enum machine_mode target_mode, tree exp, rtx target, SUBTARGET may be used as the target for computing one of EXP's operands. */ static rtx -expand_builtin_unop (enum machine_mode target_mode, tree exp, rtx target, +expand_builtin_unop (machine_mode target_mode, tree exp, rtx target, rtx subtarget, optab op_optab) { rtx op0; @@ -4647,7 +4784,7 @@ expand_builtin_unreachable (void) static rtx expand_builtin_fabs (tree exp, rtx target, rtx subtarget) { - enum machine_mode mode; + machine_mode mode; tree arg; rtx op0; @@ -4684,29 +4821,6 @@ expand_builtin_copysign (tree exp, rtx target, rtx subtarget) return expand_copysign (op0, op1, target); } -/* Create a new constant string literal and return a char* pointer to it. - The STRING_CST value is the LEN characters at STR. */ -tree -build_string_literal (int len, const char *str) -{ - tree t, elem, index, type; - - t = build_string (len, str); - elem = build_type_variant (char_type_node, 1, 0); - index = build_index_type (size_int (len - 1)); - type = build_array_type (elem, index); - TREE_TYPE (t) = type; - TREE_CONSTANT (t) = 1; - TREE_READONLY (t) = 1; - TREE_STATIC (t) = 1; - - type = build_pointer_type (elem); - t = build1 (ADDR_EXPR, type, - build4 (ARRAY_REF, elem, - t, integer_zero_node, NULL_TREE, NULL_TREE)); - return t; -} - /* Expand a call to __builtin___clear_cache. */ static rtx @@ -4867,7 +4981,7 @@ static rtx expand_builtin_signbit (tree exp, rtx target) { const struct real_format *fmt; - enum machine_mode fmode, imode, rmode; + machine_mode fmode, imode, rmode; tree arg; int word, bitpos; enum insn_code icode; @@ -4892,7 +5006,7 @@ expand_builtin_signbit (tree exp, rtx target) icode = optab_handler (signbit_optab, fmode); if (icode != CODE_FOR_nothing) { - rtx last = get_last_insn (); + rtx_insn *last = get_last_insn (); target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); if (maybe_emit_unop_insn (icode, target, temp, UNKNOWN)) return target; @@ -4943,12 +5057,12 @@ expand_builtin_signbit (tree exp, rtx target) if (bitpos < GET_MODE_BITSIZE (rmode)) { - double_int mask = double_int_zero.set_bit (bitpos); + wide_int mask = wi::set_bit_in_zero (bitpos, GET_MODE_PRECISION (rmode)); if (GET_MODE_SIZE (imode) > GET_MODE_SIZE (rmode)) temp = gen_lowpart (rmode, temp); temp = expand_binop (rmode, and_optab, temp, - immed_double_int_const (mask, rmode), + immed_wide_int_const (mask, rmode), NULL_RTX, 1, OPTAB_LIB_WIDEN); } else @@ -5040,7 +5154,7 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore) FCODE_DIFF should be fcode - base, where base is the FOO_1 code for the group of builtins. This gives us log2 of the mode size. */ -static inline enum machine_mode +static inline machine_mode get_builtin_sync_mode (int fcode_diff) { /* The size is not negotiable, so ask not to get BLKmode in return @@ -5052,7 +5166,7 @@ get_builtin_sync_mode (int fcode_diff) for the builtin_sync operations. */ static rtx -get_builtin_sync_mem (tree loc, enum machine_mode mode) +get_builtin_sync_mem (tree loc, machine_mode mode) { rtx addr, mem; @@ -5078,10 +5192,10 @@ get_builtin_sync_mem (tree loc, enum machine_mode mode) MODE is the mode it should be in. */ static rtx -expand_expr_force_mode (tree exp, enum machine_mode mode) +expand_expr_force_mode (tree exp, machine_mode mode) { rtx val; - enum machine_mode old_mode; + machine_mode old_mode; val = expand_expr (exp, NULL_RTX, mode, EXPAND_NORMAL); /* If VAL is promoted to a wider mode, convert it back to MODE. Take care @@ -5103,7 +5217,7 @@ expand_expr_force_mode (tree exp, enum machine_mode mode) fetch_and_xxx form. */ static rtx -expand_builtin_sync_operation (enum machine_mode mode, tree exp, +expand_builtin_sync_operation (machine_mode mode, tree exp, enum rtx_code code, bool after, rtx target) { @@ -5164,7 +5278,7 @@ expand_builtin_sync_operation (enum machine_mode mode, tree exp, results; this is NOT optional if IS_BOOL is true. */ static rtx -expand_builtin_compare_and_swap (enum machine_mode mode, tree exp, +expand_builtin_compare_and_swap (machine_mode mode, tree exp, bool is_bool, rtx target) { rtx old_val, new_val, mem; @@ -5198,7 +5312,7 @@ expand_builtin_compare_and_swap (enum machine_mode mode, tree exp, the results. */ static rtx -expand_builtin_sync_lock_test_and_set (enum machine_mode mode, tree exp, +expand_builtin_sync_lock_test_and_set (machine_mode mode, tree exp, rtx target) { rtx val, mem; @@ -5213,7 +5327,7 @@ expand_builtin_sync_lock_test_and_set (enum machine_mode mode, tree exp, /* Expand the __sync_lock_release intrinsic. EXP is the CALL_EXPR. */ static void -expand_builtin_sync_lock_release (enum machine_mode mode, tree exp) +expand_builtin_sync_lock_release (machine_mode mode, tree exp) { rtx mem; @@ -5256,6 +5370,11 @@ get_memmodel (tree exp) return MEMMODEL_SEQ_CST; } + /* Workaround for Bugzilla 59448. GCC doesn't track consume properly, so + be conservative and promote consume to acquire. */ + if (val == MEMMODEL_CONSUME) + val = MEMMODEL_ACQUIRE; + return (enum memmodel) val; } @@ -5265,17 +5384,12 @@ get_memmodel (tree exp) TARGET is an optional place for us to store the results. */ static rtx -expand_builtin_atomic_exchange (enum machine_mode mode, tree exp, rtx target) +expand_builtin_atomic_exchange (machine_mode mode, tree exp, rtx target) { rtx val, mem; enum memmodel model; model = get_memmodel (CALL_EXPR_ARG (exp, 2)); - if ((model & MEMMODEL_MASK) == MEMMODEL_CONSUME) - { - error ("invalid memory model for %<__atomic_exchange%>"); - return NULL_RTX; - } if (!flag_inline_atomics) return NULL_RTX; @@ -5296,10 +5410,11 @@ expand_builtin_atomic_exchange (enum machine_mode mode, tree exp, rtx target) TARGET is an optional place for us to store the results. */ static rtx -expand_builtin_atomic_compare_exchange (enum machine_mode mode, tree exp, +expand_builtin_atomic_compare_exchange (machine_mode mode, tree exp, rtx target) { - rtx expect, desired, mem, oldval, label; + rtx expect, desired, mem, oldval; + rtx_code_label *label; enum memmodel success, failure; tree weak; bool is_weak; @@ -5307,20 +5422,25 @@ expand_builtin_atomic_compare_exchange (enum machine_mode mode, tree exp, success = get_memmodel (CALL_EXPR_ARG (exp, 4)); failure = get_memmodel (CALL_EXPR_ARG (exp, 5)); + if (failure > success) + { + warning (OPT_Winvalid_memory_model, + "failure memory model cannot be stronger than success memory " + "model for %<__atomic_compare_exchange%>"); + success = MEMMODEL_SEQ_CST; + } + if ((failure & MEMMODEL_MASK) == MEMMODEL_RELEASE || (failure & MEMMODEL_MASK) == MEMMODEL_ACQ_REL) { - error ("invalid failure memory model for %<__atomic_compare_exchange%>"); - return NULL_RTX; + warning (OPT_Winvalid_memory_model, + "invalid failure memory model for " + "%<__atomic_compare_exchange%>"); + failure = MEMMODEL_SEQ_CST; + success = MEMMODEL_SEQ_CST; } - if (failure > success) - { - error ("failure memory model cannot be stronger than success " - "memory model for %<__atomic_compare_exchange%>"); - return NULL_RTX; - } - + if (!flag_inline_atomics) return NULL_RTX; @@ -5367,7 +5487,7 @@ expand_builtin_atomic_compare_exchange (enum machine_mode mode, tree exp, TARGET is an optional place for us to store the results. */ static rtx -expand_builtin_atomic_load (enum machine_mode mode, tree exp, rtx target) +expand_builtin_atomic_load (machine_mode mode, tree exp, rtx target) { rtx mem; enum memmodel model; @@ -5376,8 +5496,9 @@ expand_builtin_atomic_load (enum machine_mode mode, tree exp, rtx target) if ((model & MEMMODEL_MASK) == MEMMODEL_RELEASE || (model & MEMMODEL_MASK) == MEMMODEL_ACQ_REL) { - error ("invalid memory model for %<__atomic_load%>"); - return NULL_RTX; + warning (OPT_Winvalid_memory_model, + "invalid memory model for %<__atomic_load%>"); + model = MEMMODEL_SEQ_CST; } if (!flag_inline_atomics) @@ -5396,7 +5517,7 @@ expand_builtin_atomic_load (enum machine_mode mode, tree exp, rtx target) TARGET is an optional place for us to store the results. */ static rtx -expand_builtin_atomic_store (enum machine_mode mode, tree exp) +expand_builtin_atomic_store (machine_mode mode, tree exp) { rtx mem, val; enum memmodel model; @@ -5406,8 +5527,9 @@ expand_builtin_atomic_store (enum machine_mode mode, tree exp) && (model & MEMMODEL_MASK) != MEMMODEL_SEQ_CST && (model & MEMMODEL_MASK) != MEMMODEL_RELEASE) { - error ("invalid memory model for %<__atomic_store%>"); - return NULL_RTX; + warning (OPT_Winvalid_memory_model, + "invalid memory model for %<__atomic_store%>"); + model = MEMMODEL_SEQ_CST; } if (!flag_inline_atomics) @@ -5432,7 +5554,7 @@ expand_builtin_atomic_store (enum machine_mode mode, tree exp) resolved to an instruction sequence. */ static rtx -expand_builtin_atomic_fetch_op (enum machine_mode mode, tree exp, rtx target, +expand_builtin_atomic_fetch_op (machine_mode mode, tree exp, rtx target, enum rtx_code code, bool fetch_after, bool ignore, enum built_in_function ext_call) { @@ -5502,7 +5624,7 @@ expand_builtin_atomic_fetch_op (enum machine_mode mode, tree exp, rtx target, static rtx expand_builtin_atomic_clear (tree exp) { - enum machine_mode mode; + machine_mode mode; rtx mem, ret; enum memmodel model; @@ -5510,11 +5632,13 @@ expand_builtin_atomic_clear (tree exp) mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); model = get_memmodel (CALL_EXPR_ARG (exp, 1)); - if ((model & MEMMODEL_MASK) == MEMMODEL_ACQUIRE + if ((model & MEMMODEL_MASK) == MEMMODEL_CONSUME + || (model & MEMMODEL_MASK) == MEMMODEL_ACQUIRE || (model & MEMMODEL_MASK) == MEMMODEL_ACQ_REL) { - error ("invalid memory model for %<__atomic_store%>"); - return const0_rtx; + warning (OPT_Winvalid_memory_model, + "invalid memory model for %<__atomic_store%>"); + model = MEMMODEL_SEQ_CST; } if (HAVE_atomic_clear) @@ -5543,7 +5667,7 @@ expand_builtin_atomic_test_and_set (tree exp, rtx target) { rtx mem; enum memmodel model; - enum machine_mode mode; + machine_mode mode; mode = mode_for_size (BOOL_TYPE_SIZE, MODE_INT, 0); mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); @@ -5560,7 +5684,7 @@ static tree fold_builtin_atomic_always_lock_free (tree arg0, tree arg1) { int size; - enum machine_mode mode; + machine_mode mode; unsigned int mode_align, type_align; if (TREE_CODE (arg0) != INTEGER_CST) @@ -5580,7 +5704,7 @@ fold_builtin_atomic_always_lock_free (tree arg0, tree arg1) end before anything else has a chance to look at it. The pointer parameter at this point is usually cast to a void *, so check for that and look past the cast. */ - if (TREE_CODE (arg1) == NOP_EXPR && POINTER_TYPE_P (ttype) + if (CONVERT_EXPR_P (arg1) && POINTER_TYPE_P (ttype) && VOID_TYPE_P (TREE_TYPE (ttype))) arg1 = TREE_OPERAND (arg1, 0); @@ -5757,7 +5881,8 @@ expand_builtin_set_thread_pointer (tree exp) static void expand_stack_restore (tree var) { - rtx prev, sa = expand_normal (var); + rtx_insn *prev; + rtx sa = expand_normal (var); sa = convert_memory_address (Pmode, sa); @@ -5779,6 +5904,47 @@ expand_stack_save (void) return ret; } + +/* Expand OpenACC acc_on_device. + + This has to happen late (that is, not in early folding; expand_builtin_*, + rather than fold_builtin_*), as we have to act differently for host and + acceleration device (ACCEL_COMPILER conditional). */ + +static rtx +expand_builtin_acc_on_device (tree exp, rtx target) +{ + if (!validate_arglist (exp, INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + + tree arg = CALL_EXPR_ARG (exp, 0); + + /* Return (arg == v1 || arg == v2) ? 1 : 0. */ + machine_mode v_mode = TYPE_MODE (TREE_TYPE (arg)); + rtx v = expand_normal (arg), v1, v2; +#ifdef ACCEL_COMPILER + v1 = GEN_INT (GOMP_DEVICE_NOT_HOST); + v2 = GEN_INT (ACCEL_COMPILER_acc_device); +#else + v1 = GEN_INT (GOMP_DEVICE_NONE); + v2 = GEN_INT (GOMP_DEVICE_HOST); +#endif + machine_mode target_mode = TYPE_MODE (integer_type_node); + if (!target || !register_operand (target, target_mode)) + target = gen_reg_rtx (target_mode); + emit_move_insn (target, const1_rtx); + rtx_code_label *done_label = gen_label_rtx (); + do_compare_rtx_and_jump (v, v1, EQ, false, v_mode, NULL_RTX, + NULL_RTX, done_label, PROB_EVEN); + do_compare_rtx_and_jump (v, v2, EQ, false, v_mode, NULL_RTX, + NULL_RTX, done_label, PROB_EVEN); + emit_move_insn (target, const0_rtx); + emit_label (done_label); + + return target; +} + + /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient (and in mode MODE if that's convenient). @@ -5786,14 +5952,22 @@ expand_stack_save (void) IGNORE is nonzero if the value is to be ignored. */ rtx -expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, +expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, int ignore) { tree fndecl = get_callee_fndecl (exp); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); - enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp)); + machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp)); int flags; + /* When ASan is enabled, we don't want to expand some memory/string + builtins and rely on libsanitizer's hooks. This allows us to avoid + redundant checks and be sure, that possible overflow will be detected + by ASan. */ + + if ((flag_sanitize & SANITIZE_ADDRESS) && asan_intercepted_p (fcode)) + return expand_call (exp, target, ignore); + if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) return targetm.expand_builtin (exp, target, subtarget, mode, ignore); @@ -5810,7 +5984,19 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, && fcode != BUILT_IN_EXECVE && fcode != BUILT_IN_ALLOCA && fcode != BUILT_IN_ALLOCA_WITH_ALIGN - && fcode != BUILT_IN_FREE) + && fcode != BUILT_IN_FREE + && fcode != BUILT_IN_CHKP_SET_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_INIT_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_NULL_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_COPY_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_NARROW_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_STORE_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_CHECK_PTR_LBOUNDS + && fcode != BUILT_IN_CHKP_CHECK_PTR_UBOUNDS + && fcode != BUILT_IN_CHKP_CHECK_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_GET_PTR_LBOUND + && fcode != BUILT_IN_CHKP_GET_PTR_UBOUND + && fcode != BUILT_IN_CHKP_BNDRET) return expand_call (exp, target, ignore); /* The built-in function expanders test for target == const0_rtx @@ -5844,6 +6030,10 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, } } + /* expand_builtin_with_bounds is supposed to be used for + instrumented builtin calls. */ + gcc_assert (!CALL_WITH_BOUNDS_P (exp)); + switch (fcode) { CASE_FLT_FN (BUILT_IN_FABS): @@ -6217,7 +6407,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, /* This is copied from the handling of non-local gotos. */ expand_builtin_setjmp_setup (buf_addr, label_r); nonlocal_goto_handler_labels - = gen_rtx_EXPR_LIST (VOIDmode, label_r, + = gen_rtx_INSN_LIST (VOIDmode, label_r, nonlocal_goto_handler_labels); /* ??? Do not let expand_label treat us as such since we would not want to be both on the list of non-local labels and on @@ -6848,6 +7038,57 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_builtin_cilk_pop_frame (exp); return const0_rtx; + case BUILT_IN_CHKP_INIT_PTR_BOUNDS: + case BUILT_IN_CHKP_NULL_PTR_BOUNDS: + case BUILT_IN_CHKP_COPY_PTR_BOUNDS: + case BUILT_IN_CHKP_CHECK_PTR_LBOUNDS: + case BUILT_IN_CHKP_CHECK_PTR_UBOUNDS: + case BUILT_IN_CHKP_CHECK_PTR_BOUNDS: + case BUILT_IN_CHKP_SET_PTR_BOUNDS: + case BUILT_IN_CHKP_NARROW_PTR_BOUNDS: + case BUILT_IN_CHKP_STORE_PTR_BOUNDS: + case BUILT_IN_CHKP_GET_PTR_LBOUND: + case BUILT_IN_CHKP_GET_PTR_UBOUND: + /* We allow user CHKP builtins if Pointer Bounds + Checker is off. */ + if (!chkp_function_instrumented_p (current_function_decl)) + { + if (fcode == BUILT_IN_CHKP_SET_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_NARROW_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_INIT_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_NULL_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_COPY_PTR_BOUNDS) + return expand_normal (CALL_EXPR_ARG (exp, 0)); + else if (fcode == BUILT_IN_CHKP_GET_PTR_LBOUND) + return expand_normal (size_zero_node); + else if (fcode == BUILT_IN_CHKP_GET_PTR_UBOUND) + return expand_normal (size_int (-1)); + else + return const0_rtx; + } + /* FALLTHROUGH */ + + case BUILT_IN_CHKP_BNDMK: + case BUILT_IN_CHKP_BNDSTX: + case BUILT_IN_CHKP_BNDCL: + case BUILT_IN_CHKP_BNDCU: + case BUILT_IN_CHKP_BNDLDX: + case BUILT_IN_CHKP_BNDRET: + case BUILT_IN_CHKP_INTERSECT: + case BUILT_IN_CHKP_NARROW: + case BUILT_IN_CHKP_EXTRACT_LOWER: + case BUILT_IN_CHKP_EXTRACT_UPPER: + /* Software implementation of Pointer Bounds Checker is NYI. + Target support is required. */ + error ("Your target platform does not support -fcheck-pointer-bounds"); + break; + + case BUILT_IN_ACC_ON_DEVICE: + target = expand_builtin_acc_on_device (exp, target); + if (target) + return target; + break; + default: /* just do library call, if unknown builtin */ break; } @@ -6857,6 +7098,53 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return expand_call (exp, target, ignore); } +/* Similar to expand_builtin but is used for instrumented calls. */ + +rtx +expand_builtin_with_bounds (tree exp, rtx target, + rtx subtarget ATTRIBUTE_UNUSED, + machine_mode mode, int ignore) +{ + tree fndecl = get_callee_fndecl (exp); + enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); + + gcc_assert (CALL_WITH_BOUNDS_P (exp)); + + if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) + return targetm.expand_builtin (exp, target, subtarget, mode, ignore); + + gcc_assert (fcode > BEGIN_CHKP_BUILTINS + && fcode < END_CHKP_BUILTINS); + + switch (fcode) + { + case BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP: + target = expand_builtin_memcpy_with_bounds (exp, target); + if (target) + return target; + break; + + case BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK_CHKP: + target = expand_builtin_mempcpy_with_bounds (exp, target, mode); + if (target) + return target; + break; + + case BUILT_IN_CHKP_MEMSET_NOBND_NOCHK_CHKP: + target = expand_builtin_memset_with_bounds (exp, target, mode); + if (target) + return target; + break; + + default: + break; + } + + /* The switch statement above can drop through to cause the function + to be called normally. */ + return expand_call (exp, target, ignore); + } + /* Determine whether a tree node represents a call to a built-in function. If the tree T is a call to a built-in function with the right number of arguments of the appropriate types, return @@ -7009,7 +7297,7 @@ fold_builtin_expect (location_t loc, tree arg0, tree arg1, tree arg2) /* Distribute the expected value over short-circuiting operators. See through the cast from truthvalue_type_node to long. */ inner_arg0 = arg0; - while (TREE_CODE (inner_arg0) == NOP_EXPR + while (CONVERT_EXPR_P (inner_arg0) && INTEGRAL_TYPE_P (TREE_TYPE (inner_arg0)) && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (inner_arg0, 0)))) inner_arg0 = TREE_OPERAND (inner_arg0, 0); @@ -7175,7 +7463,7 @@ integer_valued_real_p (tree t) case REAL_CST: return real_isinteger (TREE_REAL_CST_PTR (t), TYPE_MODE (TREE_TYPE (t))); - case NOP_EXPR: + CASE_CONVERT: { tree type = TREE_TYPE (TREE_OPERAND (t, 0)); if (TREE_CODE (type) == INTEGER_TYPE) @@ -7461,7 +7749,7 @@ fold_builtin_cproj (location_t loc, tree arg, tree type) return NULL_TREE; /* If there are no infinities, return arg. */ - if (! HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (type)))) + if (! HONOR_INFINITIES (type)) return non_lvalue_loc (loc, arg); /* Calculate the result when the argument is a constant. */ @@ -8012,8 +8300,8 @@ fold_builtin_int_roundingfn (location_t loc, tree fndecl, tree arg) { tree itype = TREE_TYPE (TREE_TYPE (fndecl)); tree ftype = TREE_TYPE (arg); - double_int val; REAL_VALUE_TYPE r; + bool fail = false; switch (DECL_FUNCTION_CODE (fndecl)) { @@ -8039,9 +8327,9 @@ fold_builtin_int_roundingfn (location_t loc, tree fndecl, tree arg) gcc_unreachable (); } - real_to_integer2 ((HOST_WIDE_INT *)&val.low, &val.high, &r); - if (double_int_fits_to_tree_p (itype, val)) - return double_int_to_tree (itype, val); + wide_int val = real_to_integer (&r, &fail, TYPE_PRECISION (itype)); + if (!fail) + return wide_int_to_tree (itype, val); } } @@ -8074,94 +8362,39 @@ fold_builtin_bitop (tree fndecl, tree arg) /* Optimize for constant argument. */ if (TREE_CODE (arg) == INTEGER_CST && !TREE_OVERFLOW (arg)) { - HOST_WIDE_INT hi, width, result; - unsigned HOST_WIDE_INT lo; - tree type; - - type = TREE_TYPE (arg); - width = TYPE_PRECISION (type); - lo = TREE_INT_CST_LOW (arg); - - /* Clear all the bits that are beyond the type's precision. */ - if (width > HOST_BITS_PER_WIDE_INT) - { - hi = TREE_INT_CST_HIGH (arg); - if (width < HOST_BITS_PER_DOUBLE_INT) - hi &= ~(HOST_WIDE_INT_M1U << (width - HOST_BITS_PER_WIDE_INT)); - } - else - { - hi = 0; - if (width < HOST_BITS_PER_WIDE_INT) - lo &= ~(HOST_WIDE_INT_M1U << width); - } + tree type = TREE_TYPE (arg); + int result; switch (DECL_FUNCTION_CODE (fndecl)) { CASE_INT_FN (BUILT_IN_FFS): - if (lo != 0) - result = ffs_hwi (lo); - else if (hi != 0) - result = HOST_BITS_PER_WIDE_INT + ffs_hwi (hi); - else - result = 0; + result = wi::ffs (arg); break; CASE_INT_FN (BUILT_IN_CLZ): - if (hi != 0) - result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT; - else if (lo != 0) - result = width - floor_log2 (lo) - 1; + if (wi::ne_p (arg, 0)) + result = wi::clz (arg); else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result)) - result = width; + result = TYPE_PRECISION (type); break; CASE_INT_FN (BUILT_IN_CTZ): - if (lo != 0) - result = ctz_hwi (lo); - else if (hi != 0) - result = HOST_BITS_PER_WIDE_INT + ctz_hwi (hi); + if (wi::ne_p (arg, 0)) + result = wi::ctz (arg); else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result)) - result = width; + result = TYPE_PRECISION (type); break; CASE_INT_FN (BUILT_IN_CLRSB): - if (width > 2 * HOST_BITS_PER_WIDE_INT) - return NULL_TREE; - if (width > HOST_BITS_PER_WIDE_INT - && (hi & ((unsigned HOST_WIDE_INT) 1 - << (width - HOST_BITS_PER_WIDE_INT - 1))) != 0) - { - hi = ~hi & ~(HOST_WIDE_INT_M1U - << (width - HOST_BITS_PER_WIDE_INT - 1)); - lo = ~lo; - } - else if (width <= HOST_BITS_PER_WIDE_INT - && (lo & ((unsigned HOST_WIDE_INT) 1 << (width - 1))) != 0) - lo = ~lo & ~(HOST_WIDE_INT_M1U << (width - 1)); - if (hi != 0) - result = width - floor_log2 (hi) - 2 - HOST_BITS_PER_WIDE_INT; - else if (lo != 0) - result = width - floor_log2 (lo) - 2; - else - result = width - 1; + result = wi::clrsb (arg); break; CASE_INT_FN (BUILT_IN_POPCOUNT): - result = 0; - while (lo) - result++, lo &= lo - 1; - while (hi) - result++, hi &= (unsigned HOST_WIDE_INT) hi - 1; + result = wi::popcount (arg); break; CASE_INT_FN (BUILT_IN_PARITY): - result = 0; - while (lo) - result++, lo &= lo - 1; - while (hi) - result++, hi &= (unsigned HOST_WIDE_INT) hi - 1; - result &= 1; + result = wi::parity (arg); break; default: @@ -8185,142 +8418,24 @@ fold_builtin_bswap (tree fndecl, tree arg) /* Optimize constant value. */ if (TREE_CODE (arg) == INTEGER_CST && !TREE_OVERFLOW (arg)) { - HOST_WIDE_INT hi, width, r_hi = 0; - unsigned HOST_WIDE_INT lo, r_lo = 0; tree type = TREE_TYPE (TREE_TYPE (fndecl)); - width = TYPE_PRECISION (type); - lo = TREE_INT_CST_LOW (arg); - hi = TREE_INT_CST_HIGH (arg); - switch (DECL_FUNCTION_CODE (fndecl)) { case BUILT_IN_BSWAP16: case BUILT_IN_BSWAP32: case BUILT_IN_BSWAP64: { - int s; - - for (s = 0; s < width; s += 8) - { - int d = width - s - 8; - unsigned HOST_WIDE_INT byte; - - if (s < HOST_BITS_PER_WIDE_INT) - byte = (lo >> s) & 0xff; - else - byte = (hi >> (s - HOST_BITS_PER_WIDE_INT)) & 0xff; - - if (d < HOST_BITS_PER_WIDE_INT) - r_lo |= byte << d; - else - r_hi |= byte << (d - HOST_BITS_PER_WIDE_INT); - } + signop sgn = TYPE_SIGN (type); + tree result = + wide_int_to_tree (type, + wide_int::from (arg, TYPE_PRECISION (type), + sgn).bswap ()); + return result; } - - break; - default: gcc_unreachable (); } - - if (width < HOST_BITS_PER_WIDE_INT) - return build_int_cst (type, r_lo); - else - return build_int_cst_wide (type, r_lo, r_hi); - } - - return NULL_TREE; -} - -/* A subroutine of fold_builtin to fold the various logarithmic - functions. Return NULL_TREE if no simplification can me made. - FUNC is the corresponding MPFR logarithm function. */ - -static tree -fold_builtin_logarithm (location_t loc, tree fndecl, tree arg, - int (*func)(mpfr_ptr, mpfr_srcptr, mp_rnd_t)) -{ - if (validate_arg (arg, REAL_TYPE)) - { - tree type = TREE_TYPE (TREE_TYPE (fndecl)); - tree res; - const enum built_in_function fcode = builtin_mathfn_code (arg); - - /* Calculate the result when the argument is a constant. */ - if ((res = do_mpfr_arg1 (arg, type, func, &dconst0, NULL, false))) - return res; - - /* Special case, optimize logN(expN(x)) = x. */ - if (flag_unsafe_math_optimizations - && ((func == mpfr_log - && (fcode == BUILT_IN_EXP - || fcode == BUILT_IN_EXPF - || fcode == BUILT_IN_EXPL)) - || (func == mpfr_log2 - && (fcode == BUILT_IN_EXP2 - || fcode == BUILT_IN_EXP2F - || fcode == BUILT_IN_EXP2L)) - || (func == mpfr_log10 && (BUILTIN_EXP10_P (fcode))))) - return fold_convert_loc (loc, type, CALL_EXPR_ARG (arg, 0)); - - /* Optimize logN(func()) for various exponential functions. We - want to determine the value "x" and the power "exponent" in - order to transform logN(x**exponent) into exponent*logN(x). */ - if (flag_unsafe_math_optimizations) - { - tree exponent = 0, x = 0; - - switch (fcode) - { - CASE_FLT_FN (BUILT_IN_EXP): - /* Prepare to do logN(exp(exponent) -> exponent*logN(e). */ - x = build_real (type, real_value_truncate (TYPE_MODE (type), - dconst_e ())); - exponent = CALL_EXPR_ARG (arg, 0); - break; - CASE_FLT_FN (BUILT_IN_EXP2): - /* Prepare to do logN(exp2(exponent) -> exponent*logN(2). */ - x = build_real (type, dconst2); - exponent = CALL_EXPR_ARG (arg, 0); - break; - CASE_FLT_FN (BUILT_IN_EXP10): - CASE_FLT_FN (BUILT_IN_POW10): - /* Prepare to do logN(exp10(exponent) -> exponent*logN(10). */ - { - REAL_VALUE_TYPE dconst10; - real_from_integer (&dconst10, VOIDmode, 10, 0, 0); - x = build_real (type, dconst10); - } - exponent = CALL_EXPR_ARG (arg, 0); - break; - CASE_FLT_FN (BUILT_IN_SQRT): - /* Prepare to do logN(sqrt(x) -> 0.5*logN(x). */ - x = CALL_EXPR_ARG (arg, 0); - exponent = build_real (type, dconsthalf); - break; - CASE_FLT_FN (BUILT_IN_CBRT): - /* Prepare to do logN(cbrt(x) -> (1/3)*logN(x). */ - x = CALL_EXPR_ARG (arg, 0); - exponent = build_real (type, real_value_truncate (TYPE_MODE (type), - dconst_third ())); - break; - CASE_FLT_FN (BUILT_IN_POW): - /* Prepare to do logN(pow(x,exponent) -> exponent*logN(x). */ - x = CALL_EXPR_ARG (arg, 0); - exponent = CALL_EXPR_ARG (arg, 1); - break; - default: - break; - } - - /* Now perform the optimization. */ - if (x && exponent) - { - tree logfn = build_call_expr_loc (loc, fndecl, 1, x); - return fold_build2_loc (loc, MULT_EXPR, type, exponent, logfn); - } - } } return NULL_TREE; @@ -8442,7 +8557,7 @@ fold_builtin_pow (location_t loc, tree fndecl, tree arg0, tree arg1, tree type) /* Check for an integer exponent. */ n = real_to_integer (&c); - real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); + real_from_integer (&cint, VOIDmode, n, SIGNED); if (real_identical (&c, &cint)) { /* Attempt to evaluate pow at compile-time, unless this should @@ -8613,570 +8728,6 @@ fold_builtin_exponent (location_t loc, tree fndecl, tree arg, return NULL_TREE; } -/* Return true if VAR is a VAR_DECL or a component thereof. */ - -static bool -var_decl_component_p (tree var) -{ - tree inner = var; - while (handled_component_p (inner)) - inner = TREE_OPERAND (inner, 0); - return SSA_VAR_P (inner); -} - -/* Fold function call to builtin memset. Return - NULL_TREE if no simplification can be made. */ - -static tree -fold_builtin_memset (location_t loc, tree dest, tree c, tree len, - tree type, bool ignore) -{ - tree var, ret, etype; - unsigned HOST_WIDE_INT length, cval; - - if (! validate_arg (dest, POINTER_TYPE) - || ! validate_arg (c, INTEGER_TYPE) - || ! validate_arg (len, INTEGER_TYPE)) - return NULL_TREE; - - if (! tree_fits_uhwi_p (len)) - return NULL_TREE; - - /* If the LEN parameter is zero, return DEST. */ - if (integer_zerop (len)) - return omit_one_operand_loc (loc, type, dest, c); - - if (TREE_CODE (c) != INTEGER_CST || TREE_SIDE_EFFECTS (dest)) - return NULL_TREE; - - var = dest; - STRIP_NOPS (var); - if (TREE_CODE (var) != ADDR_EXPR) - return NULL_TREE; - - var = TREE_OPERAND (var, 0); - if (TREE_THIS_VOLATILE (var)) - return NULL_TREE; - - etype = TREE_TYPE (var); - if (TREE_CODE (etype) == ARRAY_TYPE) - etype = TREE_TYPE (etype); - - if (!INTEGRAL_TYPE_P (etype) - && !POINTER_TYPE_P (etype)) - return NULL_TREE; - - if (! var_decl_component_p (var)) - return NULL_TREE; - - length = tree_to_uhwi (len); - if (GET_MODE_SIZE (TYPE_MODE (etype)) != length - || get_pointer_alignment (dest) / BITS_PER_UNIT < length) - return NULL_TREE; - - if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT) - return NULL_TREE; - - if (integer_zerop (c)) - cval = 0; - else - { - if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64) - return NULL_TREE; - - cval = TREE_INT_CST_LOW (c); - cval &= 0xff; - cval |= cval << 8; - cval |= cval << 16; - cval |= (cval << 31) << 1; - } - - ret = build_int_cst_type (etype, cval); - var = build_fold_indirect_ref_loc (loc, - fold_convert_loc (loc, - build_pointer_type (etype), - dest)); - ret = build2 (MODIFY_EXPR, etype, var, ret); - if (ignore) - return ret; - - return omit_one_operand_loc (loc, type, dest, ret); -} - -/* Fold function call to builtin memset. Return - NULL_TREE if no simplification can be made. */ - -static tree -fold_builtin_bzero (location_t loc, tree dest, tree size, bool ignore) -{ - if (! validate_arg (dest, POINTER_TYPE) - || ! validate_arg (size, INTEGER_TYPE)) - return NULL_TREE; - - if (!ignore) - return NULL_TREE; - - /* New argument list transforming bzero(ptr x, int y) to - memset(ptr x, int 0, size_t y). This is done this way - so that if it isn't expanded inline, we fallback to - calling bzero instead of memset. */ - - return fold_builtin_memset (loc, dest, integer_zero_node, - fold_convert_loc (loc, size_type_node, size), - void_type_node, ignore); -} - -/* Fold function call to builtin mem{{,p}cpy,move}. Return - NULL_TREE if no simplification can be made. - If ENDP is 0, return DEST (like memcpy). - If ENDP is 1, return DEST+LEN (like mempcpy). - If ENDP is 2, return DEST+LEN-1 (like stpcpy). - If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap - (memmove). */ - -static tree -fold_builtin_memory_op (location_t loc, tree dest, tree src, - tree len, tree type, bool ignore, int endp) -{ - tree destvar, srcvar, expr; - - if (! validate_arg (dest, POINTER_TYPE) - || ! validate_arg (src, POINTER_TYPE) - || ! validate_arg (len, INTEGER_TYPE)) - return NULL_TREE; - - /* If the LEN parameter is zero, return DEST. */ - if (integer_zerop (len)) - return omit_one_operand_loc (loc, type, dest, src); - - /* If SRC and DEST are the same (and not volatile), return - DEST{,+LEN,+LEN-1}. */ - if (operand_equal_p (src, dest, 0)) - expr = len; - else - { - tree srctype, desttype; - unsigned int src_align, dest_align; - tree off0; - - if (endp == 3) - { - src_align = get_pointer_alignment (src); - dest_align = get_pointer_alignment (dest); - - /* Both DEST and SRC must be pointer types. - ??? This is what old code did. Is the testing for pointer types - really mandatory? - - If either SRC is readonly or length is 1, we can use memcpy. */ - if (!dest_align || !src_align) - return NULL_TREE; - if (readonly_data_expr (src) - || (tree_fits_uhwi_p (len) - && (MIN (src_align, dest_align) / BITS_PER_UNIT - >= tree_to_uhwi (len)))) - { - tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY); - if (!fn) - return NULL_TREE; - return build_call_expr_loc (loc, fn, 3, dest, src, len); - } - - /* If *src and *dest can't overlap, optimize into memcpy as well. */ - if (TREE_CODE (src) == ADDR_EXPR - && TREE_CODE (dest) == ADDR_EXPR) - { - tree src_base, dest_base, fn; - HOST_WIDE_INT src_offset = 0, dest_offset = 0; - HOST_WIDE_INT size = -1; - HOST_WIDE_INT maxsize = -1; - - srcvar = TREE_OPERAND (src, 0); - src_base = get_ref_base_and_extent (srcvar, &src_offset, - &size, &maxsize); - destvar = TREE_OPERAND (dest, 0); - dest_base = get_ref_base_and_extent (destvar, &dest_offset, - &size, &maxsize); - if (tree_fits_uhwi_p (len)) - maxsize = tree_to_uhwi (len); - else - maxsize = -1; - src_offset /= BITS_PER_UNIT; - dest_offset /= BITS_PER_UNIT; - if (SSA_VAR_P (src_base) - && SSA_VAR_P (dest_base)) - { - if (operand_equal_p (src_base, dest_base, 0) - && ranges_overlap_p (src_offset, maxsize, - dest_offset, maxsize)) - return NULL_TREE; - } - else if (TREE_CODE (src_base) == MEM_REF - && TREE_CODE (dest_base) == MEM_REF) - { - double_int off; - if (! operand_equal_p (TREE_OPERAND (src_base, 0), - TREE_OPERAND (dest_base, 0), 0)) - return NULL_TREE; - off = mem_ref_offset (src_base) + - double_int::from_shwi (src_offset); - if (!off.fits_shwi ()) - return NULL_TREE; - src_offset = off.low; - off = mem_ref_offset (dest_base) + - double_int::from_shwi (dest_offset); - if (!off.fits_shwi ()) - return NULL_TREE; - dest_offset = off.low; - if (ranges_overlap_p (src_offset, maxsize, - dest_offset, maxsize)) - return NULL_TREE; - } - else - return NULL_TREE; - - fn = builtin_decl_implicit (BUILT_IN_MEMCPY); - if (!fn) - return NULL_TREE; - return build_call_expr_loc (loc, fn, 3, dest, src, len); - } - - /* If the destination and source do not alias optimize into - memcpy as well. */ - if ((is_gimple_min_invariant (dest) - || TREE_CODE (dest) == SSA_NAME) - && (is_gimple_min_invariant (src) - || TREE_CODE (src) == SSA_NAME)) - { - ao_ref destr, srcr; - ao_ref_init_from_ptr_and_size (&destr, dest, len); - ao_ref_init_from_ptr_and_size (&srcr, src, len); - if (!refs_may_alias_p_1 (&destr, &srcr, false)) - { - tree fn; - fn = builtin_decl_implicit (BUILT_IN_MEMCPY); - if (!fn) - return NULL_TREE; - return build_call_expr_loc (loc, fn, 3, dest, src, len); - } - } - - return NULL_TREE; - } - - if (!tree_fits_shwi_p (len)) - return NULL_TREE; - /* FIXME: - This logic lose for arguments like (type *)malloc (sizeof (type)), - since we strip the casts of up to VOID return value from malloc. - Perhaps we ought to inherit type from non-VOID argument here? */ - STRIP_NOPS (src); - STRIP_NOPS (dest); - if (!POINTER_TYPE_P (TREE_TYPE (src)) - || !POINTER_TYPE_P (TREE_TYPE (dest))) - return NULL_TREE; - /* In the following try to find a type that is most natural to be - used for the memcpy source and destination and that allows - the most optimization when memcpy is turned into a plain assignment - using that type. In theory we could always use a char[len] type - but that only gains us that the destination and source possibly - no longer will have their address taken. */ - /* As we fold (void *)(p + CST) to (void *)p + CST undo this here. */ - if (TREE_CODE (src) == POINTER_PLUS_EXPR) - { - tree tem = TREE_OPERAND (src, 0); - STRIP_NOPS (tem); - if (tem != TREE_OPERAND (src, 0)) - src = build1 (NOP_EXPR, TREE_TYPE (tem), src); - } - if (TREE_CODE (dest) == POINTER_PLUS_EXPR) - { - tree tem = TREE_OPERAND (dest, 0); - STRIP_NOPS (tem); - if (tem != TREE_OPERAND (dest, 0)) - dest = build1 (NOP_EXPR, TREE_TYPE (tem), dest); - } - srctype = TREE_TYPE (TREE_TYPE (src)); - if (TREE_CODE (srctype) == ARRAY_TYPE - && !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len)) - { - srctype = TREE_TYPE (srctype); - STRIP_NOPS (src); - src = build1 (NOP_EXPR, build_pointer_type (srctype), src); - } - desttype = TREE_TYPE (TREE_TYPE (dest)); - if (TREE_CODE (desttype) == ARRAY_TYPE - && !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len)) - { - desttype = TREE_TYPE (desttype); - STRIP_NOPS (dest); - dest = build1 (NOP_EXPR, build_pointer_type (desttype), dest); - } - if (TREE_ADDRESSABLE (srctype) - || TREE_ADDRESSABLE (desttype)) - return NULL_TREE; - - /* Make sure we are not copying using a floating-point mode or - a type whose size possibly does not match its precision. */ - if (FLOAT_MODE_P (TYPE_MODE (desttype)) - || TREE_CODE (desttype) == BOOLEAN_TYPE - || TREE_CODE (desttype) == ENUMERAL_TYPE) - { - /* A more suitable int_mode_for_mode would return a vector - integer mode for a vector float mode or a integer complex - mode for a float complex mode if there isn't a regular - integer mode covering the mode of desttype. */ - enum machine_mode mode = int_mode_for_mode (TYPE_MODE (desttype)); - if (mode == BLKmode) - desttype = NULL_TREE; - else - desttype = build_nonstandard_integer_type (GET_MODE_BITSIZE (mode), - 1); - } - if (FLOAT_MODE_P (TYPE_MODE (srctype)) - || TREE_CODE (srctype) == BOOLEAN_TYPE - || TREE_CODE (srctype) == ENUMERAL_TYPE) - { - enum machine_mode mode = int_mode_for_mode (TYPE_MODE (srctype)); - if (mode == BLKmode) - srctype = NULL_TREE; - else - srctype = build_nonstandard_integer_type (GET_MODE_BITSIZE (mode), - 1); - } - if (!srctype) - srctype = desttype; - if (!desttype) - desttype = srctype; - if (!srctype) - return NULL_TREE; - - src_align = get_pointer_alignment (src); - dest_align = get_pointer_alignment (dest); - if (dest_align < TYPE_ALIGN (desttype) - || src_align < TYPE_ALIGN (srctype)) - return NULL_TREE; - - if (!ignore) - dest = builtin_save_expr (dest); - - /* Build accesses at offset zero with a ref-all character type. */ - off0 = build_int_cst (build_pointer_type_for_mode (char_type_node, - ptr_mode, true), 0); - - destvar = dest; - STRIP_NOPS (destvar); - if (TREE_CODE (destvar) == ADDR_EXPR - && var_decl_component_p (TREE_OPERAND (destvar, 0)) - && tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len)) - destvar = fold_build2 (MEM_REF, desttype, destvar, off0); - else - destvar = NULL_TREE; - - srcvar = src; - STRIP_NOPS (srcvar); - if (TREE_CODE (srcvar) == ADDR_EXPR - && var_decl_component_p (TREE_OPERAND (srcvar, 0)) - && tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len)) - { - if (!destvar - || src_align >= TYPE_ALIGN (desttype)) - srcvar = fold_build2 (MEM_REF, destvar ? desttype : srctype, - srcvar, off0); - else if (!STRICT_ALIGNMENT) - { - srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype), - src_align); - srcvar = fold_build2 (MEM_REF, srctype, srcvar, off0); - } - else - srcvar = NULL_TREE; - } - else - srcvar = NULL_TREE; - - if (srcvar == NULL_TREE && destvar == NULL_TREE) - return NULL_TREE; - - if (srcvar == NULL_TREE) - { - STRIP_NOPS (src); - if (src_align >= TYPE_ALIGN (desttype)) - srcvar = fold_build2 (MEM_REF, desttype, src, off0); - else - { - if (STRICT_ALIGNMENT) - return NULL_TREE; - srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype), - src_align); - srcvar = fold_build2 (MEM_REF, srctype, src, off0); - } - } - else if (destvar == NULL_TREE) - { - STRIP_NOPS (dest); - if (dest_align >= TYPE_ALIGN (srctype)) - destvar = fold_build2 (MEM_REF, srctype, dest, off0); - else - { - if (STRICT_ALIGNMENT) - return NULL_TREE; - desttype = build_aligned_type (TYPE_MAIN_VARIANT (srctype), - dest_align); - destvar = fold_build2 (MEM_REF, desttype, dest, off0); - } - } - - expr = build2 (MODIFY_EXPR, TREE_TYPE (destvar), destvar, srcvar); - } - - if (ignore) - return expr; - - if (endp == 0 || endp == 3) - return omit_one_operand_loc (loc, type, dest, expr); - - if (expr == len) - expr = NULL_TREE; - - if (endp == 2) - len = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (len), len, - ssize_int (1)); - - dest = fold_build_pointer_plus_loc (loc, dest, len); - dest = fold_convert_loc (loc, type, dest); - if (expr) - dest = omit_one_operand_loc (loc, type, dest, expr); - return dest; -} - -/* Fold function call to builtin strcpy with arguments DEST and SRC. - If LEN is not NULL, it represents the length of the string to be - copied. Return NULL_TREE if no simplification can be made. */ - -tree -fold_builtin_strcpy (location_t loc, tree fndecl, tree dest, tree src, tree len) -{ - tree fn; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE)) - return NULL_TREE; - - /* If SRC and DEST are the same (and not volatile), return DEST. */ - if (operand_equal_p (src, dest, 0)) - return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest); - - if (optimize_function_for_size_p (cfun)) - return NULL_TREE; - - fn = builtin_decl_implicit (BUILT_IN_MEMCPY); - if (!fn) - return NULL_TREE; - - if (!len) - { - len = c_strlen (src, 1); - if (! len || TREE_SIDE_EFFECTS (len)) - return NULL_TREE; - } - - len = fold_convert_loc (loc, size_type_node, len); - len = size_binop_loc (loc, PLUS_EXPR, len, build_int_cst (size_type_node, 1)); - return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), - build_call_expr_loc (loc, fn, 3, dest, src, len)); -} - -/* Fold function call to builtin stpcpy with arguments DEST and SRC. - Return NULL_TREE if no simplification can be made. */ - -static tree -fold_builtin_stpcpy (location_t loc, tree fndecl, tree dest, tree src) -{ - tree fn, len, lenp1, call, type; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE)) - return NULL_TREE; - - len = c_strlen (src, 1); - if (!len - || TREE_CODE (len) != INTEGER_CST) - return NULL_TREE; - - if (optimize_function_for_size_p (cfun) - /* If length is zero it's small enough. */ - && !integer_zerop (len)) - return NULL_TREE; - - fn = builtin_decl_implicit (BUILT_IN_MEMCPY); - if (!fn) - return NULL_TREE; - - lenp1 = size_binop_loc (loc, PLUS_EXPR, - fold_convert_loc (loc, size_type_node, len), - build_int_cst (size_type_node, 1)); - /* We use dest twice in building our expression. Save it from - multiple expansions. */ - dest = builtin_save_expr (dest); - call = build_call_expr_loc (loc, fn, 3, dest, src, lenp1); - - type = TREE_TYPE (TREE_TYPE (fndecl)); - dest = fold_build_pointer_plus_loc (loc, dest, len); - dest = fold_convert_loc (loc, type, dest); - dest = omit_one_operand_loc (loc, type, dest, call); - return dest; -} - -/* Fold function call to builtin strncpy with arguments DEST, SRC, and LEN. - If SLEN is not NULL, it represents the length of the source string. - Return NULL_TREE if no simplification can be made. */ - -tree -fold_builtin_strncpy (location_t loc, tree fndecl, tree dest, - tree src, tree len, tree slen) -{ - tree fn; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE) - || !validate_arg (len, INTEGER_TYPE)) - return NULL_TREE; - - /* If the LEN parameter is zero, return DEST. */ - if (integer_zerop (len)) - return omit_one_operand_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest, src); - - /* We can't compare slen with len as constants below if len is not a - constant. */ - if (len == 0 || TREE_CODE (len) != INTEGER_CST) - return NULL_TREE; - - if (!slen) - slen = c_strlen (src, 1); - - /* Now, we must be passed a constant src ptr parameter. */ - if (slen == 0 || TREE_CODE (slen) != INTEGER_CST) - return NULL_TREE; - - slen = size_binop_loc (loc, 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 NULL_TREE; - - /* OK transform into builtin memcpy. */ - fn = builtin_decl_implicit (BUILT_IN_MEMCPY); - if (!fn) - return NULL_TREE; - - len = fold_convert_loc (loc, size_type_node, len); - return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), - build_call_expr_loc (loc, fn, 3, dest, src, len)); -} - /* Fold function call to builtin memchr. ARG1, ARG2 and LEN are the arguments to the call, and TYPE is its return type. Return NULL_TREE if no simplification can be made. */ @@ -9469,7 +9020,7 @@ fold_builtin_signbit (location_t loc, tree arg, tree type) return omit_one_operand_loc (loc, type, integer_zero_node, arg); /* If ARG's format doesn't have signed zeros, return "arg < 0.0". */ - if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg)))) + if (!HONOR_SIGNED_ZEROS (arg)) return fold_convert (type, fold_build2_loc (loc, LT_EXPR, boolean_type_node, arg, build_real (TREE_TYPE (arg), dconst0))); @@ -9663,12 +9214,12 @@ fold_builtin_fmin_fmax (location_t loc, tree arg0, tree arg1, omit_one_operand() ensures we create a non-lvalue. */ if (TREE_CODE (arg0) == REAL_CST && real_isnan (&TREE_REAL_CST (arg0)) - && (! HONOR_SNANS (TYPE_MODE (TREE_TYPE (arg0))) + && (! HONOR_SNANS (arg0) || ! TREE_REAL_CST (arg0).signalling)) return omit_one_operand_loc (loc, type, arg1, arg0); if (TREE_CODE (arg1) == REAL_CST && real_isnan (&TREE_REAL_CST (arg1)) - && (! HONOR_SNANS (TYPE_MODE (TREE_TYPE (arg1))) + && (! HONOR_SNANS (arg1) || ! TREE_REAL_CST (arg1).signalling)) return omit_one_operand_loc (loc, type, arg0, arg1); @@ -9982,7 +9533,7 @@ fold_builtin_modf (location_t loc, tree arg0, tree arg1, tree rettype) static tree fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg) { - enum machine_mode mode; + machine_mode mode; if (!validate_arg (arg, REAL_TYPE)) return NULL_TREE; @@ -10079,7 +9630,7 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index) switch (builtin_index) { case BUILT_IN_ISINF: - if (!HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (arg)))) + if (!HONOR_INFINITIES (arg)) return omit_one_operand_loc (loc, type, integer_zero_node, arg); if (TREE_CODE (arg) == REAL_CST) @@ -10127,8 +9678,8 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index) } case BUILT_IN_ISFINITE: - if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg))) - && !HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (arg)))) + if (!HONOR_NANS (arg) + && !HONOR_INFINITIES (arg)) return omit_one_operand_loc (loc, type, integer_one_node, arg); if (TREE_CODE (arg) == REAL_CST) @@ -10140,7 +9691,7 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index) return NULL_TREE; case BUILT_IN_ISNAN: - if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg)))) + if (!HONOR_NANS (arg)) return omit_one_operand_loc (loc, type, integer_zero_node, arg); if (TREE_CODE (arg) == REAL_CST) @@ -10166,26 +9717,30 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index) one floating point argument which is "type generic". */ static tree -fold_builtin_fpclassify (location_t loc, tree exp) +fold_builtin_fpclassify (location_t loc, tree *args, int nargs) { tree fp_nan, fp_infinite, fp_normal, fp_subnormal, fp_zero, arg, type, res, tmp; - enum machine_mode mode; + machine_mode mode; REAL_VALUE_TYPE r; char buf[128]; /* Verify the required arguments in the original call. */ - if (!validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, - INTEGER_TYPE, INTEGER_TYPE, - INTEGER_TYPE, REAL_TYPE, VOID_TYPE)) - return NULL_TREE; - - fp_nan = CALL_EXPR_ARG (exp, 0); - fp_infinite = CALL_EXPR_ARG (exp, 1); - fp_normal = CALL_EXPR_ARG (exp, 2); - fp_subnormal = CALL_EXPR_ARG (exp, 3); - fp_zero = CALL_EXPR_ARG (exp, 4); - arg = CALL_EXPR_ARG (exp, 5); + if (nargs != 6 + || !validate_arg (args[0], INTEGER_TYPE) + || !validate_arg (args[1], INTEGER_TYPE) + || !validate_arg (args[2], INTEGER_TYPE) + || !validate_arg (args[3], INTEGER_TYPE) + || !validate_arg (args[4], INTEGER_TYPE) + || !validate_arg (args[5], REAL_TYPE)) + return NULL_TREE; + + fp_nan = args[0]; + fp_infinite = args[1]; + fp_normal = args[2]; + fp_subnormal = args[3]; + fp_zero = args[4]; + arg = args[5]; type = TREE_TYPE (arg); mode = TYPE_MODE (type); arg = builtin_save_expr (fold_build1_loc (loc, ABS_EXPR, type, arg)); @@ -10264,23 +9819,77 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1, if (unordered_code == UNORDERED_EXPR) { - if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg0)))) + if (!HONOR_NANS (arg0)) return omit_two_operands_loc (loc, type, integer_zero_node, arg0, arg1); return fold_build2_loc (loc, UNORDERED_EXPR, type, arg0, arg1); } - code = HONOR_NANS (TYPE_MODE (TREE_TYPE (arg0))) ? unordered_code - : ordered_code; + code = HONOR_NANS (arg0) ? unordered_code : ordered_code; return fold_build1_loc (loc, TRUTH_NOT_EXPR, type, fold_build2_loc (loc, code, type, arg0, arg1)); } +/* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal + arithmetics if it can never overflow, or into internal functions that + return both result of arithmetics and overflowed boolean flag in + a complex integer result, or some other check for overflow. */ + +static tree +fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode, + tree arg0, tree arg1, tree arg2) +{ + enum internal_fn ifn = IFN_LAST; + tree type = TREE_TYPE (TREE_TYPE (arg2)); + tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2); + switch (fcode) + { + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + ifn = IFN_ADD_OVERFLOW; + break; + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + ifn = IFN_SUB_OVERFLOW; + break; + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + ifn = IFN_MUL_OVERFLOW; + break; + default: + gcc_unreachable (); + } + tree ctype = build_complex_type (type); + tree call = build_call_expr_internal_loc (loc, ifn, ctype, + 2, arg0, arg1); + tree tgt = save_expr (call); + tree intres = build1_loc (loc, REALPART_EXPR, type, tgt); + tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt); + ovfres = fold_convert_loc (loc, boolean_type_node, ovfres); + tree store + = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres); + return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres); +} + /* Fold a call to built-in function FNDECL with 0 arguments. - IGNORE is true if the result of the function call is ignored. This - function returns NULL_TREE if no simplification was possible. */ + This function returns NULL_TREE if no simplification was possible. */ static tree -fold_builtin_0 (location_t loc, tree fndecl, bool ignore ATTRIBUTE_UNUSED) +fold_builtin_0 (location_t loc, tree fndecl) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); @@ -10298,14 +9907,6 @@ fold_builtin_0 (location_t loc, tree fndecl, bool ignore ATTRIBUTE_UNUSED) case BUILT_IN_CLASSIFY_TYPE: return fold_builtin_classify_type (NULL_TREE); - case BUILT_IN_UNREACHABLE: - if (flag_sanitize & SANITIZE_UNREACHABLE - && (current_function_decl == NULL - || !lookup_attribute ("no_sanitize_undefined", - DECL_ATTRIBUTES (current_function_decl)))) - return ubsan_instrument_unreachable (loc); - break; - default: break; } @@ -10313,11 +9914,10 @@ fold_builtin_0 (location_t loc, tree fndecl, bool ignore ATTRIBUTE_UNUSED) } /* Fold a call to built-in function FNDECL with 1 argument, ARG0. - IGNORE is true if the result of the function call is ignored. This - function returns NULL_TREE if no simplification was possible. */ + This function returns NULL_TREE if no simplification was possible. */ static tree -fold_builtin_1 (location_t loc, tree fndecl, tree arg0, bool ignore) +fold_builtin_1 (location_t loc, tree fndecl, tree arg0) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); @@ -10559,16 +10159,22 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0, bool ignore) CASE_FLT_FN (BUILT_IN_EXPM1): if (validate_arg (arg0, REAL_TYPE)) return do_mpfr_arg1 (arg0, type, mpfr_expm1, NULL, NULL, 0); - break; + break; CASE_FLT_FN (BUILT_IN_LOG): - return fold_builtin_logarithm (loc, fndecl, arg0, mpfr_log); + if (validate_arg (arg0, REAL_TYPE)) + return do_mpfr_arg1 (arg0, type, mpfr_log, &dconst0, NULL, false); + break; CASE_FLT_FN (BUILT_IN_LOG2): - return fold_builtin_logarithm (loc, fndecl, arg0, mpfr_log2); + if (validate_arg (arg0, REAL_TYPE)) + return do_mpfr_arg1 (arg0, type, mpfr_log2, &dconst0, NULL, false); + break; CASE_FLT_FN (BUILT_IN_LOG10): - return fold_builtin_logarithm (loc, fndecl, arg0, mpfr_log10); + if (validate_arg (arg0, REAL_TYPE)) + return do_mpfr_arg1 (arg0, type, mpfr_log10, &dconst0, NULL, false); + break; CASE_FLT_FN (BUILT_IN_LOG1P): if (validate_arg (arg0, REAL_TYPE)) @@ -10708,11 +10314,6 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0, bool ignore) case BUILT_IN_ISNAND128: return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISNAN); - case BUILT_IN_PRINTF: - case BUILT_IN_PRINTF_UNLOCKED: - case BUILT_IN_VPRINTF: - return fold_builtin_printf (loc, fndecl, arg0, NULL_TREE, ignore, fcode); - case BUILT_IN_FREE: if (integer_zerop (arg0)) return build_empty_stmt (loc); @@ -10727,11 +10328,10 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0, bool ignore) } /* Fold a call to built-in function FNDECL with 2 arguments, ARG0 and ARG1. - IGNORE is true if the result of the function call is ignored. This - function returns NULL_TREE if no simplification was possible. */ + This function returns NULL_TREE if no simplification was possible. */ static tree -fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) +fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); @@ -10801,21 +10401,9 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) CASE_FLT_FN (BUILT_IN_MODF): return fold_builtin_modf (loc, arg0, arg1, type); - case BUILT_IN_BZERO: - return fold_builtin_bzero (loc, arg0, arg1, ignore); - - case BUILT_IN_FPUTS: - return fold_builtin_fputs (loc, arg0, arg1, ignore, false, NULL_TREE); - - case BUILT_IN_FPUTS_UNLOCKED: - return fold_builtin_fputs (loc, arg0, arg1, ignore, true, NULL_TREE); - case BUILT_IN_STRSTR: return fold_builtin_strstr (loc, arg0, arg1, type); - case BUILT_IN_STRCAT: - return fold_builtin_strcat (loc, arg0, arg1, NULL_TREE); - case BUILT_IN_STRSPN: return fold_builtin_strspn (loc, arg0, arg1); @@ -10830,22 +10418,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) case BUILT_IN_RINDEX: return fold_builtin_strrchr (loc, arg0, arg1, type); - case BUILT_IN_STRCPY: - return fold_builtin_strcpy (loc, fndecl, arg0, arg1, NULL_TREE); - - case BUILT_IN_STPCPY: - if (ignore) - { - tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); - if (!fn) - break; - - return build_call_expr_loc (loc, fn, 2, arg0, arg1); - } - else - return fold_builtin_stpcpy (loc, fndecl, arg0, arg1); - break; - case BUILT_IN_STRCMP: return fold_builtin_strcmp (loc, arg0, arg1); @@ -10894,33 +10466,9 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) case BUILT_IN_VA_START: break; - case BUILT_IN_SPRINTF: - return fold_builtin_sprintf (loc, arg0, arg1, NULL_TREE, ignore); - case BUILT_IN_OBJECT_SIZE: return fold_builtin_object_size (arg0, arg1); - case BUILT_IN_PRINTF: - case BUILT_IN_PRINTF_UNLOCKED: - case BUILT_IN_VPRINTF: - return fold_builtin_printf (loc, fndecl, arg0, arg1, ignore, fcode); - - case BUILT_IN_PRINTF_CHK: - case BUILT_IN_VPRINTF_CHK: - if (!validate_arg (arg0, INTEGER_TYPE) - || TREE_SIDE_EFFECTS (arg0)) - return NULL_TREE; - else - return fold_builtin_printf (loc, fndecl, - arg1, NULL_TREE, ignore, fcode); - break; - - case BUILT_IN_FPRINTF: - case BUILT_IN_FPRINTF_UNLOCKED: - case BUILT_IN_VFPRINTF: - return fold_builtin_fprintf (loc, fndecl, arg0, arg1, NULL_TREE, - ignore, fcode); - case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: return fold_builtin_atomic_always_lock_free (arg0, arg1); @@ -10934,12 +10482,12 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) } /* Fold a call to built-in function FNDECL with 3 arguments, ARG0, ARG1, - and ARG2. IGNORE is true if the result of the function call is ignored. + and ARG2. This function returns NULL_TREE if no simplification was possible. */ static tree fold_builtin_3 (location_t loc, tree fndecl, - tree arg0, tree arg1, tree arg2, bool ignore) + tree arg0, tree arg1, tree arg2) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); @@ -10960,31 +10508,6 @@ fold_builtin_3 (location_t loc, tree fndecl, return do_mpfr_remquo (arg0, arg1, arg2); break; - case BUILT_IN_MEMSET: - return fold_builtin_memset (loc, arg0, arg1, arg2, type, ignore); - - case BUILT_IN_BCOPY: - return fold_builtin_memory_op (loc, arg1, arg0, arg2, - void_type_node, true, /*endp=*/3); - - case BUILT_IN_MEMCPY: - return fold_builtin_memory_op (loc, arg0, arg1, arg2, - type, ignore, /*endp=*/0); - - case BUILT_IN_MEMPCPY: - return fold_builtin_memory_op (loc, arg0, arg1, arg2, - type, ignore, /*endp=*/1); - - case BUILT_IN_MEMMOVE: - return fold_builtin_memory_op (loc, arg0, arg1, arg2, - type, ignore, /*endp=*/3); - - case BUILT_IN_STRNCAT: - return fold_builtin_strncat (loc, arg0, arg1, arg2); - - case BUILT_IN_STRNCPY: - return fold_builtin_strncpy (loc, fndecl, arg0, arg1, arg2, NULL_TREE); - case BUILT_IN_STRNCMP: return fold_builtin_strncmp (loc, arg0, arg1, arg2); @@ -10995,94 +10518,31 @@ fold_builtin_3 (location_t loc, tree fndecl, case BUILT_IN_MEMCMP: return fold_builtin_memcmp (loc, arg0, arg1, arg2);; - case BUILT_IN_SPRINTF: - return fold_builtin_sprintf (loc, arg0, arg1, arg2, ignore); - - case BUILT_IN_SNPRINTF: - return fold_builtin_snprintf (loc, arg0, arg1, arg2, NULL_TREE, ignore); - - case BUILT_IN_STRCPY_CHK: - case BUILT_IN_STPCPY_CHK: - return fold_builtin_stxcpy_chk (loc, fndecl, arg0, arg1, arg2, NULL_TREE, - ignore, fcode); - - case BUILT_IN_STRCAT_CHK: - return fold_builtin_strcat_chk (loc, fndecl, arg0, arg1, arg2); - - case BUILT_IN_PRINTF_CHK: - case BUILT_IN_VPRINTF_CHK: - if (!validate_arg (arg0, INTEGER_TYPE) - || TREE_SIDE_EFFECTS (arg0)) - return NULL_TREE; - else - return fold_builtin_printf (loc, fndecl, arg1, arg2, ignore, fcode); - break; - - case BUILT_IN_FPRINTF: - case BUILT_IN_FPRINTF_UNLOCKED: - case BUILT_IN_VFPRINTF: - return fold_builtin_fprintf (loc, fndecl, arg0, arg1, arg2, - ignore, fcode); - - case BUILT_IN_FPRINTF_CHK: - case BUILT_IN_VFPRINTF_CHK: - if (!validate_arg (arg1, INTEGER_TYPE) - || TREE_SIDE_EFFECTS (arg1)) - return NULL_TREE; - else - return fold_builtin_fprintf (loc, fndecl, arg0, arg2, NULL_TREE, - ignore, fcode); - case BUILT_IN_EXPECT: return fold_builtin_expect (loc, arg0, arg1, arg2); - default: - break; - } - return NULL_TREE; -} - -/* Fold a call to built-in function FNDECL with 4 arguments, ARG0, ARG1, - ARG2, and ARG3. IGNORE is true if the result of the function call is - ignored. This function returns NULL_TREE if no simplification was - possible. */ - -static tree -fold_builtin_4 (location_t loc, tree fndecl, - tree arg0, tree arg1, tree arg2, tree arg3, bool ignore) -{ - enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); - - switch (fcode) - { - 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 (loc, fndecl, arg0, arg1, arg2, arg3, - NULL_TREE, ignore, - DECL_FUNCTION_CODE (fndecl)); - - case BUILT_IN_STRNCPY_CHK: - case BUILT_IN_STPNCPY_CHK: - return fold_builtin_stxncpy_chk (loc, arg0, arg1, arg2, arg3, NULL_TREE, - ignore, fcode); - - case BUILT_IN_STRNCAT_CHK: - return fold_builtin_strncat_chk (loc, fndecl, arg0, arg1, arg2, arg3); - - case BUILT_IN_SNPRINTF: - return fold_builtin_snprintf (loc, arg0, arg1, arg2, arg3, ignore); - - case BUILT_IN_FPRINTF_CHK: - case BUILT_IN_VFPRINTF_CHK: - if (!validate_arg (arg1, INTEGER_TYPE) - || TREE_SIDE_EFFECTS (arg1)) - return NULL_TREE; - else - return fold_builtin_fprintf (loc, fndecl, arg0, arg2, arg3, - ignore, fcode); - break; + case BUILT_IN_ADD_OVERFLOW: + case BUILT_IN_SUB_OVERFLOW: + case BUILT_IN_MUL_OVERFLOW: + case BUILT_IN_SADD_OVERFLOW: + case BUILT_IN_SADDL_OVERFLOW: + case BUILT_IN_SADDLL_OVERFLOW: + case BUILT_IN_SSUB_OVERFLOW: + case BUILT_IN_SSUBL_OVERFLOW: + case BUILT_IN_SSUBLL_OVERFLOW: + case BUILT_IN_SMUL_OVERFLOW: + case BUILT_IN_SMULL_OVERFLOW: + case BUILT_IN_SMULLL_OVERFLOW: + case BUILT_IN_UADD_OVERFLOW: + case BUILT_IN_UADDL_OVERFLOW: + case BUILT_IN_UADDLL_OVERFLOW: + case BUILT_IN_USUB_OVERFLOW: + case BUILT_IN_USUBL_OVERFLOW: + case BUILT_IN_USUBLL_OVERFLOW: + case BUILT_IN_UMUL_OVERFLOW: + case BUILT_IN_UMULL_OVERFLOW: + case BUILT_IN_UMULLL_OVERFLOW: + return fold_builtin_arith_overflow (loc, fcode, arg0, arg1, arg2); default: break; @@ -11091,39 +10551,31 @@ fold_builtin_4 (location_t loc, tree fndecl, } /* Fold a call to built-in function FNDECL. ARGS is an array of NARGS - arguments, where NARGS <= 4. IGNORE is true if the result of the - function call is ignored. This function returns NULL_TREE if no - simplification was possible. Note that this only folds builtins with - fixed argument patterns. Foldings that do varargs-to-varargs - transformations, or that match calls with more than 4 arguments, - need to be handled with fold_builtin_varargs instead. */ - -#define MAX_ARGS_TO_FOLD_BUILTIN 4 + arguments. IGNORE is true if the result of the + function call is ignored. This function returns NULL_TREE if no + simplification was possible. */ -static tree -fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool ignore) +tree +fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool) { tree ret = NULL_TREE; switch (nargs) { case 0: - ret = fold_builtin_0 (loc, fndecl, ignore); + ret = fold_builtin_0 (loc, fndecl); break; case 1: - ret = fold_builtin_1 (loc, fndecl, args[0], ignore); + ret = fold_builtin_1 (loc, fndecl, args[0]); break; case 2: - ret = fold_builtin_2 (loc, fndecl, args[0], args[1], ignore); + ret = fold_builtin_2 (loc, fndecl, args[0], args[1]); break; case 3: - ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2], ignore); - break; - case 4: - ret = fold_builtin_4 (loc, fndecl, args[0], args[1], args[2], args[3], - ignore); + ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2]); break; default: + ret = fold_builtin_varargs (loc, fndecl, args, nargs); break; } if (ret) @@ -11164,25 +10616,6 @@ rewrite_call_expr_valist (location_t loc, int oldnargs, tree *args, return build_call_expr_loc_array (loc, fndecl, nargs, buffer); } -/* Construct a new CALL_EXPR to FNDECL using the tail of the argument - list ARGS along with N new arguments specified as the "..." - parameters. SKIP is the number of arguments in ARGS to be omitted. - OLDNARGS is the number of elements in ARGS. */ - -static tree -rewrite_call_expr_array (location_t loc, int oldnargs, tree *args, - int skip, tree fndecl, int n, ...) -{ - va_list ap; - tree t; - - va_start (ap, n); - t = rewrite_call_expr_valist (loc, oldnargs, args, skip, fndecl, n, ap); - va_end (ap); - - return t; -} - /* Return true if FNDECL shouldn't be folded right now. If a built-in function has an inline attribute always_inline wrapper, defer folding it after always_inline functions have @@ -11239,13 +10672,8 @@ fold_call_expr (location_t loc, tree exp, bool ignore) CALL_EXPR_ARGP (exp), ignore); else { - if (nargs <= MAX_ARGS_TO_FOLD_BUILTIN) - { - tree *args = CALL_EXPR_ARGP (exp); - ret = fold_builtin_n (loc, fndecl, args, nargs, ignore); - } - if (!ret) - ret = fold_builtin_varargs (loc, fndecl, exp, ignore); + tree *args = CALL_EXPR_ARGP (exp); + ret = fold_builtin_n (loc, fndecl, args, nargs, ignore); if (ret) return ret; } @@ -11253,122 +10681,43 @@ fold_call_expr (location_t loc, tree exp, bool ignore) return NULL_TREE; } -/* Conveniently construct a function call expression. FNDECL names the - function to be called and N arguments are passed in the array - ARGARRAY. */ - -tree -build_call_expr_loc_array (location_t loc, tree fndecl, int n, tree *argarray) -{ - tree fntype = TREE_TYPE (fndecl); - tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl); - - return fold_builtin_call_array (loc, TREE_TYPE (fntype), fn, n, argarray); -} - -/* Conveniently construct a function call expression. FNDECL names the - function to be called and the arguments are passed in the vector - VEC. */ - -tree -build_call_expr_loc_vec (location_t loc, tree fndecl, vec<tree, va_gc> *vec) -{ - return build_call_expr_loc_array (loc, fndecl, vec_safe_length (vec), - vec_safe_address (vec)); -} - - -/* Conveniently construct a function call expression. FNDECL names the - function to be called, N is the number of arguments, and the "..." - parameters are the argument expressions. */ +/* Fold a CALL_EXPR with type TYPE with FN as the function expression. + N arguments are passed in the array ARGARRAY. Return a folded + expression or NULL_TREE if no simplification was possible. */ tree -build_call_expr_loc (location_t loc, tree fndecl, int n, ...) -{ - va_list ap; - tree *argarray = XALLOCAVEC (tree, n); - int i; - - va_start (ap, n); - for (i = 0; i < n; i++) - argarray[i] = va_arg (ap, tree); - va_end (ap); - return build_call_expr_loc_array (loc, fndecl, n, argarray); -} - -/* Like build_call_expr_loc (UNKNOWN_LOCATION, ...). Duplicated because - varargs macros aren't supported by all bootstrap compilers. */ - -tree -build_call_expr (tree fndecl, int n, ...) -{ - va_list ap; - tree *argarray = XALLOCAVEC (tree, n); - int i; - - va_start (ap, n); - for (i = 0; i < n; i++) - argarray[i] = va_arg (ap, tree); - va_end (ap); - return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray); -} - -/* Construct a CALL_EXPR with type TYPE with FN as the function expression. - N arguments are passed in the array ARGARRAY. */ - -tree -fold_builtin_call_array (location_t loc, tree type, +fold_builtin_call_array (location_t loc, tree, tree fn, int n, tree *argarray) { - tree ret = NULL_TREE; - tree exp; - - if (TREE_CODE (fn) == ADDR_EXPR) - { - tree fndecl = TREE_OPERAND (fn, 0); - if (TREE_CODE (fndecl) == FUNCTION_DECL - && DECL_BUILT_IN (fndecl)) - { - /* If last argument is __builtin_va_arg_pack (), arguments to this - function are not finalized yet. Defer folding until they are. */ - if (n && TREE_CODE (argarray[n - 1]) == CALL_EXPR) - { - tree fndecl2 = get_callee_fndecl (argarray[n - 1]); - if (fndecl2 - && TREE_CODE (fndecl2) == FUNCTION_DECL - && DECL_BUILT_IN_CLASS (fndecl2) == BUILT_IN_NORMAL - && DECL_FUNCTION_CODE (fndecl2) == BUILT_IN_VA_ARG_PACK) - return build_call_array_loc (loc, type, fn, n, argarray); - } - if (avoid_folding_inline_builtin (fndecl)) - return build_call_array_loc (loc, type, fn, n, argarray); - if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) - { - ret = targetm.fold_builtin (fndecl, n, argarray, false); - if (ret) - return ret; + if (TREE_CODE (fn) != ADDR_EXPR) + return NULL_TREE; - return build_call_array_loc (loc, type, fn, n, argarray); - } - else if (n <= MAX_ARGS_TO_FOLD_BUILTIN) - { - /* First try the transformations that don't require consing up - an exp. */ - ret = fold_builtin_n (loc, fndecl, argarray, n, false); - if (ret) - return ret; - } - - /* If we got this far, we need to build an exp. */ - exp = build_call_array_loc (loc, type, fn, n, argarray); - ret = fold_builtin_varargs (loc, fndecl, exp, false); - return ret ? ret : exp; - } - } + tree fndecl = TREE_OPERAND (fn, 0); + if (TREE_CODE (fndecl) == FUNCTION_DECL + && DECL_BUILT_IN (fndecl)) + { + /* If last argument is __builtin_va_arg_pack (), arguments to this + function are not finalized yet. Defer folding until they are. */ + if (n && TREE_CODE (argarray[n - 1]) == CALL_EXPR) + { + tree fndecl2 = get_callee_fndecl (argarray[n - 1]); + if (fndecl2 + && TREE_CODE (fndecl2) == FUNCTION_DECL + && DECL_BUILT_IN_CLASS (fndecl2) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl2) == BUILT_IN_VA_ARG_PACK) + return NULL_TREE; + } + if (avoid_folding_inline_builtin (fndecl)) + return NULL_TREE; + if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) + return targetm.fold_builtin (fndecl, n, argarray, false); + else + return fold_builtin_n (loc, fndecl, argarray, n, false); + } - return build_call_array_loc (loc, type, fn, n, argarray); + return NULL_TREE; } /* Construct a new CALL_EXPR using the tail of the argument list of EXP @@ -11415,7 +10764,7 @@ validate_arg (const_tree arg, enum tree_code code) validate_arglist will then be removed. */ bool -validate_gimple_arglist (const_gimple call, ...) +validate_gimple_arglist (const gcall *call, ...) { enum tree_code code; bool res = 0; @@ -11466,7 +10815,7 @@ rtx default_expand_builtin (tree exp ATTRIBUTE_UNUSED, rtx target ATTRIBUTE_UNUSED, rtx subtarget ATTRIBUTE_UNUSED, - enum machine_mode mode ATTRIBUTE_UNUSED, + machine_mode mode ATTRIBUTE_UNUSED, int ignore ATTRIBUTE_UNUSED) { return NULL_RTX; @@ -11475,7 +10824,7 @@ default_expand_builtin (tree exp ATTRIBUTE_UNUSED, /* Returns true is EXP represents data that would potentially reside in a readonly section. */ -static bool +bool readonly_data_expr (tree exp) { STRIP_NOPS (exp); @@ -11748,129 +11097,6 @@ fold_builtin_strpbrk (location_t loc, tree s1, tree s2, tree type) } } -/* Simplify a call to the strcat builtin. DST and SRC are the arguments - to the call. - - Return NULL_TREE 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 -fold_builtin_strcat (location_t loc ATTRIBUTE_UNUSED, tree dst, tree src, - tree len) -{ - if (!validate_arg (dst, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE)) - return NULL_TREE; - else - { - const char *p = c_getstr (src); - - /* If the string length is zero, return the dst parameter. */ - if (p && *p == '\0') - return dst; - - if (optimize_insn_for_speed_p ()) - { - /* See if we can store by pieces into (dst + strlen(dst)). */ - tree newdst, call; - tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN); - tree memcpy_fn = builtin_decl_implicit (BUILT_IN_MEMCPY); - - if (!strlen_fn || !memcpy_fn) - return NULL_TREE; - - /* If the length of the source string isn't computable don't - split strcat into strlen and memcpy. */ - if (! len) - len = c_strlen (src, 1); - if (! len || TREE_SIDE_EFFECTS (len)) - return NULL_TREE; - - /* Stabilize the argument list. */ - dst = builtin_save_expr (dst); - - /* Create strlen (dst). */ - newdst = build_call_expr_loc (loc, strlen_fn, 1, dst); - /* Create (dst p+ strlen (dst)). */ - - newdst = fold_build_pointer_plus_loc (loc, dst, newdst); - newdst = builtin_save_expr (newdst); - - len = fold_convert_loc (loc, size_type_node, len); - len = size_binop_loc (loc, PLUS_EXPR, len, - build_int_cst (size_type_node, 1)); - - call = build_call_expr_loc (loc, memcpy_fn, 3, newdst, src, len); - return build2 (COMPOUND_EXPR, TREE_TYPE (dst), call, dst); - } - return NULL_TREE; - } -} - -/* Simplify a call to the strncat builtin. DST, SRC, and LEN are the - arguments to the call. - - Return NULL_TREE 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 -fold_builtin_strncat (location_t loc, tree dst, tree src, tree len) -{ - if (!validate_arg (dst, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE) - || !validate_arg (len, INTEGER_TYPE)) - return NULL_TREE; - else - { - const char *p = c_getstr (src); - - /* If the requested length is zero, or the src parameter string - length is zero, return the dst parameter. */ - if (integer_zerop (len) || (p && *p == '\0')) - return omit_two_operands_loc (loc, TREE_TYPE (dst), dst, src, len); - - /* If the requested len is greater than or equal to the string - length, call strcat. */ - if (TREE_CODE (len) == INTEGER_CST && p - && compare_tree_int (len, strlen (p)) >= 0) - { - tree fn = builtin_decl_implicit (BUILT_IN_STRCAT); - - /* If the replacement _DECL isn't initialized, don't do the - transformation. */ - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 2, dst, src); - } - return NULL_TREE; - } -} - /* Simplify a call to the strspn builtin. S1 and S2 are the arguments to the call. @@ -11976,84 +11202,6 @@ fold_builtin_strcspn (location_t loc, tree s1, tree s2) } } -/* Fold a call to the fputs builtin. ARG0 and ARG1 are the arguments - to the call. IGNORE is true if the value returned - by the builtin will be ignored. UNLOCKED is true is true if this - actually a call to fputs_unlocked. If LEN in non-NULL, it represents - the known length of the string. Return NULL_TREE if no simplification - was possible. */ - -tree -fold_builtin_fputs (location_t loc, tree arg0, tree arg1, - bool ignore, bool unlocked, tree len) -{ - /* If we're using an unlocked function, assume the other unlocked - functions exist explicitly. */ - tree const fn_fputc = (unlocked - ? builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED) - : builtin_decl_implicit (BUILT_IN_FPUTC)); - tree const fn_fwrite = (unlocked - ? builtin_decl_explicit (BUILT_IN_FWRITE_UNLOCKED) - : builtin_decl_implicit (BUILT_IN_FWRITE)); - - /* If the return value is used, don't do the transformation. */ - if (!ignore) - return NULL_TREE; - - /* Verify the arguments in the original call. */ - if (!validate_arg (arg0, POINTER_TYPE) - || !validate_arg (arg1, POINTER_TYPE)) - return NULL_TREE; - - if (! len) - len = c_strlen (arg0, 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 NULL_TREE; - - switch (compare_tree_int (len, 1)) - { - case -1: /* length is 0, delete the call entirely . */ - return omit_one_operand_loc (loc, integer_type_node, - integer_zero_node, arg1);; - - case 0: /* length is 1, call fputc. */ - { - const char *p = c_getstr (arg0); - - if (p != NULL) - { - if (fn_fputc) - return build_call_expr_loc (loc, fn_fputc, 2, - build_int_cst - (integer_type_node, p[0]), arg1); - else - return NULL_TREE; - } - } - /* FALLTHROUGH */ - case 1: /* length is greater than 1, call fwrite. */ - { - /* If optimizing for size keep fputs. */ - if (optimize_function_for_size_p (cfun)) - return NULL_TREE; - /* New argument list transforming fputs(string, stream) to - fwrite(string, 1, len, stream). */ - if (fn_fwrite) - return build_call_expr_loc (loc, fn_fwrite, 4, arg0, - size_one_node, len, arg1); - else - return NULL_TREE; - } - default: - gcc_unreachable (); - } - return NULL_TREE; -} - /* Fold the next_arg or va_start call EXP. Returns true if there was an error produced. False otherwise. This is done so that we don't output the error or warning twice or three times. */ @@ -12165,217 +11313,9 @@ fold_builtin_next_arg (tree exp, bool va_start_p) } -/* Simplify a call to the sprintf builtin with arguments DEST, FMT, and ORIG. - ORIG may be null if this is a 2-argument call. We don't attempt to - simplify calls with more than 3 arguments. - - Return NULL_TREE 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 -fold_builtin_sprintf (location_t loc, tree dest, tree fmt, - tree orig, int ignored) -{ - tree call, retval; - 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_arg (dest, POINTER_TYPE) - || !validate_arg (fmt, POINTER_TYPE)) - return NULL_TREE; - if (orig && !validate_arg (orig, POINTER_TYPE)) - return NULL_TREE; - - /* 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 (!init_target_chars ()) - return NULL_TREE; - - /* If the format doesn't contain % args or %%, use strcpy. */ - if (strchr (fmt_str, target_percent) == NULL) - { - tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); - - if (!fn) - return NULL_TREE; - - /* Don't optimize sprintf (buf, "abc", ptr++). */ - if (orig) - return NULL_TREE; - - /* Convert sprintf (str, fmt) into strcpy (str, fmt) when - 'format' is known to contain no % formats. */ - call = build_call_expr_loc (loc, fn, 2, dest, fmt); - if (!ignored) - retval = build_int_cst (integer_type_node, strlen (fmt_str)); - } - - /* If the format is "%s", use strcpy if the result isn't used. */ - else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0) - { - tree fn; - fn = builtin_decl_implicit (BUILT_IN_STRCPY); - - if (!fn) - return NULL_TREE; - - /* Don't crash on sprintf (str1, "%s"). */ - if (!orig) - return NULL_TREE; - - /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2). */ - if (!ignored) - { - retval = c_strlen (orig, 1); - if (!retval || TREE_CODE (retval) != INTEGER_CST) - return NULL_TREE; - } - call = build_call_expr_loc (loc, fn, 2, dest, orig); - } - - if (call && retval) - { - retval = fold_convert_loc - (loc, TREE_TYPE (TREE_TYPE (builtin_decl_implicit (BUILT_IN_SPRINTF))), - retval); - return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval); - } - else - return call; -} - -/* Simplify a call to the snprintf builtin with arguments DEST, DESTSIZE, - FMT, and ORIG. ORIG may be null if this is a 3-argument call. We don't - attempt to simplify calls with more than 4 arguments. - - Return NULL_TREE 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 -fold_builtin_snprintf (location_t loc, tree dest, tree destsize, tree fmt, - tree orig, int ignored) -{ - tree call, retval; - const char *fmt_str = NULL; - unsigned HOST_WIDE_INT destlen; - - /* Verify the required arguments in the original call. We deal with two - types of snprintf() calls: 'snprintf (str, cst, fmt)' and - 'snprintf (dest, cst, "%s", orig)'. */ - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (destsize, INTEGER_TYPE) - || !validate_arg (fmt, POINTER_TYPE)) - return NULL_TREE; - if (orig && !validate_arg (orig, POINTER_TYPE)) - return NULL_TREE; - - if (!tree_fits_uhwi_p (destsize)) - return NULL_TREE; - - /* 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 (!init_target_chars ()) - return NULL_TREE; - - destlen = tree_to_uhwi (destsize); - - /* If the format doesn't contain % args or %%, use strcpy. */ - if (strchr (fmt_str, target_percent) == NULL) - { - tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); - size_t len = strlen (fmt_str); - - /* Don't optimize snprintf (buf, 4, "abc", ptr++). */ - if (orig) - return NULL_TREE; - - /* We could expand this as - memcpy (str, fmt, cst - 1); str[cst - 1] = '\0'; - or to - memcpy (str, fmt_with_nul_at_cstm1, cst); - but in the former case that might increase code size - and in the latter case grow .rodata section too much. - So punt for now. */ - if (len >= destlen) - return NULL_TREE; - - if (!fn) - return NULL_TREE; - - /* Convert snprintf (str, cst, fmt) into strcpy (str, fmt) when - 'format' is known to contain no % formats and - strlen (fmt) < cst. */ - call = build_call_expr_loc (loc, fn, 2, dest, fmt); - - if (!ignored) - retval = build_int_cst (integer_type_node, strlen (fmt_str)); - } - - /* If the format is "%s", use strcpy if the result isn't used. */ - else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0) - { - tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); - unsigned HOST_WIDE_INT origlen; - - /* Don't crash on snprintf (str1, cst, "%s"). */ - if (!orig) - return NULL_TREE; - - retval = c_strlen (orig, 1); - if (!retval || !tree_fits_uhwi_p (retval)) - return NULL_TREE; - - origlen = tree_to_uhwi (retval); - /* We could expand this as - memcpy (str1, str2, cst - 1); str1[cst - 1] = '\0'; - or to - memcpy (str1, str2_with_nul_at_cstm1, cst); - but in the former case that might increase code size - and in the latter case grow .rodata section too much. - So punt for now. */ - if (origlen >= destlen) - return NULL_TREE; - - /* Convert snprintf (str1, cst, "%s", str2) into - strcpy (str1, str2) if strlen (str2) < cst. */ - if (!fn) - return NULL_TREE; - - call = build_call_expr_loc (loc, fn, 2, dest, orig); - - if (ignored) - retval = NULL_TREE; - } - - if (call && retval) - { - tree fn = builtin_decl_explicit (BUILT_IN_SNPRINTF); - retval = fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fn)), retval); - return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval); - } - else - return call; -} - /* Expand a call EXP to __builtin_object_size. */ -rtx +static rtx expand_builtin_object_size (tree exp) { tree ost; @@ -12415,7 +11355,7 @@ expand_builtin_object_size (tree exp) mode MODE if that's convenient). */ static rtx -expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, +expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode, enum built_in_function fcode) { tree dest, src, len, size; @@ -12680,7 +11620,7 @@ maybe_emit_free_warning (tree exp) /* Fold a call to __builtin_object_size with arguments PTR and OST, if possible. */ -tree +static tree fold_builtin_object_size (tree ptr, tree ost) { unsigned HOST_WIDE_INT bytes; @@ -12708,8 +11648,7 @@ fold_builtin_object_size (tree ptr, tree ost) if (TREE_CODE (ptr) == ADDR_EXPR) { bytes = compute_builtin_object_size (ptr, object_size_type); - if (double_int_fits_to_tree_p (size_type_node, - double_int::from_uhwi (bytes))) + if (wi::fits_to_tree_p (bytes, size_type_node)) return build_int_cstu (size_type_node, bytes); } else if (TREE_CODE (ptr) == SSA_NAME) @@ -12719,554 +11658,29 @@ fold_builtin_object_size (tree ptr, tree ost) it. */ bytes = compute_builtin_object_size (ptr, object_size_type); if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 ? -1 : 0) - && double_int_fits_to_tree_p (size_type_node, - double_int::from_uhwi (bytes))) + && wi::fits_to_tree_p (bytes, size_type_node)) return build_int_cstu (size_type_node, bytes); } return NULL_TREE; } -/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin. - DEST, SRC, LEN, and SIZE are the arguments to the call. - 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 (location_t loc, tree fndecl, - tree dest, tree src, tree len, tree size, - tree maxlen, bool ignore, - enum built_in_function fcode) -{ - tree fn; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, - (fcode == BUILT_IN_MEMSET_CHK - ? INTEGER_TYPE : POINTER_TYPE)) - || !validate_arg (len, INTEGER_TYPE) - || !validate_arg (size, INTEGER_TYPE)) - return NULL_TREE; - - /* 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_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), - dest, len); - else - { - tree temp = fold_build_pointer_plus_loc (loc, dest, len); - return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), temp); - } - } - - if (! tree_fits_uhwi_p (size)) - return NULL_TREE; - - if (! integer_all_onesp (size)) - { - if (! tree_fits_uhwi_p (len)) - { - /* 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 || ! tree_fits_uhwi_p (maxlen)) - { - if (fcode == BUILT_IN_MEMPCPY_CHK && ignore) - { - /* (void) __mempcpy_chk () can be optimized into - (void) __memcpy_chk (). */ - fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 4, dest, src, len, size); - } - return NULL_TREE; - } - } - else - maxlen = len; - - if (tree_int_cst_lt (size, maxlen)) - return NULL_TREE; - } - - 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 = builtin_decl_explicit (BUILT_IN_MEMCPY); - break; - case BUILT_IN_MEMPCPY_CHK: - fn = builtin_decl_explicit (BUILT_IN_MEMPCPY); - break; - case BUILT_IN_MEMMOVE_CHK: - fn = builtin_decl_explicit (BUILT_IN_MEMMOVE); - break; - case BUILT_IN_MEMSET_CHK: - fn = builtin_decl_explicit (BUILT_IN_MEMSET); - break; - default: - break; - } - - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 3, dest, src, len); -} - -/* Fold a call to the __st[rp]cpy_chk builtin. - DEST, SRC, and SIZE are the arguments to the call. - 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 (location_t loc, tree fndecl, tree dest, - tree src, tree size, - tree maxlen, bool ignore, - enum built_in_function fcode) -{ - tree len, fn; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE) - || !validate_arg (size, INTEGER_TYPE)) - return NULL_TREE; - - /* 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_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest); - - if (! tree_fits_uhwi_p (size)) - return NULL_TREE; - - if (! integer_all_onesp (size)) - { - len = c_strlen (src, 1); - if (! len || ! tree_fits_uhwi_p (len)) - { - /* 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 || ! tree_fits_uhwi_p (maxlen)) - { - if (fcode == BUILT_IN_STPCPY_CHK) - { - if (! ignore) - return NULL_TREE; - - /* If return value of __stpcpy_chk is ignored, - optimize into __strcpy_chk. */ - fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK); - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 3, dest, src, size); - } - - if (! len || TREE_SIDE_EFFECTS (len)) - return NULL_TREE; - - /* If c_strlen returned something, but not a constant, - transform __strcpy_chk into __memcpy_chk. */ - fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); - if (!fn) - return NULL_TREE; - - len = fold_convert_loc (loc, size_type_node, len); - len = size_binop_loc (loc, PLUS_EXPR, len, - build_int_cst (size_type_node, 1)); - return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), - build_call_expr_loc (loc, fn, 4, - dest, src, len, size)); - } - } - else - maxlen = len; - - if (! tree_int_cst_lt (maxlen, size)) - return NULL_TREE; - } - - /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */ - fn = builtin_decl_explicit (fcode == BUILT_IN_STPCPY_CHK - ? BUILT_IN_STPCPY : BUILT_IN_STRCPY); - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 2, dest, src); -} - -/* Fold a call to the __st{r,p}ncpy_chk builtin. DEST, SRC, LEN, and SIZE - are the arguments to the call. If MAXLEN is not NULL, it is maximum - length passed as third argument. IGNORE is true if return value can be - ignored. FCODE is the BUILT_IN_* code of the builtin. */ - -tree -fold_builtin_stxncpy_chk (location_t loc, tree dest, tree src, - tree len, tree size, tree maxlen, bool ignore, - enum built_in_function fcode) -{ - tree fn; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE) - || !validate_arg (len, INTEGER_TYPE) - || !validate_arg (size, INTEGER_TYPE)) - return NULL_TREE; - - if (fcode == BUILT_IN_STPNCPY_CHK && ignore) - { - /* If return value of __stpncpy_chk is ignored, - optimize into __strncpy_chk. */ - fn = builtin_decl_explicit (BUILT_IN_STRNCPY_CHK); - if (fn) - return build_call_expr_loc (loc, fn, 4, dest, src, len, size); - } - - if (! tree_fits_uhwi_p (size)) - return NULL_TREE; - - if (! integer_all_onesp (size)) - { - if (! tree_fits_uhwi_p (len)) - { - /* 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 || ! tree_fits_uhwi_p (maxlen)) - return NULL_TREE; - } - else - maxlen = len; - - if (tree_int_cst_lt (size, maxlen)) - return NULL_TREE; - } - - /* If __builtin_st{r,p}ncpy_chk is used, assume st{r,p}ncpy is available. */ - fn = builtin_decl_explicit (fcode == BUILT_IN_STPNCPY_CHK - ? BUILT_IN_STPNCPY : BUILT_IN_STRNCPY); - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 3, dest, src, len); -} - -/* Fold a call to the __strcat_chk builtin FNDECL. DEST, SRC, and SIZE - are the arguments to the call. */ - -static tree -fold_builtin_strcat_chk (location_t loc, tree fndecl, tree dest, - tree src, tree size) -{ - tree fn; - const char *p; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE) - || !validate_arg (size, INTEGER_TYPE)) - return NULL_TREE; - - p = c_getstr (src); - /* If the SRC parameter is "", return DEST. */ - if (p && *p == '\0') - return omit_one_operand_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest, src); - - if (! tree_fits_uhwi_p (size) || ! integer_all_onesp (size)) - return NULL_TREE; - - /* If __builtin_strcat_chk is used, assume strcat is available. */ - fn = builtin_decl_explicit (BUILT_IN_STRCAT); - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 2, dest, src); -} - -/* Fold a call to the __strncat_chk builtin with arguments DEST, SRC, - LEN, and SIZE. */ - -static tree -fold_builtin_strncat_chk (location_t loc, tree fndecl, - tree dest, tree src, tree len, tree size) -{ - tree fn; - const char *p; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE) - || !validate_arg (size, INTEGER_TYPE) - || !validate_arg (size, INTEGER_TYPE)) - return NULL_TREE; - - p = c_getstr (src); - /* If the SRC parameter is "" or if LEN is 0, return DEST. */ - if (p && *p == '\0') - return omit_one_operand_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest, len); - else if (integer_zerop (len)) - return omit_one_operand_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest, src); - - if (! tree_fits_uhwi_p (size)) - return NULL_TREE; - - if (! integer_all_onesp (size)) - { - tree src_len = c_strlen (src, 1); - if (src_len - && tree_fits_uhwi_p (src_len) - && tree_fits_uhwi_p (len) - && ! tree_int_cst_lt (len, src_len)) - { - /* If LEN >= strlen (SRC), optimize into __strcat_chk. */ - fn = builtin_decl_explicit (BUILT_IN_STRCAT_CHK); - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 3, dest, src, size); - } - return NULL_TREE; - } - - /* If __builtin_strncat_chk is used, assume strncat is available. */ - fn = builtin_decl_explicit (BUILT_IN_STRNCAT); - if (!fn) - return NULL_TREE; - - return build_call_expr_loc (loc, fn, 3, dest, src, len); -} - -/* Fold a call EXP to __{,v}sprintf_chk having NARGS passed as ARGS. - Return NULL_TREE 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_1 (location_t loc, int nargs, tree *args, - 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 (nargs < 4) - return NULL_TREE; - dest = args[0]; - if (!validate_arg (dest, POINTER_TYPE)) - return NULL_TREE; - flag = args[1]; - if (!validate_arg (flag, INTEGER_TYPE)) - return NULL_TREE; - size = args[2]; - if (!validate_arg (size, INTEGER_TYPE)) - return NULL_TREE; - fmt = args[3]; - if (!validate_arg (fmt, POINTER_TYPE)) - return NULL_TREE; - - if (! tree_fits_uhwi_p (size)) - return NULL_TREE; - - len = NULL_TREE; - - if (!init_target_chars ()) - return 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, target_percent) == 0) - { - if (fcode != BUILT_IN_SPRINTF_CHK || nargs == 4) - 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, target_percent_s) == 0) - { - tree arg; - - if (nargs == 5) - { - arg = args[4]; - if (validate_arg (arg, POINTER_TYPE)) - { - len = c_strlen (arg, 1); - if (! len || ! tree_fits_uhwi_p (len)) - len = NULL_TREE; - } - } - } - } - - if (! integer_all_onesp (size)) - { - if (! len || ! tree_int_cst_lt (len, size)) - return NULL_TREE; - } - - /* 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 NULL_TREE; - if (strchr (fmt_str, target_percent) != NULL - && strcmp (fmt_str, target_percent_s)) - return NULL_TREE; - } - - /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */ - fn = builtin_decl_explicit (fcode == BUILT_IN_VSPRINTF_CHK - ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF); - if (!fn) - return NULL_TREE; - - return rewrite_call_expr_array (loc, nargs, args, 4, fn, 2, dest, fmt); -} - -/* Fold a call EXP to __{,v}sprintf_chk. Return NULL_TREE 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 (location_t loc, tree exp, - enum built_in_function fcode) -{ - return fold_builtin_sprintf_chk_1 (loc, call_expr_nargs (exp), - CALL_EXPR_ARGP (exp), fcode); -} - -/* Fold a call EXP to {,v}snprintf having NARGS passed as ARGS. Return - NULL_TREE 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. */ - -static tree -fold_builtin_snprintf_chk_1 (location_t loc, int nargs, tree *args, - 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 (nargs < 5) - return NULL_TREE; - dest = args[0]; - if (!validate_arg (dest, POINTER_TYPE)) - return NULL_TREE; - len = args[1]; - if (!validate_arg (len, INTEGER_TYPE)) - return NULL_TREE; - flag = args[2]; - if (!validate_arg (flag, INTEGER_TYPE)) - return NULL_TREE; - size = args[3]; - if (!validate_arg (size, INTEGER_TYPE)) - return NULL_TREE; - fmt = args[4]; - if (!validate_arg (fmt, POINTER_TYPE)) - return NULL_TREE; - - if (! tree_fits_uhwi_p (size)) - return NULL_TREE; - - if (! integer_all_onesp (size)) - { - if (! tree_fits_uhwi_p (len)) - { - /* 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 || ! tree_fits_uhwi_p (maxlen)) - return NULL_TREE; - } - else - maxlen = len; - - if (tree_int_cst_lt (size, maxlen)) - return NULL_TREE; - } - - if (!init_target_chars ()) - return NULL_TREE; - - /* 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 NULL_TREE; - if (strchr (fmt_str, target_percent) != NULL - && strcmp (fmt_str, target_percent_s)) - return NULL_TREE; - } - - /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is - available. */ - fn = builtin_decl_explicit (fcode == BUILT_IN_VSNPRINTF_CHK - ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF); - if (!fn) - return NULL_TREE; - - return rewrite_call_expr_array (loc, nargs, args, 5, fn, 3, dest, len, fmt); -} - -/* Fold a call EXP to {,v}snprintf. Return NULL_TREE 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. */ - -static tree -fold_builtin_snprintf_chk (location_t loc, tree exp, tree maxlen, - enum built_in_function fcode) -{ - return fold_builtin_snprintf_chk_1 (loc, call_expr_nargs (exp), - CALL_EXPR_ARGP (exp), maxlen, fcode); -} - /* Builtins with folding operations that operate on "..." arguments need special handling; we need to store the arguments in a convenient data structure before attempting any folding. Fortunately there are only a few builtins that fall into this category. FNDECL is the - function, EXP is the CALL_EXPR for the call, and IGNORE is true if the - result of the function call is ignored. */ + function, EXP is the CALL_EXPR for the call. */ static tree -fold_builtin_varargs (location_t loc, tree fndecl, tree exp, - bool ignore ATTRIBUTE_UNUSED) +fold_builtin_varargs (location_t loc, tree fndecl, tree *args, int nargs) { enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); tree ret = NULL_TREE; switch (fcode) { - case BUILT_IN_SPRINTF_CHK: - case BUILT_IN_VSPRINTF_CHK: - ret = fold_builtin_sprintf_chk (loc, exp, fcode); - break; - - case BUILT_IN_SNPRINTF_CHK: - case BUILT_IN_VSNPRINTF_CHK: - ret = fold_builtin_snprintf_chk (loc, exp, NULL_TREE, fcode); - break; - case BUILT_IN_FPCLASSIFY: - ret = fold_builtin_fpclassify (loc, exp); + ret = fold_builtin_fpclassify (loc, args, nargs); break; default: @@ -13282,257 +11696,9 @@ fold_builtin_varargs (location_t loc, tree fndecl, tree exp, return NULL_TREE; } -/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins. - FMT and ARG are the arguments to the call; we don't fold cases with - more than 2 arguments, and ARG may be null if this is a 1-argument case. - - Return NULL_TREE 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 (location_t loc, tree fndecl, tree fmt, - tree arg, bool ignore, - enum built_in_function fcode) -{ - tree fn_putchar, fn_puts, newarg, call = NULL_TREE; - const char *fmt_str = NULL; - - /* If the return value is used, don't do the transformation. */ - if (! ignore) - return NULL_TREE; - - /* Verify the required arguments in the original call. */ - if (!validate_arg (fmt, POINTER_TYPE)) - return NULL_TREE; - - /* 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) - { - /* If we're using an unlocked function, assume the other - unlocked functions exist explicitly. */ - fn_putchar = builtin_decl_explicit (BUILT_IN_PUTCHAR_UNLOCKED); - fn_puts = builtin_decl_explicit (BUILT_IN_PUTS_UNLOCKED); - } - else - { - fn_putchar = builtin_decl_implicit (BUILT_IN_PUTCHAR); - fn_puts = builtin_decl_implicit (BUILT_IN_PUTS); - } - - if (!init_target_chars ()) - return NULL_TREE; - - if (strcmp (fmt_str, target_percent_s) == 0 - || strchr (fmt_str, target_percent) == NULL) - { - const char *str; - - if (strcmp (fmt_str, target_percent_s) == 0) - { - if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) - return NULL_TREE; - - if (!arg || !validate_arg (arg, POINTER_TYPE)) - return NULL_TREE; - - str = c_getstr (arg); - if (str == NULL) - return NULL_TREE; - } - else - { - /* The format specifier doesn't contain any '%' characters. */ - if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK - && arg) - return NULL_TREE; - 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. */ - newarg = build_int_cst (integer_type_node, str[0]); - if (fn_putchar) - call = build_call_expr_loc (loc, fn_putchar, 1, newarg); - } - else - { - /* If the string was "string\n", call puts("string"). */ - size_t len = strlen (str); - if ((unsigned char)str[len - 1] == target_newline - && (size_t) (int) len == len - && (int) len > 0) - { - char *newstr; - tree offset_node, string_cst; - - /* Create a NUL-terminated string that's one char shorter - than the original, stripping off the trailing '\n'. */ - newarg = build_string_literal (len, str); - string_cst = string_constant (newarg, &offset_node); - gcc_checking_assert (string_cst - && (TREE_STRING_LENGTH (string_cst) - == (int) len) - && integer_zerop (offset_node) - && (unsigned char) - TREE_STRING_POINTER (string_cst)[len - 1] - == target_newline); - /* build_string_literal creates a new STRING_CST, - modify it in place to avoid double copying. */ - newstr = CONST_CAST (char *, TREE_STRING_POINTER (string_cst)); - newstr[len - 1] = '\0'; - if (fn_puts) - call = build_call_expr_loc (loc, fn_puts, 1, newarg); - } - 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 NULL_TREE; - } - } - - /* 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 NULL_TREE; - - /* If the format specifier was "%s\n", call __builtin_puts(arg). */ - else if (strcmp (fmt_str, target_percent_s_newline) == 0) - { - if (!arg || !validate_arg (arg, POINTER_TYPE)) - return NULL_TREE; - if (fn_puts) - call = build_call_expr_loc (loc, fn_puts, 1, arg); - } - - /* If the format specifier was "%c", call __builtin_putchar(arg). */ - else if (strcmp (fmt_str, target_percent_c) == 0) - { - if (!arg || !validate_arg (arg, INTEGER_TYPE)) - return NULL_TREE; - if (fn_putchar) - call = build_call_expr_loc (loc, fn_putchar, 1, arg); - } - - if (!call) - return NULL_TREE; - - return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), call); -} - -/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins. - FP, FMT, and ARG are the arguments to the call. We don't fold calls with - more than 3 arguments, and ARG may be null in the 2-argument case. - - Return NULL_TREE 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 (location_t loc, tree fndecl, tree fp, - tree fmt, tree arg, bool ignore, - enum built_in_function fcode) -{ - tree fn_fputc, fn_fputs, call = NULL_TREE; - const char *fmt_str = NULL; - - /* If the return value is used, don't do the transformation. */ - if (! ignore) - return NULL_TREE; - - /* Verify the required arguments in the original call. */ - if (!validate_arg (fp, POINTER_TYPE)) - return NULL_TREE; - if (!validate_arg (fmt, POINTER_TYPE)) - return NULL_TREE; - - /* 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) - { - /* If we're using an unlocked function, assume the other - unlocked functions exist explicitly. */ - fn_fputc = builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED); - fn_fputs = builtin_decl_explicit (BUILT_IN_FPUTS_UNLOCKED); - } - else - { - fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC); - fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS); - } - - if (!init_target_chars ()) - return NULL_TREE; - - /* If the format doesn't contain % args or %%, use strcpy. */ - if (strchr (fmt_str, target_percent) == NULL) - { - if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK - && arg) - return NULL_TREE; - - /* 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 NULL_TREE; - - 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. */ - if (fn_fputs) - call = build_call_expr_loc (loc, fn_fputs, 2, fmt, fp); - } - - /* 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 NULL_TREE; - - /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */ - else if (strcmp (fmt_str, target_percent_s) == 0) - { - if (!arg || !validate_arg (arg, POINTER_TYPE)) - return NULL_TREE; - if (fn_fputs) - call = build_call_expr_loc (loc, fn_fputs, 2, arg, fp); - } - - /* If the format specifier was "%c", call __builtin_fputc (arg, fp). */ - else if (strcmp (fmt_str, target_percent_c) == 0) - { - if (!arg || !validate_arg (arg, INTEGER_TYPE)) - return NULL_TREE; - if (fn_fputc) - call = build_call_expr_loc (loc, fn_fputc, 2, arg, fp); - } - - if (!call) - return NULL_TREE; - return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), call); -} - /* Initialize format string characters in the target charset. */ -static bool +bool init_target_chars (void) { static bool init; @@ -14149,82 +12315,12 @@ do_mpc_arg2 (tree arg0, tree arg1, tree type, int do_nonfinite, return result; } -/* Fold a call STMT to __{,v}sprintf_chk. Return NULL_TREE 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 -gimple_fold_builtin_sprintf_chk (gimple stmt, enum built_in_function fcode) -{ - int nargs = gimple_call_num_args (stmt); - - return fold_builtin_sprintf_chk_1 (gimple_location (stmt), nargs, - (nargs > 0 - ? gimple_call_arg_ptr (stmt, 0) - : &error_mark_node), fcode); -} - -/* Fold a call STMT to {,v}snprintf. Return NULL_TREE 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 -gimple_fold_builtin_snprintf_chk (gimple stmt, tree maxlen, - enum built_in_function fcode) -{ - int nargs = gimple_call_num_args (stmt); - - return fold_builtin_snprintf_chk_1 (gimple_location (stmt), nargs, - (nargs > 0 - ? gimple_call_arg_ptr (stmt, 0) - : &error_mark_node), maxlen, fcode); -} - -/* Builtins with folding operations that operate on "..." arguments - need special handling; we need to store the arguments in a convenient - data structure before attempting any folding. Fortunately there are - only a few builtins that fall into this category. FNDECL is the - function, EXP is the CALL_EXPR for the call, and IGNORE is true if the - result of the function call is ignored. */ - -static tree -gimple_fold_builtin_varargs (tree fndecl, gimple stmt, - bool ignore ATTRIBUTE_UNUSED) -{ - enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); - tree ret = NULL_TREE; - - switch (fcode) - { - case BUILT_IN_SPRINTF_CHK: - case BUILT_IN_VSPRINTF_CHK: - ret = gimple_fold_builtin_sprintf_chk (stmt, fcode); - break; - - case BUILT_IN_SNPRINTF_CHK: - case BUILT_IN_VSNPRINTF_CHK: - ret = gimple_fold_builtin_snprintf_chk (stmt, NULL_TREE, fcode); - - default: - break; - } - if (ret) - { - ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret); - TREE_NO_WARNING (ret) = 1; - return ret; - } - return NULL_TREE; -} - /* 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_call_stmt (gimple stmt, bool ignore) +fold_call_stmt (gcall *stmt, bool ignore) { tree ret = NULL_TREE; tree fndecl = gimple_call_fndecl (stmt); @@ -14247,10 +12343,7 @@ fold_call_stmt (gimple stmt, bool ignore) } else { - if (nargs <= MAX_ARGS_TO_FOLD_BUILTIN) - ret = fold_builtin_n (loc, fndecl, args, nargs, ignore); - if (!ret) - ret = gimple_fold_builtin_varargs (fndecl, stmt, ignore); + ret = fold_builtin_n (loc, fndecl, args, nargs, ignore); if (ret) { /* Propagate location information from original call to @@ -14433,6 +12526,7 @@ is_inexpensive_builtin (tree decl) case BUILT_IN_LABS: case BUILT_IN_LLABS: case BUILT_IN_PREFETCH: + case BUILT_IN_ACC_ON_DEVICE: return true; default: |