/* Expand builtin functions.
- Copyright (C) 1988-2013 Free Software Foundation, Inc.
+ Copyright (C) 1988-2015 Free Software Foundation, Inc.
This file is part of GCC.
#include "system.h"
#include "coretypes.h"
#include "tm.h"
-#include "machmode.h"
#include "rtl.h"
+#include "alias.h"
+#include "symtab.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 "hard-reg-set.h"
+#include "function.h"
+#include "cfgrtl.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-expr.h"
-#include "is-a.h"
#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 "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"
#include "value-prof.h"
#include "diagnostic-core.h"
#include "builtins.h"
-#include "ubsan.h"
+#include "asan.h"
#include "cilk.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));
};
#undef DEF_BUILTIN
-/* Setup an array of _DECL trees, make sure each element is
+/* Setup an array of builtin_info_type, make sure each element decl is
initialized to NULL_TREE. */
-builtin_info_type builtin_info;
+builtin_info_type builtin_info[(int)END_BUILTINS];
/* 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);
#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
static rtx result_vector (int, rtx);
#endif
-static void expand_builtin_update_setjmp_buf (rtx);
static void expand_builtin_prefetch (tree);
static rtx expand_builtin_apply_args (void);
static rtx expand_builtin_apply_args_1 (void);
static rtx expand_builtin_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);
static tree fold_builtin_constant_p (tree);
-static tree fold_builtin_expect (location_t, tree, tree);
static tree fold_builtin_classify_type (tree);
static tree fold_builtin_strlen (location_t, tree, tree);
static tree fold_builtin_inf (location_t, 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);
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);
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_strcat (location_t, 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,
return true;
if (strncmp (name, "__atomic_", 9) == 0)
return true;
- if (flag_enable_cilkplus
+ if (flag_cilkplus
&& (!strcmp (name, "__cilkrts_detach")
|| !strcmp (name, "__cilkrts_pop_frame")))
return true;
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". */
{
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;
tree addr = TREE_OPERAND (exp, 0);
unsigned ptr_align;
unsigned HOST_WIDE_INT ptr_bitpos;
+ unsigned HOST_WIDE_INT ptr_bitmask = ~0;
+ /* If the address is explicitely aligned, handle that. */
if (TREE_CODE (addr) == BIT_AND_EXPR
&& TREE_CODE (TREE_OPERAND (addr, 1)) == INTEGER_CST)
{
- align = (TREE_INT_CST_LOW (TREE_OPERAND (addr, 1))
- & -TREE_INT_CST_LOW (TREE_OPERAND (addr, 1)));
- align *= BITS_PER_UNIT;
+ ptr_bitmask = TREE_INT_CST_LOW (TREE_OPERAND (addr, 1));
+ ptr_bitmask *= BITS_PER_UNIT;
+ align = ptr_bitmask & -ptr_bitmask;
addr = TREE_OPERAND (addr, 0);
}
= get_pointer_alignment_1 (addr, &ptr_align, &ptr_bitpos);
align = MAX (ptr_align, align);
+ /* Re-apply explicit alignment to the bitpos. */
+ ptr_bitpos &= ptr_bitmask;
+
/* The alignment of the pointer operand in a TARGET_MEM_REF
has to take the variable offset parts into account. */
if (TREE_CODE (exp) == TARGET_MEM_REF)
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)
alignment that can prevail. */
if (offset)
{
- int trailing_zeros = tree_ctz (offset);
+ unsigned int trailing_zeros = tree_ctz (offset);
if (trailing_zeros < HOST_BITS_PER_INT)
{
unsigned int inner = (1U << trailing_zeros) * BITS_PER_UNIT;
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
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;
/* 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;
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++)
{
&& 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
|| 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;
register. There is no way to access it off of the current frame
pointer, but it can be accessed off the previous frame pointer by
reading the value from the register window save area. */
-#ifdef RETURN_ADDR_IN_PREVIOUS_FRAME
- if (fndecl_code == BUILT_IN_RETURN_ADDRESS)
+ if (RETURN_ADDR_IN_PREVIOUS_FRAME && fndecl_code == BUILT_IN_RETURN_ADDRESS)
count--;
-#endif
/* Scan back COUNT frames to the specified frame. */
for (i = 0; i < count; i++)
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;
#ifdef HAVE_nonlocal_goto
if (! HAVE_nonlocal_goto)
#endif
- /* First adjust our frame pointer to its actual value. It was
- previously set to the start of the virtual area corresponding to
- the stacked variables when we branched here and now needs to be
- adjusted to the actual hardware fp value.
-
- Assignments to virtual registers are converted by
- instantiate_virtual_regs into the corresponding assignment
- to the underlying register (fp in this case) that makes
- the original assignment true.
- So the following insn will actually be decrementing fp by
- STARTING_FRAME_OFFSET. */
- emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
+ {
+ /* First adjust our frame pointer to its actual value. It was
+ previously set to the start of the virtual area corresponding to
+ the stacked variables when we branched here and now needs to be
+ adjusted to the actual hardware fp value.
+
+ Assignments to virtual registers are converted by
+ instantiate_virtual_regs into the corresponding assignment
+ to the underlying register (fp in this case) that makes
+ the original assignment true.
+ So the following insn will actually be decrementing fp by
+ STARTING_FRAME_OFFSET. */
+ emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
+
+ /* Restoring the frame pointer also modifies the hard frame pointer.
+ Mark it used (so that the previous assignment remains live once
+ the frame pointer is eliminated) and clobbered (to represent the
+ implicit update from the assignment). */
+ emit_use (hard_frame_pointer_rtx);
+ emit_clobber (hard_frame_pointer_rtx);
+ }
#if !HARD_FRAME_POINTER_IS_ARG_POINTER
if (fixed_regs[ARG_POINTER_REGNUM])
/* We must not allow the code we just generated to be reordered by
scheduling. Specifically, the update of the frame pointer must
- happen immediately, not later. Similarly, we must block
- (frame-related) register values to be used across this code. */
+ happen immediately, not later. */
emit_insn (gen_blockage ());
}
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 */
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;
/* __builtin_update_setjmp_buf is passed a pointer to an array of five words
(not all will be used on all machines) that was passed to __builtin_setjmp.
- It updates the stack pointer in that block to correspond to the current
- stack pointer. */
+ It updates the stack pointer in that block to the current value. This is
+ also called directly by the SJLJ exception handling code. */
-static void
+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
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)
{
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)
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);
reg = gen_rtx_REG (mode, savep ? regno : INCOMING_REGNO (regno));
mem = adjust_address (result, mode, size);
savevec[nelts++] = (savep
- ? gen_rtx_SET (VOIDmode, mem, reg)
- : gen_rtx_SET (VOIDmode, reg, mem));
+ ? gen_rtx_SET (mem, reg)
+ : gen_rtx_SET (reg, mem));
size += GET_MODE_SIZE (mode);
}
return gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (nelts, savevec));
{
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,
/* Save the arg pointer to the block. */
tem = copy_to_reg (crtl->args.internal_arg_pointer);
-#ifdef STACK_GROWS_DOWNWARD
/* We need the pointer as the caller actually passed them to us, not
as we might have pretended they were passed. Make sure it's a valid
operand, as emit_move_insn isn't expected to handle a PLUS. */
- tem
- = force_operand (plus_constant (Pmode, tem, crtl->args.pretend_args_size),
- NULL_RTX);
-#endif
+ if (STACK_GROWS_DOWNWARD)
+ tem
+ = force_operand (plus_constant (Pmode, tem,
+ crtl->args.pretend_args_size),
+ NULL_RTX);
emit_move_insn (adjust_address (registers, Pmode, 0), tem);
size = GET_MODE_SIZE (Pmode);
saved on entry to this function. So we migrate the
call to the first insn of this function. */
rtx temp;
- rtx seq;
start_sequence ();
temp = expand_builtin_apply_args_1 ();
- seq = get_insns ();
+ rtx_insn *seq = get_insns ();
end_sequence ();
apply_args_value = temp;
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);
/* Fetch the arg pointer from the ARGUMENTS block. */
incoming_args = gen_reg_rtx (Pmode);
emit_move_insn (incoming_args, gen_rtx_MEM (Pmode, arguments));
-#ifndef STACK_GROWS_DOWNWARD
- incoming_args = expand_simple_binop (Pmode, MINUS, incoming_args, argsize,
- incoming_args, 0, OPTAB_LIB_WIDEN);
-#endif
+ if (!STACK_GROWS_DOWNWARD)
+ incoming_args = expand_simple_binop (Pmode, MINUS, incoming_args, argsize,
+ incoming_args, 0, OPTAB_LIB_WIDEN);
/* Push a new argument block and copy the arguments. Do not allow
the (potential) memcpy call below to interfere with our stack
crtl->need_drap = true;
dest = virtual_outgoing_args_rtx;
-#ifndef STACK_GROWS_DOWNWARD
- if (CONST_INT_P (argsize))
- dest = plus_constant (Pmode, dest, -INTVAL (argsize));
- else
- dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize));
-#endif
+ if (!STACK_GROWS_DOWNWARD)
+ {
+ if (CONST_INT_P (argsize))
+ dest = plus_constant (Pmode, dest, -INTVAL (argsize));
+ else
+ dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize));
+ }
dest = gen_rtx_MEM (BLKmode, dest);
set_mem_align (dest, PARM_BOUNDARY);
src = gen_rtx_MEM (BLKmode, incoming_args);
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);
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. */
do_compare_rtx_and_jump (target, target, EQ, 0, GET_MODE (target),
- NULL_RTX, NULL_RTX, lab,
+ NULL_RTX, NULL, lab,
/* The jump is very likely. */
REG_BR_PROB_BASE - (REG_BR_PROB_BASE / 2000 - 1));
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;
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))
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;
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))
{
bool errno_set = false;
optab builtin_optab = unknown_optab;
- enum machine_mode mode;
+ machine_mode mode;
switch (DECL_FUNCTION_CODE (fndecl))
{
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))
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
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);
{
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);
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))
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))
{
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;
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;
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;
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;
{
if (CONST_INT_P (len_rtx))
{
- *min_size = *max_size = UINTVAL (len_rtx);
+ *min_size = *max_size = *probable_max_size = UINTVAL (len_rtx);
return;
}
else
{
- double_int min, max;
+ wide_int min, max;
enum value_range_type range_type = VR_UNDEFINED;
/* Determine bounds from the type. */
else
*min_size = 0;
if (tree_fits_uhwi_p (TYPE_MAX_VALUE (TREE_TYPE (len))))
- *probable_max_size = *max_size = tree_to_uhwi (TYPE_MAX_VALUE (TREE_TYPE (len)));
+ *probable_max_size = *max_size
+ = tree_to_uhwi (TYPE_MAX_VALUE (TREE_TYPE (len)));
else
*probable_max_size = *max_size = GET_MODE_MASK (GET_MODE (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
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 <=
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
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;
}
}
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))
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;
}
}
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,
create_output_operand (&ops[0], endp ? target : NULL_RTX, Pmode);
create_fixed_operand (&ops[1], dest_mem);
create_fixed_operand (&ops[2], src_mem);
- expand_insn (CODE_FOR_movstr, 3, ops);
+ if (!maybe_expand_insn (CODE_FOR_movstr, 3, ops))
+ return NULL_RTX;
if (endp && target != const0_rtx)
{
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);
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;
rtx
builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
- enum machine_mode mode)
+ machine_mode mode)
{
const char *str = (const char *) data;
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));
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;
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))
}
}
+/* 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.
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;
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)
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);
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;
/* 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. */
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);
if (insn)
{
- enum machine_mode mode;
+ machine_mode mode;
emit_insn (insn);
/* Return the value in the proper mode for this function. */
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);
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);
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. */
{
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. */
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;
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;
#ifdef HAVE_trap
if (HAVE_trap)
{
- rtx insn = emit_insn (gen_trap ());
+ rtx_insn *insn = emit_insn (gen_trap ());
/* For trap insns when not accumulating outgoing args force
REG_ARGS_SIZE note to prevent crossjumping of calls with
different args sizes. */
static rtx
expand_builtin_fabs (tree exp, rtx target, rtx subtarget)
{
- enum machine_mode mode;
+ machine_mode mode;
tree arg;
rtx op0;
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
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;
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;
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
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
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;
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
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)
{
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
- return expand_atomic_fetch_op (target, mem, val, code, MEMMODEL_SEQ_CST,
+ return expand_atomic_fetch_op (target, mem, val, code, MEMMODEL_SYNC_SEQ_CST,
after);
}
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;
poval = ⌖
}
if (!expand_atomic_compare_and_swap (pbool, poval, mem, old_val, new_val,
- false, MEMMODEL_SEQ_CST,
- MEMMODEL_SEQ_CST))
+ false, MEMMODEL_SYNC_SEQ_CST,
+ MEMMODEL_SYNC_SEQ_CST))
return NULL_RTX;
return target;
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;
/* 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;
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
- expand_atomic_store (mem, const0_rtx, MEMMODEL_RELEASE, true);
+ expand_atomic_store (mem, const0_rtx, MEMMODEL_SYNC_RELEASE, true);
}
/* Given an integer representing an ``enum memmodel'', verify its
return MEMMODEL_SEQ_CST;
}
- if ((INTVAL (op) & MEMMODEL_MASK) >= MEMMODEL_LAST)
+ /* Should never see a user explicit SYNC memodel model, so >= LAST works. */
+ if (memmodel_base (val) >= MEMMODEL_LAST)
{
warning (OPT_Winvalid_memory_model,
"invalid memory model argument to builtin");
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;
}
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;
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;
+ rtx_code_label *label;
enum memmodel success, failure;
tree weak;
bool is_weak;
success = get_memmodel (CALL_EXPR_ARG (exp, 4));
failure = get_memmodel (CALL_EXPR_ARG (exp, 5));
- if ((failure & MEMMODEL_MASK) == MEMMODEL_RELEASE
- || (failure & MEMMODEL_MASK) == MEMMODEL_ACQ_REL)
+ if (failure > success)
{
- error ("invalid failure memory model for %<__atomic_compare_exchange%>");
- return NULL_RTX;
+ 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 > success)
+
+ if (is_mm_release (failure) || is_mm_acq_rel (failure))
{
- error ("failure memory model cannot be stronger than success "
- "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 (!flag_inline_atomics)
return NULL_RTX;
if (tree_fits_shwi_p (weak) && tree_to_shwi (weak) != 0)
is_weak = true;
- oldval = expect;
- if (!expand_atomic_compare_and_swap ((target == const0_rtx ? NULL : &target),
- &oldval, mem, oldval, desired,
+ if (target == const0_rtx)
+ target = NULL;
+
+ /* Lest the rtl backend create a race condition with an imporoper store
+ to memory, always create a new pseudo for OLDVAL. */
+ oldval = NULL;
+
+ if (!expand_atomic_compare_and_swap (&target, &oldval, mem, expect, desired,
is_weak, success, failure))
return NULL_RTX;
- if (oldval != expect)
- emit_move_insn (expect, oldval);
+ /* Conditionally store back to EXPECT, lest we create a race condition
+ with an improper store to memory. */
+ /* ??? With a rearrangement of atomics at the gimple level, we can handle
+ the normal case where EXPECT is totally private, i.e. a register. At
+ which point the store can be unconditional. */
+ label = gen_label_rtx ();
+ emit_cmp_and_jump_insns (target, const0_rtx, NE, NULL,
+ GET_MODE (target), 1, label);
+ emit_move_insn (expect, oldval);
+ emit_label (label);
return target;
}
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;
model = get_memmodel (CALL_EXPR_ARG (exp, 1));
- if ((model & MEMMODEL_MASK) == MEMMODEL_RELEASE
- || (model & MEMMODEL_MASK) == MEMMODEL_ACQ_REL)
+ if (is_mm_release (model) || is_mm_acq_rel (model))
{
- 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)
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;
model = get_memmodel (CALL_EXPR_ARG (exp, 2));
- if ((model & MEMMODEL_MASK) != MEMMODEL_RELAXED
- && (model & MEMMODEL_MASK) != MEMMODEL_SEQ_CST
- && (model & MEMMODEL_MASK) != MEMMODEL_RELEASE)
+ if (!(is_mm_relaxed (model) || is_mm_seq_cst (model)
+ || is_mm_release (model)))
{
- 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)
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)
{
static rtx
expand_builtin_atomic_clear (tree exp)
{
- enum machine_mode mode;
+ machine_mode mode;
rtx mem, ret;
enum memmodel model;
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
- || (model & MEMMODEL_MASK) == MEMMODEL_ACQ_REL)
+ if (is_mm_consume (model) || is_mm_acquire (model) || is_mm_acq_rel (model))
{
- 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)
{
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);
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)
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);
static void
expand_builtin_sync_synchronize (void)
{
- expand_mem_thread_fence (MEMMODEL_SEQ_CST);
+ expand_mem_thread_fence (MEMMODEL_SYNC_SEQ_CST);
}
static rtx
if (icode != CODE_FOR_nothing)
{
struct expand_operand op;
- if (!REG_P (target) || GET_MODE (target) != Pmode)
+ /* If the target is not sutitable then create a new target. */
+ if (target == NULL_RTX
+ || !REG_P (target)
+ || GET_MODE (target) != Pmode)
target = gen_reg_rtx (Pmode);
create_output_operand (&op, target, Pmode);
expand_insn (icode, 1, &op);
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);
prev = get_last_insn ();
emit_stack_restore (SAVE_BLOCK, sa);
+
+ record_new_stack_level ();
+
fixup_args_size_notes (prev, get_last_insn (), 0);
}
-
/* Emit code to save the current value of stack. */
static rtx
{
rtx ret = NULL_RTX;
- do_pending_stack_adjust ();
emit_stack_save (SAVE_BLOCK, &ret);
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 ATTRIBUTE_UNUSED,
+ rtx target ATTRIBUTE_UNUSED)
+{
+#ifdef ACCEL_COMPILER
+ 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;
+ v1 = GEN_INT (GOMP_DEVICE_NOT_HOST);
+ v2 = GEN_INT (ACCEL_COMPILER_acc_device);
+ 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, done_label, PROB_EVEN);
+ do_compare_rtx_and_jump (v, v2, EQ, false, v_mode, NULL_RTX,
+ NULL, done_label, PROB_EVEN);
+ emit_move_insn (target, const0_rtx);
+ emit_label (done_label);
+
+ return target;
+#else
+ return NULL;
+#endif
+}
+
+
/* 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).
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;
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
+ /* 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);
+
/* When not optimizing, generate calls to library functions for a certain
set of builtins. */
if (!optimize
&& 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_GET_PTR_UBOUND
+ && fcode != BUILT_IN_CHKP_BNDRET)
return expand_call (exp, target, ignore);
/* The built-in function expanders test for target == const0_rtx
}
}
+ /* 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):
rtx buf_addr = expand_expr (CALL_EXPR_ARG (exp, 0), subtarget,
VOIDmode, EXPAND_NORMAL);
tree label = TREE_OPERAND (CALL_EXPR_ARG (exp, 1), 0);
- rtx label_r = label_rtx (label);
+ rtx_insn *label_r = label_rtx (label);
/* 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
}
break;
- case BUILT_IN_SETJMP_DISPATCHER:
- /* __builtin_setjmp_dispatcher is passed the dispatcher label. */
- if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
- {
- tree label = TREE_OPERAND (CALL_EXPR_ARG (exp, 0), 0);
- rtx label_r = label_rtx (label);
-
- /* Remove the dispatcher label from the list of non-local labels
- since the receiver labels have been added to it above. */
- remove_node_from_expr_list (label_r, &nonlocal_goto_handler_labels);
- return const0_rtx;
- }
- break;
-
case BUILT_IN_SETJMP_RECEIVER:
/* __builtin_setjmp_receiver is passed the receiver label. */
if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
{
tree label = TREE_OPERAND (CALL_EXPR_ARG (exp, 0), 0);
- rtx label_r = label_rtx (label);
+ rtx_insn *label_r = label_rtx (label);
expand_builtin_setjmp_receiver (label_r);
return const0_rtx;
expand_builtin_eh_return (CALL_EXPR_ARG (exp, 0),
CALL_EXPR_ARG (exp, 1));
return const0_rtx;
-#ifdef EH_RETURN_DATA_REGNO
case BUILT_IN_EH_RETURN_DATA_REGNO:
return expand_builtin_eh_return_data_regno (exp);
-#endif
case BUILT_IN_EXTEND_POINTER:
return expand_builtin_extend_pointer (CALL_EXPR_ARG (exp, 0));
case BUILT_IN_EH_POINTER:
case BUILT_IN_CHKP_INIT_PTR_BOUNDS:
case BUILT_IN_CHKP_NULL_PTR_BOUNDS:
case BUILT_IN_CHKP_COPY_PTR_BOUNDS:
- return expand_normal (CALL_EXPR_ARG (exp, 0));
-
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_GET_PTR_UBOUND:
/* We allow user CHKP builtins if Pointer Bounds
Checker is off. */
- if (!flag_check_pointer_bounds)
+ 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_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);
case BUILT_IN_CHKP_BNDLDX:
case BUILT_IN_CHKP_BNDRET:
case BUILT_IN_CHKP_INTERSECT:
- case BUILT_IN_CHKP_ARG_BND:
case BUILT_IN_CHKP_NARROW:
case BUILT_IN_CHKP_EXTRACT_LOWER:
case BUILT_IN_CHKP_EXTRACT_UPPER:
- /* Software implementation of pointers checker is NYI.
+ /* Software implementation of Pointer Bounds Checker is NYI.
Target support is required. */
- error ("Your target platform does not support -fcheck-pointers");
+ 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 */
return expand_call (exp, target, ignore);
}
-/* Determine whether a tree node represents a call to a built-in
- function. If the tree T is a call to a built-in function with
- the right number of arguments of the appropriate types, return
- the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT.
- Otherwise the return value is END_BUILTINS. */
+/* Similar to expand_builtin but is used for instrumented calls. */
-enum built_in_function
-builtin_mathfn_code (const_tree t)
+rtx
+expand_builtin_with_bounds (tree exp, rtx target,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ machine_mode mode, int ignore)
{
- const_tree fndecl, arg, parmlist;
- const_tree argtype, parmtype;
- const_call_expr_arg_iterator iter;
+ 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
+ the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT.
+ Otherwise the return value is END_BUILTINS. */
+
+enum built_in_function
+builtin_mathfn_code (const_tree t)
+{
+ const_tree fndecl, arg, parmlist;
+ const_tree argtype, parmtype;
+ const_call_expr_arg_iterator iter;
if (TREE_CODE (t) != CALL_EXPR
|| TREE_CODE (CALL_EXPR_FN (t)) != ADDR_EXPR)
return it as a truthvalue. */
static tree
-build_builtin_expect_predicate (location_t loc, tree pred, tree expected)
+build_builtin_expect_predicate (location_t loc, tree pred, tree expected,
+ tree predictor)
{
tree fn, arg_types, pred_type, expected_type, call_expr, ret_type;
pred = fold_convert_loc (loc, pred_type, pred);
expected = fold_convert_loc (loc, expected_type, expected);
- call_expr = build_call_expr_loc (loc, fn, 2, pred, expected);
+ call_expr = build_call_expr_loc (loc, fn, predictor ? 3 : 2, pred, expected,
+ predictor);
return build2 (NE_EXPR, TREE_TYPE (pred), call_expr,
build_int_cst (ret_type, 0));
/* Fold a call to builtin_expect with arguments ARG0 and ARG1. Return
NULL_TREE if no simplification is possible. */
-static tree
-fold_builtin_expect (location_t loc, tree arg0, tree arg1)
+tree
+fold_builtin_expect (location_t loc, tree arg0, tree arg1, tree arg2)
{
tree inner, fndecl, inner_arg0;
enum tree_code code;
/* 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);
tree op0 = TREE_OPERAND (inner, 0);
tree op1 = TREE_OPERAND (inner, 1);
- op0 = build_builtin_expect_predicate (loc, op0, arg1);
- op1 = build_builtin_expect_predicate (loc, op1, arg1);
+ op0 = build_builtin_expect_predicate (loc, op0, arg1, arg2);
+ op1 = build_builtin_expect_predicate (loc, op1, arg1, arg2);
inner = build2 (code, TREE_TYPE (inner), op0, op1);
return fold_convert_loc (loc, TREE_TYPE (arg0), inner);
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)
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. */
{
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))
{
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);
}
}
/* 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:
/* 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;
/* 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
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. */
+/* 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. */
static tree
-fold_builtin_memset (location_t loc, tree dest, tree c, tree len,
- tree type, bool ignore)
+fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
{
- 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))
+ if (!validate_arg (arg1, POINTER_TYPE)
+ || !validate_arg (arg2, INTEGER_TYPE)
+ || !validate_arg (len, INTEGER_TYPE))
return NULL_TREE;
+ else
+ {
+ const char *p1;
- /* If the LEN parameter is zero, return DEST. */
- if (integer_zerop (len))
- return omit_one_operand_loc (loc, type, dest, c);
+ if (TREE_CODE (arg2) != INTEGER_CST
+ || !tree_fits_uhwi_p (len))
+ return NULL_TREE;
- if (TREE_CODE (c) != INTEGER_CST || TREE_SIDE_EFFECTS (dest))
- return NULL_TREE;
+ p1 = c_getstr (arg1);
+ if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
+ {
+ char c;
+ const char *r;
+ tree tem;
- var = dest;
- STRIP_NOPS (var);
- if (TREE_CODE (var) != ADDR_EXPR)
- return NULL_TREE;
+ if (target_char_cast (arg2, &c))
+ return NULL_TREE;
- var = TREE_OPERAND (var, 0);
- if (TREE_THIS_VOLATILE (var))
- return NULL_TREE;
+ r = (const char *) memchr (p1, c, tree_to_uhwi (len));
- etype = TREE_TYPE (var);
- if (TREE_CODE (etype) == ARRAY_TYPE)
- etype = TREE_TYPE (etype);
+ if (r == NULL)
+ return build_int_cst (TREE_TYPE (arg1), 0);
- if (!INTEGRAL_TYPE_P (etype)
- && !POINTER_TYPE_P (etype))
- return NULL_TREE;
+ tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
+ return fold_convert_loc (loc, type, tem);
+ }
+ return NULL_TREE;
+ }
+}
- if (! var_decl_component_p (var))
- return NULL_TREE;
+/* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
+ Return NULL_TREE if no simplification can be made. */
- length = tree_to_uhwi (len);
- if (GET_MODE_SIZE (TYPE_MODE (etype)) != length
- || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
- return NULL_TREE;
+static tree
+fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
+{
+ const char *p1, *p2;
- if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+ if (!validate_arg (arg1, POINTER_TYPE)
+ || !validate_arg (arg2, POINTER_TYPE)
+ || !validate_arg (len, INTEGER_TYPE))
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;
- }
+ /* If the LEN parameter is zero, return zero. */
+ if (integer_zerop (len))
+ return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
+ arg1, arg2);
- 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;
+ /* If ARG1 and ARG2 are the same (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
- return omit_one_operand_loc (loc, type, dest, ret);
-}
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
-/* Fold function call to builtin memset. Return
- NULL_TREE if no simplification can be made. */
+ /* If all arguments are constant, and the value of len is not greater
+ than the lengths of arg1 and arg2, evaluate at compile-time. */
+ if (tree_fits_uhwi_p (len) && p1 && p2
+ && compare_tree_int (len, strlen (p1) + 1) <= 0
+ && compare_tree_int (len, strlen (p2) + 1) <= 0)
+ {
+ const int r = memcmp (p1, p2, tree_to_uhwi (len));
-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 (r > 0)
+ return integer_one_node;
+ else if (r < 0)
+ return integer_minus_one_node;
+ else
+ return integer_zero_node;
+ }
- if (!ignore)
- return NULL_TREE;
+ /* If len parameter is one, return an expression corresponding to
+ (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
+ if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
- /* 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. */
+ tree ind1
+ = fold_convert_loc (loc, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ fold_convert_loc (loc,
+ cst_uchar_ptr_node,
+ arg1)));
+ tree ind2
+ = fold_convert_loc (loc, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ fold_convert_loc (loc,
+ cst_uchar_ptr_node,
+ arg2)));
+ return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
+ }
- return fold_builtin_memset (loc, dest, integer_zero_node,
- fold_convert_loc (loc, size_type_node, size),
- void_type_node, ignore);
+ return NULL_TREE;
}
-/* 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). */
+/* Fold function call to builtin strcmp with arguments ARG1 and ARG2.
+ Return NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_memory_op (location_t loc, tree dest, tree src,
- tree len, tree type, bool ignore, int endp)
+fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
{
- tree destvar, srcvar, expr;
+ const char *p1, *p2;
- if (! validate_arg (dest, POINTER_TYPE)
- || ! validate_arg (src, POINTER_TYPE)
- || ! validate_arg (len, INTEGER_TYPE))
+ if (!validate_arg (arg1, POINTER_TYPE)
+ || !validate_arg (arg2, POINTER_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 ARG1 and ARG2 are the same (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return integer_zero_node;
- /* 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
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
+
+ if (p1 && p2)
{
- tree srctype, desttype;
- unsigned int src_align, dest_align;
- tree off0;
+ const int i = strcmp (p1, p2);
+ if (i < 0)
+ return integer_minus_one_node;
+ else if (i > 0)
+ return integer_one_node;
+ else
+ return integer_zero_node;
+ }
- if (endp == 3)
- {
- src_align = get_pointer_alignment (src);
- dest_align = get_pointer_alignment (dest);
+ /* If the second arg is "", return *(const unsigned char*)arg1. */
+ if (p2 && *p2 == '\0')
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
- /* Both DEST and SRC must be pointer types.
- ??? This is what old code did. Is the testing for pointer types
- really mandatory?
+ return fold_convert_loc (loc, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ fold_convert_loc (loc,
+ cst_uchar_ptr_node,
+ arg1)));
+ }
- 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;
+ /* If the first arg is "", return -*(const unsigned char*)arg2. */
+ if (p1 && *p1 == '\0')
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
- fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
- if (!fn)
- return NULL_TREE;
- return build_call_expr_loc (loc, fn, 3, dest, src, len);
- }
+ tree temp
+ = fold_convert_loc (loc, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ fold_convert_loc (loc,
+ cst_uchar_ptr_node,
+ arg2)));
+ return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
+ }
- /* 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;
+}
- return NULL_TREE;
- }
+/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
+ Return NULL_TREE if no simplification can be made. */
- 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;
- /* 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;
+static tree
+fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
+{
+ const char *p1, *p2;
- 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 (!validate_arg (arg1, POINTER_TYPE)
+ || !validate_arg (arg2, POINTER_TYPE)
+ || !validate_arg (len, INTEGER_TYPE))
+ return NULL_TREE;
- if (!ignore)
- dest = builtin_save_expr (dest);
+ /* If the LEN parameter is zero, return zero. */
+ if (integer_zerop (len))
+ return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
+ arg1, arg2);
- /* 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);
+ /* If ARG1 and ARG2 are the same (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
- 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;
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
- 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;
- }
+ if (tree_fits_uhwi_p (len) && p1 && p2)
+ {
+ const int i = strncmp (p1, p2, tree_to_uhwi (len));
+ if (i > 0)
+ return integer_one_node;
+ else if (i < 0)
+ return integer_minus_one_node;
else
- srcvar = NULL_TREE;
-
- if (srcvar == NULL_TREE && destvar == NULL_TREE)
- return NULL_TREE;
+ return integer_zero_node;
+ }
- 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);
- }
- }
+ /* If the second arg is "", and the length is greater than zero,
+ return *(const unsigned char*)arg1. */
+ if (p2 && *p2 == '\0'
+ && TREE_CODE (len) == INTEGER_CST
+ && tree_int_cst_sgn (len) == 1)
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
- expr = build2 (MODIFY_EXPR, TREE_TYPE (destvar), destvar, srcvar);
+ return fold_convert_loc (loc, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ fold_convert_loc (loc,
+ cst_uchar_ptr_node,
+ arg1)));
}
- if (ignore)
- return expr;
+ /* If the first arg is "", and the length is greater than zero,
+ return -*(const unsigned char*)arg2. */
+ if (p1 && *p1 == '\0'
+ && TREE_CODE (len) == INTEGER_CST
+ && tree_int_cst_sgn (len) == 1)
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
- if (endp == 0 || endp == 3)
- return omit_one_operand_loc (loc, type, dest, expr);
+ tree temp = fold_convert_loc (loc, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ fold_convert_loc (loc,
+ cst_uchar_ptr_node,
+ arg2)));
+ return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
+ }
- if (expr == len)
- expr = NULL_TREE;
+ /* If len parameter is one, return an expression corresponding to
+ (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
+ if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
+ {
+ tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
- if (endp == 2)
- len = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (len), len,
- ssize_int (1));
+ tree ind1 = fold_convert_loc (loc, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ fold_convert_loc (loc,
+ cst_uchar_ptr_node,
+ arg1)));
+ tree ind2 = fold_convert_loc (loc, integer_type_node,
+ build1 (INDIRECT_REF, cst_uchar_node,
+ fold_convert_loc (loc,
+ cst_uchar_ptr_node,
+ arg2)));
+ return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
+ }
- 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;
+ return NULL_TREE;
}
-/* 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. */
+/* Fold function call to builtin signbit, signbitf or signbitl with argument
+ ARG. Return NULL_TREE if no simplification can be made. */
-tree
-fold_builtin_strcpy (location_t loc, tree fndecl, tree dest, tree src, tree len)
+static tree
+fold_builtin_signbit (location_t loc, tree arg, tree type)
{
- tree fn;
-
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE))
+ if (!validate_arg (arg, REAL_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 ARG is a compile-time constant, determine the result. */
+ if (TREE_CODE (arg) == REAL_CST
+ && !TREE_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE c;
- if (optimize_function_for_size_p (cfun))
- return NULL_TREE;
+ c = TREE_REAL_CST (arg);
+ return (REAL_VALUE_NEGATIVE (c)
+ ? build_one_cst (type)
+ : build_zero_cst (type));
+ }
- fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
- if (!fn)
- return NULL_TREE;
+ /* If ARG is non-negative, the result is always zero. */
+ if (tree_expr_nonnegative_p (arg))
+ return omit_one_operand_loc (loc, type, integer_zero_node, arg);
- if (!len)
- {
- len = c_strlen (src, 1);
- if (! len || TREE_SIDE_EFFECTS (len))
- return NULL_TREE;
- }
+ /* If ARG's format doesn't have signed zeros, return "arg < 0.0". */
+ 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)));
- 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));
+ return NULL_TREE;
}
-/* Fold function call to builtin stpcpy with arguments DEST and SRC.
- Return NULL_TREE if no simplification can be made. */
+/* Fold function call to builtin copysign, copysignf or copysignl with
+ arguments ARG1 and ARG2. Return NULL_TREE if no simplification can
+ be made. */
static tree
-fold_builtin_stpcpy (location_t loc, tree fndecl, tree dest, tree src)
+fold_builtin_copysign (location_t loc, tree fndecl,
+ tree arg1, tree arg2, tree type)
{
- tree fn, len, lenp1, call, type;
+ tree tem;
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE))
+ if (!validate_arg (arg1, REAL_TYPE)
+ || !validate_arg (arg2, REAL_TYPE))
return NULL_TREE;
- len = c_strlen (src, 1);
- if (!len
- || TREE_CODE (len) != INTEGER_CST)
- return NULL_TREE;
+ /* copysign(X,X) is X. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return fold_convert_loc (loc, type, arg1);
- if (optimize_function_for_size_p (cfun)
- /* If length is zero it's small enough. */
- && !integer_zerop (len))
- return NULL_TREE;
+ /* If ARG1 and ARG2 are compile-time constants, determine the result. */
+ if (TREE_CODE (arg1) == REAL_CST
+ && TREE_CODE (arg2) == REAL_CST
+ && !TREE_OVERFLOW (arg1)
+ && !TREE_OVERFLOW (arg2))
+ {
+ REAL_VALUE_TYPE c1, c2;
- fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
- if (!fn)
- return NULL_TREE;
+ c1 = TREE_REAL_CST (arg1);
+ c2 = TREE_REAL_CST (arg2);
+ /* c1.sign := c2.sign. */
+ real_copysign (&c1, &c2);
+ return build_real (type, c1);
+ }
- 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);
+ /* copysign(X, Y) is fabs(X) when Y is always non-negative.
+ Remember to evaluate Y for side-effects. */
+ if (tree_expr_nonnegative_p (arg2))
+ return omit_one_operand_loc (loc, type,
+ fold_build1_loc (loc, ABS_EXPR, type, arg1),
+ arg2);
+
+ /* Strip sign changing operations for the first argument. */
+ tem = fold_strip_sign_ops (arg1);
+ if (tem)
+ return build_call_expr_loc (loc, fndecl, 2, tem, arg2);
- 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;
+ return NULL_TREE;
}
-/* 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. */
+/* Fold a call to builtin isascii with argument ARG. */
-tree
-fold_builtin_strncpy (location_t loc, tree fndecl, tree dest,
- tree src, tree len, tree slen)
+static tree
+fold_builtin_isascii (location_t loc, tree arg)
{
- 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)
+ if (!validate_arg (arg, INTEGER_TYPE))
return NULL_TREE;
+ else
+ {
+ /* Transform isascii(c) -> ((c & ~0x7f) == 0). */
+ arg = fold_build2 (BIT_AND_EXPR, integer_type_node, arg,
+ build_int_cst (integer_type_node,
+ ~ (unsigned HOST_WIDE_INT) 0x7f));
+ return fold_build2_loc (loc, EQ_EXPR, integer_type_node,
+ arg, integer_zero_node);
+ }
+}
- if (!slen)
- slen = c_strlen (src, 1);
+/* Fold a call to builtin toascii with argument ARG. */
- /* Now, we must be passed a constant src ptr parameter. */
- if (slen == 0 || TREE_CODE (slen) != INTEGER_CST)
+static tree
+fold_builtin_toascii (location_t loc, tree arg)
+{
+ if (!validate_arg (arg, INTEGER_TYPE))
return NULL_TREE;
- slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
+ /* Transform toascii(c) -> (c & 0x7f). */
+ return fold_build2_loc (loc, BIT_AND_EXPR, integer_type_node, arg,
+ build_int_cst (integer_type_node, 0x7f));
+}
- /* 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;
+/* Fold a call to builtin isdigit with argument ARG. */
- /* 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. */
-
-static tree
-fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
-{
- if (!validate_arg (arg1, POINTER_TYPE)
- || !validate_arg (arg2, INTEGER_TYPE)
- || !validate_arg (len, INTEGER_TYPE))
+static tree
+fold_builtin_isdigit (location_t loc, tree arg)
+{
+ if (!validate_arg (arg, INTEGER_TYPE))
return NULL_TREE;
else
{
- const char *p1;
+ /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9. */
+ /* According to the C standard, isdigit is unaffected by locale.
+ However, it definitely is affected by the target character set. */
+ unsigned HOST_WIDE_INT target_digit0
+ = lang_hooks.to_target_charset ('0');
- if (TREE_CODE (arg2) != INTEGER_CST
- || !tree_fits_uhwi_p (len))
+ if (target_digit0 == 0)
return NULL_TREE;
- p1 = c_getstr (arg1);
- if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
- {
- char c;
- const char *r;
- tree tem;
-
- if (target_char_cast (arg2, &c))
- return NULL_TREE;
+ arg = fold_convert_loc (loc, unsigned_type_node, arg);
+ arg = fold_build2 (MINUS_EXPR, unsigned_type_node, arg,
+ build_int_cst (unsigned_type_node, target_digit0));
+ return fold_build2_loc (loc, LE_EXPR, integer_type_node, arg,
+ build_int_cst (unsigned_type_node, 9));
+ }
+}
- r = (const char *) memchr (p1, c, tree_to_uhwi (len));
+/* Fold a call to fabs, fabsf or fabsl with argument ARG. */
- if (r == NULL)
- return build_int_cst (TREE_TYPE (arg1), 0);
+static tree
+fold_builtin_fabs (location_t loc, tree arg, tree type)
+{
+ if (!validate_arg (arg, REAL_TYPE))
+ return NULL_TREE;
- tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
- return fold_convert_loc (loc, type, tem);
- }
- return NULL_TREE;
- }
+ arg = fold_convert_loc (loc, type, arg);
+ if (TREE_CODE (arg) == REAL_CST)
+ return fold_abs_const (arg, type);
+ return fold_build1_loc (loc, ABS_EXPR, type, arg);
}
-/* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
- Return NULL_TREE if no simplification can be made. */
+/* Fold a call to abs, labs, llabs or imaxabs with argument ARG. */
static tree
-fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
+fold_builtin_abs (location_t loc, tree arg, tree type)
{
- const char *p1, *p2;
-
- if (!validate_arg (arg1, POINTER_TYPE)
- || !validate_arg (arg2, POINTER_TYPE)
- || !validate_arg (len, INTEGER_TYPE))
+ if (!validate_arg (arg, INTEGER_TYPE))
return NULL_TREE;
- /* If the LEN parameter is zero, return zero. */
- if (integer_zerop (len))
- return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
- arg1, arg2);
+ arg = fold_convert_loc (loc, type, arg);
+ if (TREE_CODE (arg) == INTEGER_CST)
+ return fold_abs_const (arg, type);
+ return fold_build1_loc (loc, ABS_EXPR, type, arg);
+}
- /* If ARG1 and ARG2 are the same (and not volatile), return zero. */
- if (operand_equal_p (arg1, arg2, 0))
- return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
+/* Fold a fma operation with arguments ARG[012]. */
- p1 = c_getstr (arg1);
- p2 = c_getstr (arg2);
+tree
+fold_fma (location_t loc ATTRIBUTE_UNUSED,
+ tree type, tree arg0, tree arg1, tree arg2)
+{
+ if (TREE_CODE (arg0) == REAL_CST
+ && TREE_CODE (arg1) == REAL_CST
+ && TREE_CODE (arg2) == REAL_CST)
+ return do_mpfr_arg3 (arg0, arg1, arg2, type, mpfr_fma);
- /* If all arguments are constant, and the value of len is not greater
- than the lengths of arg1 and arg2, evaluate at compile-time. */
- if (tree_fits_uhwi_p (len) && p1 && p2
- && compare_tree_int (len, strlen (p1) + 1) <= 0
- && compare_tree_int (len, strlen (p2) + 1) <= 0)
- {
- const int r = memcmp (p1, p2, tree_to_uhwi (len));
+ return NULL_TREE;
+}
- if (r > 0)
- return integer_one_node;
- else if (r < 0)
- return integer_minus_one_node;
- else
- return integer_zero_node;
- }
+/* Fold a call to fma, fmaf, or fmal with arguments ARG[012]. */
- /* If len parameter is one, return an expression corresponding to
- (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
- if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
+static tree
+fold_builtin_fma (location_t loc, tree arg0, tree arg1, tree arg2, tree type)
+{
+ if (validate_arg (arg0, REAL_TYPE)
+ && validate_arg (arg1, REAL_TYPE)
+ && validate_arg (arg2, REAL_TYPE))
{
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node
- = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+ tree tem = fold_fma (loc, type, arg0, arg1, arg2);
+ if (tem)
+ return tem;
- tree ind1
- = fold_convert_loc (loc, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert_loc (loc,
- cst_uchar_ptr_node,
- arg1)));
- tree ind2
- = fold_convert_loc (loc, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert_loc (loc,
- cst_uchar_ptr_node,
- arg2)));
- return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
+ /* ??? Only expand to FMA_EXPR if it's directly supported. */
+ if (optab_handler (fma_optab, TYPE_MODE (type)) != CODE_FOR_nothing)
+ return fold_build3_loc (loc, FMA_EXPR, type, arg0, arg1, arg2);
}
-
return NULL_TREE;
}
-/* Fold function call to builtin strcmp with arguments ARG1 and ARG2.
- Return NULL_TREE if no simplification can be made. */
+/* Fold a call to builtin fmin or fmax. */
static tree
-fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
+fold_builtin_fmin_fmax (location_t loc, tree arg0, tree arg1,
+ tree type, bool max)
{
- const char *p1, *p2;
+ if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, REAL_TYPE))
+ {
+ /* Calculate the result when the argument is a constant. */
+ tree res = do_mpfr_arg2 (arg0, arg1, type, (max ? mpfr_max : mpfr_min));
- if (!validate_arg (arg1, POINTER_TYPE)
- || !validate_arg (arg2, POINTER_TYPE))
- return NULL_TREE;
+ if (res)
+ return res;
- /* If ARG1 and ARG2 are the same (and not volatile), return zero. */
- if (operand_equal_p (arg1, arg2, 0))
- return integer_zero_node;
+ /* If either argument is NaN, return the other one. Avoid the
+ transformation if we get (and honor) a signalling NaN. Using
+ omit_one_operand() ensures we create a non-lvalue. */
+ if (TREE_CODE (arg0) == REAL_CST
+ && real_isnan (&TREE_REAL_CST (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 (arg1)
+ || ! TREE_REAL_CST (arg1).signalling))
+ return omit_one_operand_loc (loc, type, arg0, arg1);
- p1 = c_getstr (arg1);
- p2 = c_getstr (arg2);
+ /* Transform fmin/fmax(x,x) -> x. */
+ if (operand_equal_p (arg0, arg1, OEP_PURE_SAME))
+ return omit_one_operand_loc (loc, type, arg0, arg1);
- if (p1 && p2)
- {
- const int i = strcmp (p1, p2);
- if (i < 0)
- return integer_minus_one_node;
- else if (i > 0)
- return integer_one_node;
- else
- return integer_zero_node;
+ /* Convert fmin/fmax to MIN_EXPR/MAX_EXPR. C99 requires these
+ functions to return the numeric arg if the other one is NaN.
+ These tree codes don't honor that, so only transform if
+ -ffinite-math-only is set. C99 doesn't require -0.0 to be
+ handled, so we don't have to worry about it either. */
+ if (flag_finite_math_only)
+ return fold_build2_loc (loc, (max ? MAX_EXPR : MIN_EXPR), type,
+ fold_convert_loc (loc, type, arg0),
+ fold_convert_loc (loc, type, arg1));
}
+ return NULL_TREE;
+}
- /* If the second arg is "", return *(const unsigned char*)arg1. */
- if (p2 && *p2 == '\0')
- {
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node
- = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
- return fold_convert_loc (loc, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert_loc (loc,
- cst_uchar_ptr_node,
- arg1)));
- }
+/* Fold a call to builtin carg(a+bi) -> atan2(b,a). */
- /* If the first arg is "", return -*(const unsigned char*)arg2. */
- if (p1 && *p1 == '\0')
+static tree
+fold_builtin_carg (location_t loc, tree arg, tree type)
+{
+ if (validate_arg (arg, COMPLEX_TYPE)
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == REAL_TYPE)
{
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node
- = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+ tree atan2_fn = mathfn_built_in (type, BUILT_IN_ATAN2);
- tree temp
- = fold_convert_loc (loc, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert_loc (loc,
- cst_uchar_ptr_node,
- arg2)));
- return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
+ if (atan2_fn)
+ {
+ tree new_arg = builtin_save_expr (arg);
+ tree r_arg = fold_build1_loc (loc, REALPART_EXPR, type, new_arg);
+ tree i_arg = fold_build1_loc (loc, IMAGPART_EXPR, type, new_arg);
+ return build_call_expr_loc (loc, atan2_fn, 2, i_arg, r_arg);
+ }
}
return NULL_TREE;
}
-/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
- Return NULL_TREE if no simplification can be made. */
+/* Fold a call to builtin logb/ilogb. */
static tree
-fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
+fold_builtin_logb (location_t loc, tree arg, tree rettype)
{
- const char *p1, *p2;
-
- if (!validate_arg (arg1, POINTER_TYPE)
- || !validate_arg (arg2, POINTER_TYPE)
- || !validate_arg (len, INTEGER_TYPE))
+ if (! validate_arg (arg, REAL_TYPE))
return NULL_TREE;
- /* If the LEN parameter is zero, return zero. */
- if (integer_zerop (len))
- return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
- arg1, arg2);
+ STRIP_NOPS (arg);
- /* If ARG1 and ARG2 are the same (and not volatile), return zero. */
- if (operand_equal_p (arg1, arg2, 0))
- return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
+ {
+ const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
- p1 = c_getstr (arg1);
- p2 = c_getstr (arg2);
-
- if (tree_fits_uhwi_p (len) && p1 && p2)
- {
- const int i = strncmp (p1, p2, tree_to_uhwi (len));
- if (i > 0)
- return integer_one_node;
- else if (i < 0)
- return integer_minus_one_node;
- else
- return integer_zero_node;
- }
-
- /* If the second arg is "", and the length is greater than zero,
- return *(const unsigned char*)arg1. */
- if (p2 && *p2 == '\0'
- && TREE_CODE (len) == INTEGER_CST
- && tree_int_cst_sgn (len) == 1)
- {
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node
- = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
- return fold_convert_loc (loc, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert_loc (loc,
- cst_uchar_ptr_node,
- arg1)));
- }
-
- /* If the first arg is "", and the length is greater than zero,
- return -*(const unsigned char*)arg2. */
- if (p1 && *p1 == '\0'
- && TREE_CODE (len) == INTEGER_CST
- && tree_int_cst_sgn (len) == 1)
- {
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node
- = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
- tree temp = fold_convert_loc (loc, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert_loc (loc,
- cst_uchar_ptr_node,
- arg2)));
- return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
- }
-
- /* If len parameter is one, return an expression corresponding to
- (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
- if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
- {
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node
- = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
- tree ind1 = fold_convert_loc (loc, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert_loc (loc,
- cst_uchar_ptr_node,
- arg1)));
- tree ind2 = fold_convert_loc (loc, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert_loc (loc,
- cst_uchar_ptr_node,
- arg2)));
- return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
+ switch (value->cl)
+ {
+ case rvc_nan:
+ case rvc_inf:
+ /* If arg is Inf or NaN and we're logb, return it. */
+ if (TREE_CODE (rettype) == REAL_TYPE)
+ {
+ /* For logb(-Inf) we have to return +Inf. */
+ if (real_isinf (value) && real_isneg (value))
+ {
+ REAL_VALUE_TYPE tem;
+ real_inf (&tem);
+ return build_real (rettype, tem);
+ }
+ return fold_convert_loc (loc, rettype, arg);
+ }
+ /* Fall through... */
+ case rvc_zero:
+ /* Zero may set errno and/or raise an exception for logb, also
+ for ilogb we don't know FP_ILOGB0. */
+ return NULL_TREE;
+ case rvc_normal:
+ /* For normal numbers, proceed iff radix == 2. In GCC,
+ normalized significands are in the range [0.5, 1.0). We
+ want the exponent as if they were [1.0, 2.0) so get the
+ exponent and subtract 1. */
+ if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
+ return fold_convert_loc (loc, rettype,
+ build_int_cst (integer_type_node,
+ REAL_EXP (value)-1));
+ break;
+ }
}
return NULL_TREE;
}
-/* Fold function call to builtin signbit, signbitf or signbitl with argument
- ARG. Return NULL_TREE if no simplification can be made. */
+/* Fold a call to builtin significand, if radix == 2. */
static tree
-fold_builtin_signbit (location_t loc, tree arg, tree type)
+fold_builtin_significand (location_t loc, tree arg, tree rettype)
{
- if (!validate_arg (arg, REAL_TYPE))
+ if (! validate_arg (arg, REAL_TYPE))
return NULL_TREE;
- /* If ARG is a compile-time constant, determine the result. */
- if (TREE_CODE (arg) == REAL_CST
- && !TREE_OVERFLOW (arg))
+ STRIP_NOPS (arg);
+
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
{
- REAL_VALUE_TYPE c;
+ const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
- c = TREE_REAL_CST (arg);
- return (REAL_VALUE_NEGATIVE (c)
- ? build_one_cst (type)
- : build_zero_cst (type));
+ switch (value->cl)
+ {
+ case rvc_zero:
+ case rvc_nan:
+ case rvc_inf:
+ /* If arg is +-0, +-Inf or +-NaN, then return it. */
+ return fold_convert_loc (loc, rettype, arg);
+ case rvc_normal:
+ /* For normal numbers, proceed iff radix == 2. */
+ if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
+ {
+ REAL_VALUE_TYPE result = *value;
+ /* In GCC, normalized significands are in the range [0.5,
+ 1.0). We want them to be [1.0, 2.0) so set the
+ exponent to 1. */
+ SET_REAL_EXP (&result, 1);
+ return build_real (rettype, result);
+ }
+ break;
+ }
}
- /* If ARG is non-negative, the result is always zero. */
- if (tree_expr_nonnegative_p (arg))
- 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))))
- return fold_convert (type,
- fold_build2_loc (loc, LT_EXPR, boolean_type_node, arg,
- build_real (TREE_TYPE (arg), dconst0)));
-
return NULL_TREE;
}
-/* Fold function call to builtin copysign, copysignf or copysignl with
- arguments ARG1 and ARG2. Return NULL_TREE if no simplification can
- be made. */
+/* Fold a call to builtin frexp, we can assume the base is 2. */
static tree
-fold_builtin_copysign (location_t loc, tree fndecl,
- tree arg1, tree arg2, tree type)
+fold_builtin_frexp (location_t loc, tree arg0, tree arg1, tree rettype)
{
- tree tem;
+ if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
+ return NULL_TREE;
- if (!validate_arg (arg1, REAL_TYPE)
- || !validate_arg (arg2, REAL_TYPE))
+ STRIP_NOPS (arg0);
+
+ if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
return NULL_TREE;
- /* copysign(X,X) is X. */
- if (operand_equal_p (arg1, arg2, 0))
- return fold_convert_loc (loc, type, arg1);
+ arg1 = build_fold_indirect_ref_loc (loc, arg1);
- /* If ARG1 and ARG2 are compile-time constants, determine the result. */
- if (TREE_CODE (arg1) == REAL_CST
- && TREE_CODE (arg2) == REAL_CST
- && !TREE_OVERFLOW (arg1)
- && !TREE_OVERFLOW (arg2))
+ /* Proceed if a valid pointer type was passed in. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == integer_type_node)
{
- REAL_VALUE_TYPE c1, c2;
-
- c1 = TREE_REAL_CST (arg1);
- c2 = TREE_REAL_CST (arg2);
- /* c1.sign := c2.sign. */
- real_copysign (&c1, &c2);
- return build_real (type, c1);
- }
+ const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
+ tree frac, exp;
- /* copysign(X, Y) is fabs(X) when Y is always non-negative.
- Remember to evaluate Y for side-effects. */
- if (tree_expr_nonnegative_p (arg2))
- return omit_one_operand_loc (loc, type,
- fold_build1_loc (loc, ABS_EXPR, type, arg1),
- arg2);
+ switch (value->cl)
+ {
+ case rvc_zero:
+ /* For +-0, return (*exp = 0, +-0). */
+ exp = integer_zero_node;
+ frac = arg0;
+ break;
+ case rvc_nan:
+ case rvc_inf:
+ /* For +-NaN or +-Inf, *exp is unspecified, return arg0. */
+ return omit_one_operand_loc (loc, rettype, arg0, arg1);
+ case rvc_normal:
+ {
+ /* Since the frexp function always expects base 2, and in
+ GCC normalized significands are already in the range
+ [0.5, 1.0), we have exactly what frexp wants. */
+ REAL_VALUE_TYPE frac_rvt = *value;
+ SET_REAL_EXP (&frac_rvt, 0);
+ frac = build_real (rettype, frac_rvt);
+ exp = build_int_cst (integer_type_node, REAL_EXP (value));
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
- /* Strip sign changing operations for the first argument. */
- tem = fold_strip_sign_ops (arg1);
- if (tem)
- return build_call_expr_loc (loc, fndecl, 2, tem, arg2);
+ /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
+ arg1 = fold_build2_loc (loc, MODIFY_EXPR, rettype, arg1, exp);
+ TREE_SIDE_EFFECTS (arg1) = 1;
+ return fold_build2_loc (loc, COMPOUND_EXPR, rettype, arg1, frac);
+ }
return NULL_TREE;
}
-/* Fold a call to builtin isascii with argument ARG. */
+/* Fold a call to builtin ldexp or scalbn/scalbln. If LDEXP is true
+ then we can assume the base is two. If it's false, then we have to
+ check the mode of the TYPE parameter in certain cases. */
static tree
-fold_builtin_isascii (location_t loc, tree arg)
+fold_builtin_load_exponent (location_t loc, tree arg0, tree arg1,
+ tree type, bool ldexp)
{
- if (!validate_arg (arg, INTEGER_TYPE))
- return NULL_TREE;
- else
+ if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, INTEGER_TYPE))
{
- /* Transform isascii(c) -> ((c & ~0x7f) == 0). */
- arg = fold_build2 (BIT_AND_EXPR, integer_type_node, arg,
- build_int_cst (integer_type_node,
- ~ (unsigned HOST_WIDE_INT) 0x7f));
- return fold_build2_loc (loc, EQ_EXPR, integer_type_node,
- arg, integer_zero_node);
- }
-}
-
-/* Fold a call to builtin toascii with argument ARG. */
+ STRIP_NOPS (arg0);
+ STRIP_NOPS (arg1);
-static tree
-fold_builtin_toascii (location_t loc, tree arg)
-{
- if (!validate_arg (arg, INTEGER_TYPE))
- return NULL_TREE;
+ /* If arg0 is 0, Inf or NaN, or if arg1 is 0, then return arg0. */
+ if (real_zerop (arg0) || integer_zerop (arg1)
+ || (TREE_CODE (arg0) == REAL_CST
+ && !real_isfinite (&TREE_REAL_CST (arg0))))
+ return omit_one_operand_loc (loc, type, arg0, arg1);
- /* Transform toascii(c) -> (c & 0x7f). */
- return fold_build2_loc (loc, BIT_AND_EXPR, integer_type_node, arg,
- build_int_cst (integer_type_node, 0x7f));
-}
+ /* If both arguments are constant, then try to evaluate it. */
+ if ((ldexp || REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2)
+ && TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0)
+ && tree_fits_shwi_p (arg1))
+ {
+ /* Bound the maximum adjustment to twice the range of the
+ mode's valid exponents. Use abs to ensure the range is
+ positive as a sanity check. */
+ const long max_exp_adj = 2 *
+ labs (REAL_MODE_FORMAT (TYPE_MODE (type))->emax
+ - REAL_MODE_FORMAT (TYPE_MODE (type))->emin);
-/* Fold a call to builtin isdigit with argument ARG. */
+ /* Get the user-requested adjustment. */
+ const HOST_WIDE_INT req_exp_adj = tree_to_shwi (arg1);
-static tree
-fold_builtin_isdigit (location_t loc, tree arg)
-{
- if (!validate_arg (arg, INTEGER_TYPE))
- return NULL_TREE;
- else
- {
- /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9. */
- /* According to the C standard, isdigit is unaffected by locale.
- However, it definitely is affected by the target character set. */
- unsigned HOST_WIDE_INT target_digit0
- = lang_hooks.to_target_charset ('0');
+ /* The requested adjustment must be inside this range. This
+ is a preliminary cap to avoid things like overflow, we
+ may still fail to compute the result for other reasons. */
+ if (-max_exp_adj < req_exp_adj && req_exp_adj < max_exp_adj)
+ {
+ REAL_VALUE_TYPE initial_result;
- if (target_digit0 == 0)
- return NULL_TREE;
+ real_ldexp (&initial_result, &TREE_REAL_CST (arg0), req_exp_adj);
- arg = fold_convert_loc (loc, unsigned_type_node, arg);
- arg = fold_build2 (MINUS_EXPR, unsigned_type_node, arg,
- build_int_cst (unsigned_type_node, target_digit0));
- return fold_build2_loc (loc, LE_EXPR, integer_type_node, arg,
- build_int_cst (unsigned_type_node, 9));
+ /* Ensure we didn't overflow. */
+ if (! real_isinf (&initial_result))
+ {
+ const REAL_VALUE_TYPE trunc_result
+ = real_value_truncate (TYPE_MODE (type), initial_result);
+
+ /* Only proceed if the target mode can hold the
+ resulting value. */
+ if (REAL_VALUES_EQUAL (initial_result, trunc_result))
+ return build_real (type, trunc_result);
+ }
+ }
+ }
}
+
+ return NULL_TREE;
}
-/* Fold a call to fabs, fabsf or fabsl with argument ARG. */
+/* Fold a call to builtin modf. */
static tree
-fold_builtin_fabs (location_t loc, tree arg, tree type)
+fold_builtin_modf (location_t loc, tree arg0, tree arg1, tree rettype)
{
- if (!validate_arg (arg, REAL_TYPE))
+ if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
return NULL_TREE;
- arg = fold_convert_loc (loc, type, arg);
- if (TREE_CODE (arg) == REAL_CST)
- return fold_abs_const (arg, type);
- return fold_build1_loc (loc, ABS_EXPR, type, arg);
-}
-
-/* Fold a call to abs, labs, llabs or imaxabs with argument ARG. */
+ STRIP_NOPS (arg0);
-static tree
-fold_builtin_abs (location_t loc, tree arg, tree type)
-{
- if (!validate_arg (arg, INTEGER_TYPE))
+ if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
return NULL_TREE;
- arg = fold_convert_loc (loc, type, arg);
- if (TREE_CODE (arg) == INTEGER_CST)
- return fold_abs_const (arg, type);
- return fold_build1_loc (loc, ABS_EXPR, type, arg);
-}
+ arg1 = build_fold_indirect_ref_loc (loc, arg1);
-/* Fold a fma operation with arguments ARG[012]. */
+ /* Proceed if a valid pointer type was passed in. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == TYPE_MAIN_VARIANT (rettype))
+ {
+ const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
+ REAL_VALUE_TYPE trunc, frac;
-tree
-fold_fma (location_t loc ATTRIBUTE_UNUSED,
- tree type, tree arg0, tree arg1, tree arg2)
-{
- if (TREE_CODE (arg0) == REAL_CST
- && TREE_CODE (arg1) == REAL_CST
- && TREE_CODE (arg2) == REAL_CST)
- return do_mpfr_arg3 (arg0, arg1, arg2, type, mpfr_fma);
+ switch (value->cl)
+ {
+ case rvc_nan:
+ case rvc_zero:
+ /* For +-NaN or +-0, return (*arg1 = arg0, arg0). */
+ trunc = frac = *value;
+ break;
+ case rvc_inf:
+ /* For +-Inf, return (*arg1 = arg0, +-0). */
+ frac = dconst0;
+ frac.sign = value->sign;
+ trunc = *value;
+ break;
+ case rvc_normal:
+ /* Return (*arg1 = trunc(arg0), arg0-trunc(arg0)). */
+ real_trunc (&trunc, VOIDmode, value);
+ real_arithmetic (&frac, MINUS_EXPR, value, &trunc);
+ /* If the original number was negative and already
+ integral, then the fractional part is -0.0. */
+ if (value->sign && frac.cl == rvc_zero)
+ frac.sign = value->sign;
+ break;
+ }
+
+ /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
+ arg1 = fold_build2_loc (loc, MODIFY_EXPR, rettype, arg1,
+ build_real (rettype, trunc));
+ TREE_SIDE_EFFECTS (arg1) = 1;
+ return fold_build2_loc (loc, COMPOUND_EXPR, rettype, arg1,
+ build_real (rettype, frac));
+ }
return NULL_TREE;
}
-/* Fold a call to fma, fmaf, or fmal with arguments ARG[012]. */
+/* Given a location LOC, an interclass builtin function decl FNDECL
+ and its single argument ARG, return an folded expression computing
+ the same, or NULL_TREE if we either couldn't or didn't want to fold
+ (the latter happen if there's an RTL instruction available). */
static tree
-fold_builtin_fma (location_t loc, tree arg0, tree arg1, tree arg2, tree type)
+fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
{
- if (validate_arg (arg0, REAL_TYPE)
- && validate_arg (arg1, REAL_TYPE)
- && validate_arg (arg2, REAL_TYPE))
- {
- tree tem = fold_fma (loc, type, arg0, arg1, arg2);
- if (tem)
- return tem;
+ machine_mode mode;
- /* ??? Only expand to FMA_EXPR if it's directly supported. */
- if (optab_handler (fma_optab, TYPE_MODE (type)) != CODE_FOR_nothing)
- return fold_build3_loc (loc, FMA_EXPR, type, arg0, arg1, arg2);
- }
- return NULL_TREE;
-}
+ if (!validate_arg (arg, REAL_TYPE))
+ return NULL_TREE;
-/* Fold a call to builtin fmin or fmax. */
+ if (interclass_mathfn_icode (arg, fndecl) != CODE_FOR_nothing)
+ return NULL_TREE;
-static tree
-fold_builtin_fmin_fmax (location_t loc, tree arg0, tree arg1,
- tree type, bool max)
-{
- if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, REAL_TYPE))
+ mode = TYPE_MODE (TREE_TYPE (arg));
+
+ /* If there is no optab, try generic code. */
+ switch (DECL_FUNCTION_CODE (fndecl))
{
- /* Calculate the result when the argument is a constant. */
- tree res = do_mpfr_arg2 (arg0, arg1, type, (max ? mpfr_max : mpfr_min));
+ tree result;
- if (res)
- return res;
+ CASE_FLT_FN (BUILT_IN_ISINF):
+ {
+ /* isinf(x) -> isgreater(fabs(x),DBL_MAX). */
+ tree const isgr_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
+ tree const type = TREE_TYPE (arg);
+ REAL_VALUE_TYPE r;
+ char buf[128];
- /* If either argument is NaN, return the other one. Avoid the
- transformation if we get (and honor) a signalling NaN. Using
- 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)))
- || ! 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)))
- || ! TREE_REAL_CST (arg1).signalling))
- return omit_one_operand_loc (loc, type, arg0, arg1);
+ get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
+ real_from_string (&r, buf);
+ result = build_call_expr (isgr_fn, 2,
+ fold_build1_loc (loc, ABS_EXPR, type, arg),
+ build_real (type, r));
+ return result;
+ }
+ CASE_FLT_FN (BUILT_IN_FINITE):
+ case BUILT_IN_ISFINITE:
+ {
+ /* isfinite(x) -> islessequal(fabs(x),DBL_MAX). */
+ tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
+ tree const type = TREE_TYPE (arg);
+ REAL_VALUE_TYPE r;
+ char buf[128];
- /* Transform fmin/fmax(x,x) -> x. */
- if (operand_equal_p (arg0, arg1, OEP_PURE_SAME))
- return omit_one_operand_loc (loc, type, arg0, arg1);
+ get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
+ real_from_string (&r, buf);
+ result = build_call_expr (isle_fn, 2,
+ fold_build1_loc (loc, ABS_EXPR, type, arg),
+ build_real (type, r));
+ /*result = fold_build2_loc (loc, UNGT_EXPR,
+ TREE_TYPE (TREE_TYPE (fndecl)),
+ fold_build1_loc (loc, ABS_EXPR, type, arg),
+ build_real (type, r));
+ result = fold_build1_loc (loc, TRUTH_NOT_EXPR,
+ TREE_TYPE (TREE_TYPE (fndecl)),
+ result);*/
+ return result;
+ }
+ case BUILT_IN_ISNORMAL:
+ {
+ /* isnormal(x) -> isgreaterequal(fabs(x),DBL_MIN) &
+ islessequal(fabs(x),DBL_MAX). */
+ tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
+ tree const isge_fn = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
+ tree const type = TREE_TYPE (arg);
+ REAL_VALUE_TYPE rmax, rmin;
+ char buf[128];
- /* Convert fmin/fmax to MIN_EXPR/MAX_EXPR. C99 requires these
- functions to return the numeric arg if the other one is NaN.
- These tree codes don't honor that, so only transform if
- -ffinite-math-only is set. C99 doesn't require -0.0 to be
- handled, so we don't have to worry about it either. */
- if (flag_finite_math_only)
- return fold_build2_loc (loc, (max ? MAX_EXPR : MIN_EXPR), type,
- fold_convert_loc (loc, type, arg0),
- fold_convert_loc (loc, type, arg1));
+ get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
+ real_from_string (&rmax, buf);
+ sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (mode)->emin - 1);
+ real_from_string (&rmin, buf);
+ arg = builtin_save_expr (fold_build1_loc (loc, ABS_EXPR, type, arg));
+ result = build_call_expr (isle_fn, 2, arg,
+ build_real (type, rmax));
+ result = fold_build2 (BIT_AND_EXPR, integer_type_node, result,
+ build_call_expr (isge_fn, 2, arg,
+ build_real (type, rmin)));
+ return result;
+ }
+ default:
+ break;
}
+
return NULL_TREE;
}
-/* Fold a call to builtin carg(a+bi) -> atan2(b,a). */
+/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
+ ARG is the argument for the call. */
static tree
-fold_builtin_carg (location_t loc, tree arg, tree type)
+fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
{
- if (validate_arg (arg, COMPLEX_TYPE)
- && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == REAL_TYPE)
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
+ REAL_VALUE_TYPE r;
+
+ if (!validate_arg (arg, REAL_TYPE))
+ return NULL_TREE;
+
+ switch (builtin_index)
{
- tree atan2_fn = mathfn_built_in (type, BUILT_IN_ATAN2);
+ case BUILT_IN_ISINF:
+ if (!HONOR_INFINITIES (arg))
+ return omit_one_operand_loc (loc, type, integer_zero_node, arg);
- if (atan2_fn)
- {
- tree new_arg = builtin_save_expr (arg);
- tree r_arg = fold_build1_loc (loc, REALPART_EXPR, type, new_arg);
- tree i_arg = fold_build1_loc (loc, IMAGPART_EXPR, type, new_arg);
- return build_call_expr_loc (loc, atan2_fn, 2, i_arg, r_arg);
+ if (TREE_CODE (arg) == REAL_CST)
+ {
+ r = TREE_REAL_CST (arg);
+ if (real_isinf (&r))
+ return real_compare (GT_EXPR, &r, &dconst0)
+ ? integer_one_node : integer_minus_one_node;
+ else
+ return integer_zero_node;
}
- }
- return NULL_TREE;
-}
+ return NULL_TREE;
-/* Fold a call to builtin logb/ilogb. */
+ case BUILT_IN_ISINF_SIGN:
+ {
+ /* isinf_sign(x) -> isinf(x) ? (signbit(x) ? -1 : 1) : 0 */
+ /* In a boolean context, GCC will fold the inner COND_EXPR to
+ 1. So e.g. "if (isinf_sign(x))" would be folded to just
+ "if (isinf(x) ? 1 : 0)" which becomes "if (isinf(x))". */
+ tree signbit_fn = mathfn_built_in_1 (TREE_TYPE (arg), BUILT_IN_SIGNBIT, 0);
+ tree isinf_fn = builtin_decl_explicit (BUILT_IN_ISINF);
+ tree tmp = NULL_TREE;
-static tree
-fold_builtin_logb (location_t loc, tree arg, tree rettype)
-{
- if (! validate_arg (arg, REAL_TYPE))
- return NULL_TREE;
+ arg = builtin_save_expr (arg);
- STRIP_NOPS (arg);
+ if (signbit_fn && isinf_fn)
+ {
+ tree signbit_call = build_call_expr_loc (loc, signbit_fn, 1, arg);
+ tree isinf_call = build_call_expr_loc (loc, isinf_fn, 1, arg);
- if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
- {
- const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
+ signbit_call = fold_build2_loc (loc, NE_EXPR, integer_type_node,
+ signbit_call, integer_zero_node);
+ isinf_call = fold_build2_loc (loc, NE_EXPR, integer_type_node,
+ isinf_call, integer_zero_node);
- switch (value->cl)
- {
- case rvc_nan:
- case rvc_inf:
- /* If arg is Inf or NaN and we're logb, return it. */
- if (TREE_CODE (rettype) == REAL_TYPE)
- {
- /* For logb(-Inf) we have to return +Inf. */
- if (real_isinf (value) && real_isneg (value))
- {
- REAL_VALUE_TYPE tem;
- real_inf (&tem);
- return build_real (rettype, tem);
- }
- return fold_convert_loc (loc, rettype, arg);
+ tmp = fold_build3_loc (loc, COND_EXPR, integer_type_node, signbit_call,
+ integer_minus_one_node, integer_one_node);
+ tmp = fold_build3_loc (loc, COND_EXPR, integer_type_node,
+ isinf_call, tmp,
+ integer_zero_node);
}
- /* Fall through... */
- case rvc_zero:
- /* Zero may set errno and/or raise an exception for logb, also
- for ilogb we don't know FP_ILOGB0. */
- return NULL_TREE;
- case rvc_normal:
- /* For normal numbers, proceed iff radix == 2. In GCC,
- normalized significands are in the range [0.5, 1.0). We
- want the exponent as if they were [1.0, 2.0) so get the
- exponent and subtract 1. */
- if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
- return fold_convert_loc (loc, rettype,
- build_int_cst (integer_type_node,
- REAL_EXP (value)-1));
- break;
+
+ return tmp;
}
- }
- return NULL_TREE;
-}
+ case BUILT_IN_ISFINITE:
+ if (!HONOR_NANS (arg)
+ && !HONOR_INFINITIES (arg))
+ return omit_one_operand_loc (loc, type, integer_one_node, arg);
-/* Fold a call to builtin significand, if radix == 2. */
+ if (TREE_CODE (arg) == REAL_CST)
+ {
+ r = TREE_REAL_CST (arg);
+ return real_isfinite (&r) ? integer_one_node : integer_zero_node;
+ }
-static tree
-fold_builtin_significand (location_t loc, tree arg, tree rettype)
-{
- if (! validate_arg (arg, REAL_TYPE))
- return NULL_TREE;
+ return NULL_TREE;
- STRIP_NOPS (arg);
+ case BUILT_IN_ISNAN:
+ if (!HONOR_NANS (arg))
+ return omit_one_operand_loc (loc, type, integer_zero_node, arg);
- if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
- {
- const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
+ if (TREE_CODE (arg) == REAL_CST)
+ {
+ r = TREE_REAL_CST (arg);
+ return real_isnan (&r) ? integer_one_node : integer_zero_node;
+ }
- switch (value->cl)
- {
- case rvc_zero:
- case rvc_nan:
- case rvc_inf:
- /* If arg is +-0, +-Inf or +-NaN, then return it. */
- return fold_convert_loc (loc, rettype, arg);
- case rvc_normal:
- /* For normal numbers, proceed iff radix == 2. */
- if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
- {
- REAL_VALUE_TYPE result = *value;
- /* In GCC, normalized significands are in the range [0.5,
- 1.0). We want them to be [1.0, 2.0) so set the
- exponent to 1. */
- SET_REAL_EXP (&result, 1);
- return build_real (rettype, result);
- }
- break;
- }
- }
+ arg = builtin_save_expr (arg);
+ return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg);
- return NULL_TREE;
+ default:
+ gcc_unreachable ();
+ }
}
-/* Fold a call to builtin frexp, we can assume the base is 2. */
+/* Fold a call to __builtin_fpclassify(int, int, int, int, int, ...).
+ This builtin will generate code to return the appropriate floating
+ point classification depending on the value of the floating point
+ number passed in. The possible return values must be supplied as
+ int arguments to the call in the following order: FP_NAN, FP_INFINITE,
+ FP_NORMAL, FP_SUBNORMAL and FP_ZERO. The ellipses is for exactly
+ one floating point argument which is "type generic". */
static tree
-fold_builtin_frexp (location_t loc, tree arg0, tree arg1, tree rettype)
+fold_builtin_fpclassify (location_t loc, tree *args, int nargs)
{
- if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
- return NULL_TREE;
+ tree fp_nan, fp_infinite, fp_normal, fp_subnormal, fp_zero,
+ arg, type, res, tmp;
+ machine_mode mode;
+ REAL_VALUE_TYPE r;
+ char buf[128];
- STRIP_NOPS (arg0);
+ /* Verify the required arguments in the original call. */
+ 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));
- if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
- return NULL_TREE;
+ /* fpclassify(x) ->
+ isnan(x) ? FP_NAN :
+ (fabs(x) == Inf ? FP_INFINITE :
+ (fabs(x) >= DBL_MIN ? FP_NORMAL :
+ (x == 0 ? FP_ZERO : FP_SUBNORMAL))). */
- arg1 = build_fold_indirect_ref_loc (loc, arg1);
+ tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
+ build_real (type, dconst0));
+ res = fold_build3_loc (loc, COND_EXPR, integer_type_node,
+ tmp, fp_zero, fp_subnormal);
- /* Proceed if a valid pointer type was passed in. */
- if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == integer_type_node)
- {
- const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
- tree frac, exp;
+ sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (mode)->emin - 1);
+ real_from_string (&r, buf);
+ tmp = fold_build2_loc (loc, GE_EXPR, integer_type_node,
+ arg, build_real (type, r));
+ res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, fp_normal, res);
- switch (value->cl)
- {
- case rvc_zero:
- /* For +-0, return (*exp = 0, +-0). */
- exp = integer_zero_node;
- frac = arg0;
- break;
- case rvc_nan:
- case rvc_inf:
- /* For +-NaN or +-Inf, *exp is unspecified, return arg0. */
- return omit_one_operand_loc (loc, rettype, arg0, arg1);
- case rvc_normal:
- {
- /* Since the frexp function always expects base 2, and in
- GCC normalized significands are already in the range
- [0.5, 1.0), we have exactly what frexp wants. */
- REAL_VALUE_TYPE frac_rvt = *value;
- SET_REAL_EXP (&frac_rvt, 0);
- frac = build_real (rettype, frac_rvt);
- exp = build_int_cst (integer_type_node, REAL_EXP (value));
- }
- break;
- default:
- gcc_unreachable ();
- }
+ if (HONOR_INFINITIES (mode))
+ {
+ real_inf (&r);
+ tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
+ build_real (type, r));
+ res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp,
+ fp_infinite, res);
+ }
- /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
- arg1 = fold_build2_loc (loc, MODIFY_EXPR, rettype, arg1, exp);
- TREE_SIDE_EFFECTS (arg1) = 1;
- return fold_build2_loc (loc, COMPOUND_EXPR, rettype, arg1, frac);
+ if (HONOR_NANS (mode))
+ {
+ tmp = fold_build2_loc (loc, ORDERED_EXPR, integer_type_node, arg, arg);
+ res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, res, fp_nan);
}
- return NULL_TREE;
+ return res;
}
-/* Fold a call to builtin ldexp or scalbn/scalbln. If LDEXP is true
- then we can assume the base is two. If it's false, then we have to
- check the mode of the TYPE parameter in certain cases. */
+/* Fold a call to an unordered comparison function such as
+ __builtin_isgreater(). FNDECL is the FUNCTION_DECL for the function
+ being called and ARG0 and ARG1 are the arguments for the call.
+ UNORDERED_CODE and ORDERED_CODE are comparison codes that give
+ the opposite of the desired result. UNORDERED_CODE is used
+ for modes that can hold NaNs and ORDERED_CODE is used for
+ the rest. */
static tree
-fold_builtin_load_exponent (location_t loc, tree arg0, tree arg1,
- tree type, bool ldexp)
+fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
+ enum tree_code unordered_code,
+ enum tree_code ordered_code)
{
- if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, INTEGER_TYPE))
- {
- STRIP_NOPS (arg0);
- STRIP_NOPS (arg1);
-
- /* If arg0 is 0, Inf or NaN, or if arg1 is 0, then return arg0. */
- if (real_zerop (arg0) || integer_zerop (arg1)
- || (TREE_CODE (arg0) == REAL_CST
- && !real_isfinite (&TREE_REAL_CST (arg0))))
- return omit_one_operand_loc (loc, type, arg0, arg1);
-
- /* If both arguments are constant, then try to evaluate it. */
- if ((ldexp || REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2)
- && TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0)
- && tree_fits_shwi_p (arg1))
- {
- /* Bound the maximum adjustment to twice the range of the
- mode's valid exponents. Use abs to ensure the range is
- positive as a sanity check. */
- const long max_exp_adj = 2 *
- labs (REAL_MODE_FORMAT (TYPE_MODE (type))->emax
- - REAL_MODE_FORMAT (TYPE_MODE (type))->emin);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
+ enum tree_code code;
+ tree type0, type1;
+ enum tree_code code0, code1;
+ tree cmp_type = NULL_TREE;
- /* Get the user-requested adjustment. */
- const HOST_WIDE_INT req_exp_adj = tree_to_shwi (arg1);
+ type0 = TREE_TYPE (arg0);
+ type1 = TREE_TYPE (arg1);
- /* The requested adjustment must be inside this range. This
- is a preliminary cap to avoid things like overflow, we
- may still fail to compute the result for other reasons. */
- if (-max_exp_adj < req_exp_adj && req_exp_adj < max_exp_adj)
- {
- REAL_VALUE_TYPE initial_result;
+ code0 = TREE_CODE (type0);
+ code1 = TREE_CODE (type1);
- real_ldexp (&initial_result, &TREE_REAL_CST (arg0), req_exp_adj);
+ if (code0 == REAL_TYPE && code1 == REAL_TYPE)
+ /* Choose the wider of two real types. */
+ cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
+ ? type0 : type1;
+ else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
+ cmp_type = type0;
+ else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
+ cmp_type = type1;
- /* Ensure we didn't overflow. */
- if (! real_isinf (&initial_result))
- {
- const REAL_VALUE_TYPE trunc_result
- = real_value_truncate (TYPE_MODE (type), initial_result);
+ arg0 = fold_convert_loc (loc, cmp_type, arg0);
+ arg1 = fold_convert_loc (loc, cmp_type, arg1);
- /* Only proceed if the target mode can hold the
- resulting value. */
- if (REAL_VALUES_EQUAL (initial_result, trunc_result))
- return build_real (type, trunc_result);
- }
- }
- }
+ if (unordered_code == UNORDERED_EXPR)
+ {
+ 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);
}
- return NULL_TREE;
+ 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 a call to builtin modf. */
+/* 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_modf (location_t loc, tree arg0, tree arg1, tree rettype)
+fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
+ tree arg0, tree arg1, tree arg2)
{
- if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
- return NULL_TREE;
-
- STRIP_NOPS (arg0);
-
- if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
- return NULL_TREE;
-
- arg1 = build_fold_indirect_ref_loc (loc, arg1);
-
- /* Proceed if a valid pointer type was passed in. */
- if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == TYPE_MAIN_VARIANT (rettype))
+ 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)
{
- const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
- REAL_VALUE_TYPE trunc, frac;
-
- switch (value->cl)
- {
- case rvc_nan:
- case rvc_zero:
- /* For +-NaN or +-0, return (*arg1 = arg0, arg0). */
- trunc = frac = *value;
- break;
- case rvc_inf:
- /* For +-Inf, return (*arg1 = arg0, +-0). */
- frac = dconst0;
- frac.sign = value->sign;
- trunc = *value;
- break;
- case rvc_normal:
- /* Return (*arg1 = trunc(arg0), arg0-trunc(arg0)). */
- real_trunc (&trunc, VOIDmode, value);
- real_arithmetic (&frac, MINUS_EXPR, value, &trunc);
- /* If the original number was negative and already
- integral, then the fractional part is -0.0. */
- if (value->sign && frac.cl == rvc_zero)
- frac.sign = value->sign;
- break;
- }
-
- /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
- arg1 = fold_build2_loc (loc, MODIFY_EXPR, rettype, arg1,
- build_real (rettype, trunc));
- TREE_SIDE_EFFECTS (arg1) = 1;
- return fold_build2_loc (loc, COMPOUND_EXPR, rettype, arg1,
- build_real (rettype, frac));
+ 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 ();
}
-
- return NULL_TREE;
+ 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);
}
-/* Given a location LOC, an interclass builtin function decl FNDECL
- and its single argument ARG, return an folded expression computing
- the same, or NULL_TREE if we either couldn't or didn't want to fold
- (the latter happen if there's an RTL instruction available). */
+/* Fold a call to built-in function FNDECL with 0 arguments.
+ This function returns NULL_TREE if no simplification was possible. */
static tree
-fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
+fold_builtin_0 (location_t loc, tree fndecl)
{
- enum machine_mode mode;
-
- if (!validate_arg (arg, REAL_TYPE))
- return NULL_TREE;
-
- if (interclass_mathfn_icode (arg, fndecl) != CODE_FOR_nothing)
- return NULL_TREE;
-
- mode = TYPE_MODE (TREE_TYPE (arg));
-
- /* If there is no optab, try generic code. */
- switch (DECL_FUNCTION_CODE (fndecl))
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ switch (fcode)
{
- tree result;
-
- CASE_FLT_FN (BUILT_IN_ISINF):
- {
- /* isinf(x) -> isgreater(fabs(x),DBL_MAX). */
- tree const isgr_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
- tree const type = TREE_TYPE (arg);
- REAL_VALUE_TYPE r;
- char buf[128];
+ CASE_FLT_FN (BUILT_IN_INF):
+ case BUILT_IN_INFD32:
+ case BUILT_IN_INFD64:
+ case BUILT_IN_INFD128:
+ return fold_builtin_inf (loc, type, true);
- get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
- real_from_string (&r, buf);
- result = build_call_expr (isgr_fn, 2,
- fold_build1_loc (loc, ABS_EXPR, type, arg),
- build_real (type, r));
- return result;
- }
- CASE_FLT_FN (BUILT_IN_FINITE):
- case BUILT_IN_ISFINITE:
- {
- /* isfinite(x) -> islessequal(fabs(x),DBL_MAX). */
- tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
- tree const type = TREE_TYPE (arg);
- REAL_VALUE_TYPE r;
- char buf[128];
+ CASE_FLT_FN (BUILT_IN_HUGE_VAL):
+ return fold_builtin_inf (loc, type, false);
- get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
- real_from_string (&r, buf);
- result = build_call_expr (isle_fn, 2,
- fold_build1_loc (loc, ABS_EXPR, type, arg),
- build_real (type, r));
- /*result = fold_build2_loc (loc, UNGT_EXPR,
- TREE_TYPE (TREE_TYPE (fndecl)),
- fold_build1_loc (loc, ABS_EXPR, type, arg),
- build_real (type, r));
- result = fold_build1_loc (loc, TRUTH_NOT_EXPR,
- TREE_TYPE (TREE_TYPE (fndecl)),
- result);*/
- return result;
- }
- case BUILT_IN_ISNORMAL:
- {
- /* isnormal(x) -> isgreaterequal(fabs(x),DBL_MIN) &
- islessequal(fabs(x),DBL_MAX). */
- tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
- tree const isge_fn = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
- tree const type = TREE_TYPE (arg);
- REAL_VALUE_TYPE rmax, rmin;
- char buf[128];
+ case BUILT_IN_CLASSIFY_TYPE:
+ return fold_builtin_classify_type (NULL_TREE);
- get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
- real_from_string (&rmax, buf);
- sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (mode)->emin - 1);
- real_from_string (&rmin, buf);
- arg = builtin_save_expr (fold_build1_loc (loc, ABS_EXPR, type, arg));
- result = build_call_expr (isle_fn, 2, arg,
- build_real (type, rmax));
- result = fold_build2 (BIT_AND_EXPR, integer_type_node, result,
- build_call_expr (isge_fn, 2, arg,
- build_real (type, rmin)));
- return result;
- }
default:
break;
}
-
return NULL_TREE;
}
-/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
- ARG is the argument for the call. */
+/* Fold a call to built-in function FNDECL with 1 argument, ARG0.
+ This function returns NULL_TREE if no simplification was possible. */
static tree
-fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
+fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
{
tree type = TREE_TYPE (TREE_TYPE (fndecl));
- REAL_VALUE_TYPE r;
-
- if (!validate_arg (arg, REAL_TYPE))
- return NULL_TREE;
-
- switch (builtin_index)
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ switch (fcode)
{
- case BUILT_IN_ISINF:
- if (!HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (arg))))
- return omit_one_operand_loc (loc, type, integer_zero_node, arg);
-
- if (TREE_CODE (arg) == REAL_CST)
- {
- r = TREE_REAL_CST (arg);
- if (real_isinf (&r))
- return real_compare (GT_EXPR, &r, &dconst0)
- ? integer_one_node : integer_minus_one_node;
- else
- return integer_zero_node;
- }
+ case BUILT_IN_CONSTANT_P:
+ {
+ tree val = fold_builtin_constant_p (arg0);
- return NULL_TREE;
+ /* Gimplification will pull the CALL_EXPR for the builtin out of
+ an if condition. When not optimizing, we'll not CSE it back.
+ To avoid link error types of regressions, return false now. */
+ if (!val && !optimize)
+ val = integer_zero_node;
- case BUILT_IN_ISINF_SIGN:
- {
- /* isinf_sign(x) -> isinf(x) ? (signbit(x) ? -1 : 1) : 0 */
- /* In a boolean context, GCC will fold the inner COND_EXPR to
- 1. So e.g. "if (isinf_sign(x))" would be folded to just
- "if (isinf(x) ? 1 : 0)" which becomes "if (isinf(x))". */
- tree signbit_fn = mathfn_built_in_1 (TREE_TYPE (arg), BUILT_IN_SIGNBIT, 0);
- tree isinf_fn = builtin_decl_explicit (BUILT_IN_ISINF);
- tree tmp = NULL_TREE;
+ return val;
+ }
- arg = builtin_save_expr (arg);
-
- if (signbit_fn && isinf_fn)
- {
- tree signbit_call = build_call_expr_loc (loc, signbit_fn, 1, arg);
- tree isinf_call = build_call_expr_loc (loc, isinf_fn, 1, arg);
-
- signbit_call = fold_build2_loc (loc, NE_EXPR, integer_type_node,
- signbit_call, integer_zero_node);
- isinf_call = fold_build2_loc (loc, NE_EXPR, integer_type_node,
- isinf_call, integer_zero_node);
-
- tmp = fold_build3_loc (loc, COND_EXPR, integer_type_node, signbit_call,
- integer_minus_one_node, integer_one_node);
- tmp = fold_build3_loc (loc, COND_EXPR, integer_type_node,
- isinf_call, tmp,
- integer_zero_node);
- }
-
- return tmp;
- }
-
- case BUILT_IN_ISFINITE:
- if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg)))
- && !HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (arg))))
- return omit_one_operand_loc (loc, type, integer_one_node, arg);
-
- if (TREE_CODE (arg) == REAL_CST)
- {
- r = TREE_REAL_CST (arg);
- return real_isfinite (&r) ? integer_one_node : integer_zero_node;
- }
-
- return NULL_TREE;
-
- case BUILT_IN_ISNAN:
- if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg))))
- return omit_one_operand_loc (loc, type, integer_zero_node, arg);
-
- if (TREE_CODE (arg) == REAL_CST)
- {
- r = TREE_REAL_CST (arg);
- return real_isnan (&r) ? integer_one_node : integer_zero_node;
- }
-
- arg = builtin_save_expr (arg);
- return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg);
-
- default:
- gcc_unreachable ();
- }
-}
-
-/* Fold a call to __builtin_fpclassify(int, int, int, int, int, ...).
- This builtin will generate code to return the appropriate floating
- point classification depending on the value of the floating point
- number passed in. The possible return values must be supplied as
- int arguments to the call in the following order: FP_NAN, FP_INFINITE,
- FP_NORMAL, FP_SUBNORMAL and FP_ZERO. The ellipses is for exactly
- one floating point argument which is "type generic". */
-
-static tree
-fold_builtin_fpclassify (location_t loc, tree exp)
-{
- tree fp_nan, fp_infinite, fp_normal, fp_subnormal, fp_zero,
- arg, type, res, tmp;
- enum 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);
- type = TREE_TYPE (arg);
- mode = TYPE_MODE (type);
- arg = builtin_save_expr (fold_build1_loc (loc, ABS_EXPR, type, arg));
-
- /* fpclassify(x) ->
- isnan(x) ? FP_NAN :
- (fabs(x) == Inf ? FP_INFINITE :
- (fabs(x) >= DBL_MIN ? FP_NORMAL :
- (x == 0 ? FP_ZERO : FP_SUBNORMAL))). */
-
- tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
- build_real (type, dconst0));
- res = fold_build3_loc (loc, COND_EXPR, integer_type_node,
- tmp, fp_zero, fp_subnormal);
-
- sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (mode)->emin - 1);
- real_from_string (&r, buf);
- tmp = fold_build2_loc (loc, GE_EXPR, integer_type_node,
- arg, build_real (type, r));
- res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, fp_normal, res);
-
- if (HONOR_INFINITIES (mode))
- {
- real_inf (&r);
- tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
- build_real (type, r));
- res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp,
- fp_infinite, res);
- }
-
- if (HONOR_NANS (mode))
- {
- tmp = fold_build2_loc (loc, ORDERED_EXPR, integer_type_node, arg, arg);
- res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, res, fp_nan);
- }
-
- return res;
-}
-
-/* Fold a call to an unordered comparison function such as
- __builtin_isgreater(). FNDECL is the FUNCTION_DECL for the function
- being called and ARG0 and ARG1 are the arguments for the call.
- UNORDERED_CODE and ORDERED_CODE are comparison codes that give
- the opposite of the desired result. UNORDERED_CODE is used
- for modes that can hold NaNs and ORDERED_CODE is used for
- the rest. */
-
-static tree
-fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
- enum tree_code unordered_code,
- enum tree_code ordered_code)
-{
- tree type = TREE_TYPE (TREE_TYPE (fndecl));
- enum tree_code code;
- tree type0, type1;
- enum tree_code code0, code1;
- tree cmp_type = NULL_TREE;
-
- type0 = TREE_TYPE (arg0);
- type1 = TREE_TYPE (arg1);
-
- code0 = TREE_CODE (type0);
- code1 = TREE_CODE (type1);
-
- if (code0 == REAL_TYPE && code1 == REAL_TYPE)
- /* Choose the wider of two real types. */
- cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
- ? type0 : type1;
- else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
- cmp_type = type0;
- else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
- cmp_type = type1;
-
- arg0 = fold_convert_loc (loc, cmp_type, arg0);
- arg1 = fold_convert_loc (loc, cmp_type, arg1);
-
- if (unordered_code == UNORDERED_EXPR)
- {
- if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (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;
- return fold_build1_loc (loc, TRUTH_NOT_EXPR, type,
- fold_build2_loc (loc, code, type, arg0, arg1));
-}
-
-/* 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. */
-
-static tree
-fold_builtin_0 (location_t loc, tree fndecl, bool ignore ATTRIBUTE_UNUSED)
-{
- tree type = TREE_TYPE (TREE_TYPE (fndecl));
- enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
- switch (fcode)
- {
- CASE_FLT_FN (BUILT_IN_INF):
- case BUILT_IN_INFD32:
- case BUILT_IN_INFD64:
- case BUILT_IN_INFD128:
- return fold_builtin_inf (loc, type, true);
-
- CASE_FLT_FN (BUILT_IN_HUGE_VAL):
- return fold_builtin_inf (loc, type, false);
-
- 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;
- }
- return NULL_TREE;
-}
-
-/* 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. */
-
-static tree
-fold_builtin_1 (location_t loc, tree fndecl, tree arg0, bool ignore)
-{
- tree type = TREE_TYPE (TREE_TYPE (fndecl));
- enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
- switch (fcode)
- {
- case BUILT_IN_CONSTANT_P:
- {
- tree val = fold_builtin_constant_p (arg0);
-
- /* Gimplification will pull the CALL_EXPR for the builtin out of
- an if condition. When not optimizing, we'll not CSE it back.
- To avoid link error types of regressions, return false now. */
- if (!val && !optimize)
- val = integer_zero_node;
-
- return val;
- }
-
- case BUILT_IN_CLASSIFY_TYPE:
- return fold_builtin_classify_type (arg0);
+ case BUILT_IN_CLASSIFY_TYPE:
+ return fold_builtin_classify_type (arg0);
case BUILT_IN_STRLEN:
return fold_builtin_strlen (loc, type, arg0);
CASE_FLT_FN (BUILT_IN_CREAL):
if (validate_arg (arg0, COMPLEX_TYPE)
&& TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
- return non_lvalue_loc (loc, fold_build1_loc (loc, REALPART_EXPR, type, arg0));;
+ return non_lvalue_loc (loc, fold_build1_loc (loc, REALPART_EXPR, type, arg0));
break;
CASE_FLT_FN (BUILT_IN_CIMAG):
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))
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);
}
/* 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);
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);
-
case BUILT_IN_STRSPN:
return fold_builtin_strspn (loc, arg0, arg1);
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);
return fold_builtin_strpbrk (loc, arg0, arg1, type);
case BUILT_IN_EXPECT:
- return fold_builtin_expect (loc, arg0, arg1);
+ return fold_builtin_expect (loc, arg0, arg1, NULL_TREE);
CASE_FLT_FN (BUILT_IN_POW):
return fold_builtin_pow (loc, fndecl, arg0, arg1, type);
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);
}
/* 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);
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);
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_EXPECT:
+ return fold_builtin_expect (loc, arg0, arg1, arg2);
+
+ 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);
- 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);
-
- 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;
-
- default:
- break;
- }
- return NULL_TREE;
-}
+ default:
+ break;
+ }
+ return NULL_TREE;
+}
/* 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)
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
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;
}
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. */
-
-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. */
+/* 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
-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
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;
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;
/* 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);
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_strstr (location_t loc, tree s1, tree s2, tree type)
-{
- if (!validate_arg (s1, POINTER_TYPE)
- || !validate_arg (s2, POINTER_TYPE))
- return NULL_TREE;
- else
- {
- tree fn;
- const char *p1, *p2;
-
- p2 = c_getstr (s2);
- if (p2 == NULL)
- return NULL_TREE;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- const char *r = strstr (p1, p2);
- tree tem;
-
- if (r == NULL)
- return build_int_cst (TREE_TYPE (s1), 0);
-
- /* Return an offset into the constant string argument. */
- tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
- return fold_convert_loc (loc, type, tem);
- }
-
- /* The argument is const char *, and the result is char *, so we need
- a type conversion here to avoid a warning. */
- if (p2[0] == '\0')
- return fold_convert_loc (loc, type, s1);
-
- if (p2[1] != '\0')
- return NULL_TREE;
-
- fn = builtin_decl_implicit (BUILT_IN_STRCHR);
- if (!fn)
- return NULL_TREE;
-
- /* New argument list transforming strstr(s1, s2) to
- strchr(s1, s2[0]). */
- return build_call_expr_loc (loc, fn, 2, s1,
- build_int_cst (integer_type_node, p2[0]));
- }
-}
-
-/* Simplify a call to the strchr builtin. S1 and S2 are the arguments to
- the call, and TYPE is its return type.
-
- 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_strchr (location_t loc, tree s1, tree s2, tree type)
-{
- if (!validate_arg (s1, POINTER_TYPE)
- || !validate_arg (s2, INTEGER_TYPE))
- return NULL_TREE;
- else
- {
- const char *p1;
-
- if (TREE_CODE (s2) != INTEGER_CST)
- return NULL_TREE;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- char c;
- const char *r;
- tree tem;
-
- if (target_char_cast (s2, &c))
- return NULL_TREE;
-
- r = strchr (p1, c);
-
- if (r == NULL)
- return build_int_cst (TREE_TYPE (s1), 0);
-
- /* Return an offset into the constant string argument. */
- tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
- return fold_convert_loc (loc, type, tem);
- }
- return NULL_TREE;
- }
-}
-
-/* Simplify a call to the strrchr builtin. S1 and S2 are the arguments to
- the call, and TYPE is its return type.
-
- 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_strrchr (location_t loc, tree s1, tree s2, tree type)
-{
- if (!validate_arg (s1, POINTER_TYPE)
- || !validate_arg (s2, INTEGER_TYPE))
- return NULL_TREE;
- else
- {
- tree fn;
- const char *p1;
-
- if (TREE_CODE (s2) != INTEGER_CST)
- return NULL_TREE;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- char c;
- const char *r;
- tree tem;
-
- if (target_char_cast (s2, &c))
- return NULL_TREE;
-
- r = strrchr (p1, c);
-
- if (r == NULL)
- return build_int_cst (TREE_TYPE (s1), 0);
-
- /* Return an offset into the constant string argument. */
- tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
- return fold_convert_loc (loc, type, tem);
- }
-
- if (! integer_zerop (s2))
- return NULL_TREE;
-
- fn = builtin_decl_implicit (BUILT_IN_STRCHR);
- if (!fn)
- return NULL_TREE;
-
- /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */
- return build_call_expr_loc (loc, fn, 2, s1, s2);
- }
-}
-
-/* Simplify a call to the strpbrk builtin. S1 and S2 are the arguments
- to the call, and TYPE is its return type.
-
- 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_strpbrk (location_t loc, tree s1, tree s2, tree type)
-{
- if (!validate_arg (s1, POINTER_TYPE)
- || !validate_arg (s2, POINTER_TYPE))
- return NULL_TREE;
- else
- {
- tree fn;
- const char *p1, *p2;
-
- p2 = c_getstr (s2);
- if (p2 == NULL)
- return NULL_TREE;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- const char *r = strpbrk (p1, p2);
- tree tem;
-
- if (r == NULL)
- return build_int_cst (TREE_TYPE (s1), 0);
-
- /* Return an offset into the constant string argument. */
- tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
- return fold_convert_loc (loc, type, tem);
- }
-
- if (p2[0] == '\0')
- /* strpbrk(x, "") == NULL.
- Evaluate and ignore s1 in case it had side-effects. */
- return omit_one_operand_loc (loc, TREE_TYPE (s1), integer_zero_node, s1);
-
- if (p2[1] != '\0')
- return NULL_TREE; /* Really call strpbrk. */
-
- fn = builtin_decl_implicit (BUILT_IN_STRCHR);
- if (!fn)
- return NULL_TREE;
-
- /* New argument list transforming strpbrk(s1, s2) to
- strchr(s1, s2[0]). */
- return build_call_expr_loc (loc, fn, 2, s1,
- build_int_cst (integer_type_node, p2[0]));
- }
-}
-
-/* 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. */
-
-static tree
-fold_builtin_strcat (location_t loc ATTRIBUTE_UNUSED, tree dst, tree src)
-{
- 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 strcpy_fn = builtin_decl_implicit (BUILT_IN_STRCPY);
-
- if (!strlen_fn || !strcpy_fn)
- return NULL_TREE;
-
- /* If we don't have a movstr we don't want to emit an strcpy
- call. We have to do that if the length of the source string
- isn't computable (in that case we can use memcpy probably
- later expanding to a sequence of mov instructions). If we
- have movstr instructions we can emit strcpy calls. */
- if (!HAVE_movstr)
- {
- tree 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);
-
- call = build_call_expr_loc (loc, strcpy_fn, 2, newdst, src);
- 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.
-
- 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_strspn (location_t loc, tree s1, tree s2)
-{
- if (!validate_arg (s1, POINTER_TYPE)
- || !validate_arg (s2, POINTER_TYPE))
- return NULL_TREE;
- else
- {
- const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
-
- /* If both arguments are constants, evaluate at compile-time. */
- if (p1 && p2)
- {
- const size_t r = strspn (p1, p2);
- return build_int_cst (size_type_node, r);
- }
-
- /* If either argument is "", return NULL_TREE. */
- if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
- /* Evaluate and ignore both arguments in case either one has
- side-effects. */
- return omit_two_operands_loc (loc, size_type_node, size_zero_node,
- s1, s2);
- return NULL_TREE;
- }
-}
-
-/* Simplify a call to the strcspn builtin. S1 and S2 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_strcspn (location_t loc, tree s1, tree s2)
-{
- if (!validate_arg (s1, POINTER_TYPE)
- || !validate_arg (s2, POINTER_TYPE))
- return NULL_TREE;
- else
- {
- const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
-
- /* If both arguments are constants, evaluate at compile-time. */
- if (p1 && p2)
- {
- const size_t r = strcspn (p1, p2);
- return build_int_cst (size_type_node, r);
- }
-
- /* If the first argument is "", return NULL_TREE. */
- if (p1 && *p1 == '\0')
- {
- /* Evaluate and ignore argument s2 in case it has
- side-effects. */
- return omit_one_operand_loc (loc, size_type_node,
- size_zero_node, s2);
- }
-
- /* If the second argument is "", return __builtin_strlen(s1). */
- if (p2 && *p2 == '\0')
- {
- tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
-
- /* If the replacement _DECL isn't initialized, don't do the
- transformation. */
- if (!fn)
- return NULL_TREE;
-
- return build_call_expr_loc (loc, fn, 1, s1);
- }
- return NULL_TREE;
- }
-}
-
-/* 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. */
-
-bool
-fold_builtin_next_arg (tree exp, bool va_start_p)
-{
- tree fntype = TREE_TYPE (current_function_decl);
- int nargs = call_expr_nargs (exp);
- tree arg;
- /* There is good chance the current input_location points inside the
- definition of the va_start macro (perhaps on the token for
- builtin) in a system header, so warnings will not be emitted.
- Use the location in real source code. */
- source_location current_location =
- linemap_unwind_to_first_non_reserved_loc (line_table, input_location,
- NULL);
-
- if (!stdarg_p (fntype))
- {
- error ("%<va_start%> used in function with fixed args");
- return true;
- }
-
- if (va_start_p)
- {
- if (va_start_p && (nargs != 2))
- {
- error ("wrong number of arguments to function %<va_start%>");
- return true;
- }
- arg = CALL_EXPR_ARG (exp, 1);
- }
- /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0)
- when we checked the arguments and if needed issued a warning. */
- else
- {
- if (nargs == 0)
- {
- /* Evidently an out of date version of <stdarg.h>; can't validate
- va_start's second argument, but can still work as intended. */
- warning_at (current_location,
- OPT_Wvarargs,
- "%<__builtin_next_arg%> called without an argument");
- return true;
- }
- else if (nargs > 1)
- {
- error ("wrong number of arguments to function %<__builtin_next_arg%>");
- return true;
- }
- arg = CALL_EXPR_ARG (exp, 0);
- }
-
- if (TREE_CODE (arg) == SSA_NAME)
- arg = SSA_NAME_VAR (arg);
-
- /* We destructively modify the call to be __builtin_va_start (ap, 0)
- or __builtin_next_arg (0) the first time we see it, after checking
- the arguments and if needed issuing a warning. */
- if (!integer_zerop (arg))
- {
- tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
-
- /* Strip off all nops for the sake of the comparison. This
- is not quite the same as STRIP_NOPS. It does more.
- We must also strip off INDIRECT_EXPR for C++ reference
- parameters. */
- while (CONVERT_EXPR_P (arg)
- || TREE_CODE (arg) == INDIRECT_REF)
- arg = TREE_OPERAND (arg, 0);
- if (arg != last_parm)
- {
- /* FIXME: Sometimes with the tree optimizers we can get the
- not the last argument even though the user used the last
- argument. We just warn and set the arg to be the last
- argument so that we will get wrong-code because of
- it. */
- warning_at (current_location,
- OPT_Wvarargs,
- "second parameter of %<va_start%> not last named argument");
- }
-
- /* Undefined by C99 7.15.1.4p4 (va_start):
- "If the parameter parmN is declared with the register storage
- class, with a function or array type, or with a type that is
- not compatible with the type that results after application of
- the default argument promotions, the behavior is undefined."
- */
- else if (DECL_REGISTER (arg))
- {
- warning_at (current_location,
- OPT_Wvarargs,
- "undefined behaviour when second parameter of "
- "%<va_start%> is declared with %<register%> storage");
- }
-
- /* We want to verify the second parameter just once before the tree
- optimizers are run and then avoid keeping it in the tree,
- as otherwise we could warn even for correct code like:
- void foo (int i, ...)
- { va_list ap; i++; va_start (ap, i); va_end (ap); } */
- if (va_start_p)
- CALL_EXPR_ARG (exp, 1) = integer_zero_node;
- else
- CALL_EXPR_ARG (exp, 0) = integer_zero_node;
- }
- return false;
-}
-
-
-/* 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
-expand_builtin_object_size (tree exp)
-{
- tree ost;
- int object_size_type;
- tree fndecl = get_callee_fndecl (exp);
-
- if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- {
- error ("%Kfirst argument of %D must be a pointer, second integer constant",
- exp, fndecl);
- expand_builtin_trap ();
- return const0_rtx;
- }
-
- ost = CALL_EXPR_ARG (exp, 1);
- STRIP_NOPS (ost);
-
- if (TREE_CODE (ost) != INTEGER_CST
- || tree_int_cst_sgn (ost) < 0
- || compare_tree_int (ost, 3) > 0)
- {
- error ("%Klast argument of %D is not integer constant between 0 and 3",
- exp, fndecl);
- expand_builtin_trap ();
- return const0_rtx;
- }
-
- object_size_type = tree_to_shwi (ost);
-
- return object_size_type < 2 ? constm1_rtx : const0_rtx;
-}
-
-/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin.
- FCODE is the BUILT_IN_* to use.
- Return 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_memory_chk (tree exp, rtx target, enum machine_mode mode,
- enum built_in_function fcode)
-{
- tree dest, src, len, size;
-
- if (!validate_arglist (exp,
- POINTER_TYPE,
- fcode == BUILT_IN_MEMSET_CHK
- ? INTEGER_TYPE : POINTER_TYPE,
- INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
- return NULL_RTX;
-
- dest = CALL_EXPR_ARG (exp, 0);
- src = CALL_EXPR_ARG (exp, 1);
- len = CALL_EXPR_ARG (exp, 2);
- size = CALL_EXPR_ARG (exp, 3);
-
- if (! tree_fits_uhwi_p (size))
- return NULL_RTX;
-
- if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
- {
- tree fn;
-
- if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
- {
- warning_at (tree_nonartificial_location (exp),
- 0, "%Kcall to %D will always overflow destination buffer",
- exp, get_callee_fndecl (exp));
- return NULL_RTX;
- }
-
- 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_RTX;
-
- fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 3, dest, src, len);
- gcc_assert (TREE_CODE (fn) == CALL_EXPR);
- CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
- return expand_expr (fn, target, mode, EXPAND_NORMAL);
- }
- else if (fcode == BUILT_IN_MEMSET_CHK)
- return NULL_RTX;
- else
- {
- unsigned int dest_align = get_pointer_alignment (dest);
-
- /* If DEST is not a pointer type, call the normal function. */
- if (dest_align == 0)
- return NULL_RTX;
-
- /* If SRC and DEST are the same (and not volatile), do nothing. */
- if (operand_equal_p (src, dest, 0))
- {
- tree expr;
-
- if (fcode != BUILT_IN_MEMPCPY_CHK)
- {
- /* Evaluate and ignore LEN in case it has side-effects. */
- expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
- return expand_expr (dest, target, mode, EXPAND_NORMAL);
- }
-
- expr = fold_build_pointer_plus (dest, len);
- return expand_expr (expr, target, mode, EXPAND_NORMAL);
- }
-
- /* __memmove_chk special case. */
- if (fcode == BUILT_IN_MEMMOVE_CHK)
- {
- unsigned int src_align = get_pointer_alignment (src);
-
- if (src_align == 0)
- return NULL_RTX;
-
- /* If src is categorized for a readonly section we can use
- normal __memcpy_chk. */
- if (readonly_data_expr (src))
- {
- tree fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
- if (!fn)
- return NULL_RTX;
- fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 4,
- dest, src, len, size);
- gcc_assert (TREE_CODE (fn) == CALL_EXPR);
- CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
- return expand_expr (fn, target, mode, EXPAND_NORMAL);
- }
- }
- return NULL_RTX;
- }
-}
-
-/* Emit warning if a buffer overflow is detected at compile time. */
-
-static void
-maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
-{
- int is_strlen = 0;
- tree len, size;
- location_t loc = tree_nonartificial_location (exp);
-
- switch (fcode)
- {
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STPCPY_CHK:
- /* For __strcat_chk the warning will be emitted only if overflowing
- by at least strlen (dest) + 1 bytes. */
- case BUILT_IN_STRCAT_CHK:
- len = CALL_EXPR_ARG (exp, 1);
- size = CALL_EXPR_ARG (exp, 2);
- is_strlen = 1;
- break;
- case BUILT_IN_STRNCAT_CHK:
- case BUILT_IN_STRNCPY_CHK:
- case BUILT_IN_STPNCPY_CHK:
- len = CALL_EXPR_ARG (exp, 2);
- size = CALL_EXPR_ARG (exp, 3);
- break;
- case BUILT_IN_SNPRINTF_CHK:
- case BUILT_IN_VSNPRINTF_CHK:
- len = CALL_EXPR_ARG (exp, 1);
- size = CALL_EXPR_ARG (exp, 3);
- break;
- default:
- gcc_unreachable ();
- }
-
- if (!len || !size)
- return;
-
- if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
- return;
-
- if (is_strlen)
- {
- len = c_strlen (len, 1);
- if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
- return;
- }
- else if (fcode == BUILT_IN_STRNCAT_CHK)
- {
- tree src = CALL_EXPR_ARG (exp, 1);
- if (! src || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
- return;
- src = c_strlen (src, 1);
- if (! src || ! tree_fits_uhwi_p (src))
- {
- warning_at (loc, 0, "%Kcall to %D might overflow destination buffer",
- exp, get_callee_fndecl (exp));
- return;
- }
- else if (tree_int_cst_lt (src, size))
- return;
- }
- else if (! tree_fits_uhwi_p (len) || ! tree_int_cst_lt (size, len))
- return;
-
- warning_at (loc, 0, "%Kcall to %D will always overflow destination buffer",
- exp, get_callee_fndecl (exp));
-}
-
-/* Emit warning if a buffer overflow is detected at compile time
- in __sprintf_chk/__vsprintf_chk calls. */
+ 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 void
-maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
+static tree
+fold_builtin_strstr (location_t loc, tree s1, tree s2, tree type)
{
- tree size, len, fmt;
- const char *fmt_str;
- int nargs = call_expr_nargs (exp);
+ if (!validate_arg (s1, POINTER_TYPE)
+ || !validate_arg (s2, POINTER_TYPE))
+ return NULL_TREE;
+ else
+ {
+ tree fn;
+ const char *p1, *p2;
- /* Verify the required arguments in the original call. */
+ p2 = c_getstr (s2);
+ if (p2 == NULL)
+ return NULL_TREE;
- if (nargs < 4)
- return;
- size = CALL_EXPR_ARG (exp, 2);
- fmt = CALL_EXPR_ARG (exp, 3);
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
+ {
+ const char *r = strstr (p1, p2);
+ tree tem;
- if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
- return;
+ if (r == NULL)
+ return build_int_cst (TREE_TYPE (s1), 0);
- /* Check whether the format is a literal string constant. */
- fmt_str = c_getstr (fmt);
- if (fmt_str == NULL)
- return;
+ /* Return an offset into the constant string argument. */
+ tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
+ return fold_convert_loc (loc, type, tem);
+ }
- if (!init_target_chars ())
- return;
+ /* The argument is const char *, and the result is char *, so we need
+ a type conversion here to avoid a warning. */
+ if (p2[0] == '\0')
+ return fold_convert_loc (loc, type, s1);
- /* If the format doesn't contain % args or %%, we know its size. */
- if (strchr (fmt_str, target_percent) == 0)
- len = build_int_cstu (size_type_node, strlen (fmt_str));
- /* If the format is "%s" and first ... argument is a string literal,
- we know it too. */
- else if (fcode == BUILT_IN_SPRINTF_CHK
- && strcmp (fmt_str, target_percent_s) == 0)
- {
- tree arg;
+ if (p2[1] != '\0')
+ return NULL_TREE;
- if (nargs < 5)
- return;
- arg = CALL_EXPR_ARG (exp, 4);
- if (! POINTER_TYPE_P (TREE_TYPE (arg)))
- return;
+ fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+ if (!fn)
+ return NULL_TREE;
- len = c_strlen (arg, 1);
- if (!len || ! tree_fits_uhwi_p (len))
- return;
+ /* New argument list transforming strstr(s1, s2) to
+ strchr(s1, s2[0]). */
+ return build_call_expr_loc (loc, fn, 2, s1,
+ build_int_cst (integer_type_node, p2[0]));
}
- else
- return;
-
- if (! tree_int_cst_lt (len, size))
- warning_at (tree_nonartificial_location (exp),
- 0, "%Kcall to %D will always overflow destination buffer",
- exp, get_callee_fndecl (exp));
}
-/* Emit warning if a free is called with address of a variable. */
+/* Simplify a call to the strchr builtin. S1 and S2 are the arguments to
+ the call, and TYPE is its return type.
-static void
-maybe_emit_free_warning (tree exp)
-{
- tree arg = CALL_EXPR_ARG (exp, 0);
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
- STRIP_NOPS (arg);
- if (TREE_CODE (arg) != ADDR_EXPR)
- return;
+ 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).
- arg = get_base_address (TREE_OPERAND (arg, 0));
- if (arg == NULL || INDIRECT_REF_P (arg) || TREE_CODE (arg) == MEM_REF)
- return;
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
- if (SSA_VAR_P (arg))
- warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object,
- "%Kattempt to free a non-heap object %qD", exp, arg);
+static tree
+fold_builtin_strchr (location_t loc, tree s1, tree s2, tree type)
+{
+ if (!validate_arg (s1, POINTER_TYPE)
+ || !validate_arg (s2, INTEGER_TYPE))
+ return NULL_TREE;
else
- warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object,
- "%Kattempt to free a non-heap object", exp);
-}
+ {
+ const char *p1;
-/* Fold a call to __builtin_object_size with arguments PTR and OST,
- if possible. */
+ if (TREE_CODE (s2) != INTEGER_CST)
+ return NULL_TREE;
-tree
-fold_builtin_object_size (tree ptr, tree ost)
-{
- unsigned HOST_WIDE_INT bytes;
- int object_size_type;
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
+ {
+ char c;
+ const char *r;
+ tree tem;
- if (!validate_arg (ptr, POINTER_TYPE)
- || !validate_arg (ost, INTEGER_TYPE))
- return NULL_TREE;
+ if (target_char_cast (s2, &c))
+ return NULL_TREE;
- STRIP_NOPS (ost);
+ r = strchr (p1, c);
- if (TREE_CODE (ost) != INTEGER_CST
- || tree_int_cst_sgn (ost) < 0
- || compare_tree_int (ost, 3) > 0)
- return NULL_TREE;
+ if (r == NULL)
+ return build_int_cst (TREE_TYPE (s1), 0);
- object_size_type = tree_to_shwi (ost);
+ /* Return an offset into the constant string argument. */
+ tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
+ return fold_convert_loc (loc, type, tem);
+ }
+ return NULL_TREE;
+ }
+}
- /* __builtin_object_size doesn't evaluate side-effects in its arguments;
- if there are any side-effects, it returns (size_t) -1 for types 0 and 1
- and (size_t) 0 for types 2 and 3. */
- if (TREE_SIDE_EFFECTS (ptr))
- return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0);
+/* Simplify a call to the strrchr builtin. S1 and S2 are the arguments to
+ the call, and TYPE is its return type.
- 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)))
- return build_int_cstu (size_type_node, bytes);
- }
- else if (TREE_CODE (ptr) == SSA_NAME)
- {
- /* If object size is not known yet, delay folding until
- later. Maybe subsequent passes will help determining
- it. */
- bytes = compute_builtin_object_size (ptr, object_size_type);
- if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 ? -1 : 0)
- && double_int_fits_to_tree_p (size_type_node,
- double_int::from_uhwi (bytes)))
- return build_int_cstu (size_type_node, bytes);
- }
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
- return NULL_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).
-/* 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. */
+ 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_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))
+static tree
+fold_builtin_strrchr (location_t loc, tree s1, tree s2, tree type)
+{
+ if (!validate_arg (s1, POINTER_TYPE)
+ || !validate_arg (s2, 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))
+ else
{
- 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);
- }
- }
+ tree fn;
+ const char *p1;
- if (! tree_fits_uhwi_p (size))
- return NULL_TREE;
+ if (TREE_CODE (s2) != INTEGER_CST)
+ return NULL_TREE;
- if (! integer_all_onesp (size))
- {
- if (! tree_fits_uhwi_p (len))
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
{
- /* 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;
+ char c;
+ const char *r;
+ tree tem;
- return build_call_expr_loc (loc, fn, 4, dest, src, len, size);
- }
- return NULL_TREE;
- }
+ if (target_char_cast (s2, &c))
+ return NULL_TREE;
+
+ r = strrchr (p1, c);
+
+ if (r == NULL)
+ return build_int_cst (TREE_TYPE (s1), 0);
+
+ /* Return an offset into the constant string argument. */
+ tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
+ return fold_convert_loc (loc, type, tem);
}
- else
- maxlen = len;
- if (tree_int_cst_lt (size, maxlen))
+ if (! integer_zerop (s2))
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;
+ fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+ if (!fn)
+ return NULL_TREE;
- return build_call_expr_loc (loc, fn, 3, dest, src, len);
+ /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */
+ return build_call_expr_loc (loc, fn, 2, s1, s2);
+ }
}
-/* 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. */
+/* Simplify a call to the strpbrk builtin. S1 and S2 are the arguments
+ to the call, and TYPE is its return type.
-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;
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE)
- || !validate_arg (size, INTEGER_TYPE))
- return NULL_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).
- /* 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);
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
- if (! tree_fits_uhwi_p (size))
+static tree
+fold_builtin_strpbrk (location_t loc, tree s1, tree s2, tree type)
+{
+ if (!validate_arg (s1, POINTER_TYPE)
+ || !validate_arg (s2, POINTER_TYPE))
return NULL_TREE;
-
- if (! integer_all_onesp (size))
+ else
{
- 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;
+ tree fn;
+ const char *p1, *p2;
- /* 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;
+ p2 = c_getstr (s2);
+ if (p2 == NULL)
+ return NULL_TREE;
- return build_call_expr_loc (loc, fn, 3, dest, src, size);
- }
+ p1 = c_getstr (s1);
+ if (p1 != NULL)
+ {
+ const char *r = strpbrk (p1, p2);
+ tree tem;
- if (! len || TREE_SIDE_EFFECTS (len))
- return NULL_TREE;
+ if (r == NULL)
+ return build_int_cst (TREE_TYPE (s1), 0);
- /* 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));
- }
+ /* Return an offset into the constant string argument. */
+ tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
+ return fold_convert_loc (loc, type, tem);
}
- else
- maxlen = len;
- if (! tree_int_cst_lt (maxlen, size))
- return NULL_TREE;
- }
+ if (p2[0] == '\0')
+ /* strpbrk(x, "") == NULL.
+ Evaluate and ignore s1 in case it had side-effects. */
+ return omit_one_operand_loc (loc, TREE_TYPE (s1), integer_zero_node, s1);
- /* 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;
+ if (p2[1] != '\0')
+ return NULL_TREE; /* Really call strpbrk. */
+
+ fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+ if (!fn)
+ return NULL_TREE;
- return build_call_expr_loc (loc, fn, 2, dest, src);
+ /* New argument list transforming strpbrk(s1, s2) to
+ strchr(s1, s2[0]). */
+ return build_call_expr_loc (loc, fn, 2, s1,
+ build_int_cst (integer_type_node, p2[0]));
+ }
}
-/* 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. */
+/* Simplify a call to the strspn builtin. S1 and S2 are the arguments
+ to the call.
-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;
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE)
- || !validate_arg (len, INTEGER_TYPE)
- || !validate_arg (size, INTEGER_TYPE))
- return NULL_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).
- 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);
- }
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ form of the builtin function call. */
- if (! tree_fits_uhwi_p (size))
+static tree
+fold_builtin_strspn (location_t loc, tree s1, tree s2)
+{
+ if (!validate_arg (s1, POINTER_TYPE)
+ || !validate_arg (s2, POINTER_TYPE))
return NULL_TREE;
-
- if (! integer_all_onesp (size))
+ else
{
- if (! tree_fits_uhwi_p (len))
+ const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
+
+ /* If both arguments are constants, evaluate at compile-time. */
+ if (p1 && p2)
{
- /* 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;
+ const size_t r = strspn (p1, p2);
+ return build_int_cst (size_type_node, r);
}
- else
- maxlen = len;
- if (tree_int_cst_lt (size, maxlen))
- return NULL_TREE;
+ /* If either argument is "", return NULL_TREE. */
+ if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
+ /* Evaluate and ignore both arguments in case either one has
+ side-effects. */
+ return omit_two_operands_loc (loc, size_type_node, size_zero_node,
+ s1, s2);
+ 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;
+/* Simplify a call to the strcspn builtin. S1 and S2 are the arguments
+ to the call.
- return build_call_expr_loc (loc, fn, 3, dest, src, len);
-}
+ 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).
-/* Fold a call to the __strcat_chk builtin FNDECL. DEST, SRC, and SIZE
- are the arguments to the call. */
+ 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_strcat_chk (location_t loc, tree fndecl, tree dest,
- tree src, tree size)
+fold_builtin_strcspn (location_t loc, tree s1, tree s2)
{
- tree fn;
- const char *p;
-
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE)
- || !validate_arg (size, INTEGER_TYPE))
+ if (!validate_arg (s1, POINTER_TYPE)
+ || !validate_arg (s2, POINTER_TYPE))
return NULL_TREE;
+ else
+ {
+ const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
- 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 both arguments are constants, evaluate at compile-time. */
+ if (p1 && p2)
+ {
+ const size_t r = strcspn (p1, p2);
+ return build_int_cst (size_type_node, r);
+ }
- if (! tree_fits_uhwi_p (size) || ! integer_all_onesp (size))
- return NULL_TREE;
+ /* If the first argument is "", return NULL_TREE. */
+ if (p1 && *p1 == '\0')
+ {
+ /* Evaluate and ignore argument s2 in case it has
+ side-effects. */
+ return omit_one_operand_loc (loc, size_type_node,
+ size_zero_node, s2);
+ }
- /* If __builtin_strcat_chk is used, assume strcat is available. */
- fn = builtin_decl_explicit (BUILT_IN_STRCAT);
- if (!fn)
- return NULL_TREE;
+ /* If the second argument is "", return __builtin_strlen(s1). */
+ if (p2 && *p2 == '\0')
+ {
+ tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+
+ /* 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, dest, src);
+ return build_call_expr_loc (loc, fn, 1, s1);
+ }
+ return NULL_TREE;
+ }
}
-/* Fold a call to the __strncat_chk builtin with arguments DEST, SRC,
- LEN, and SIZE. */
+/* 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. */
-static tree
-fold_builtin_strncat_chk (location_t loc, tree fndecl,
- tree dest, tree src, tree len, tree size)
+bool
+fold_builtin_next_arg (tree exp, bool va_start_p)
{
- tree fn;
- const char *p;
+ tree fntype = TREE_TYPE (current_function_decl);
+ int nargs = call_expr_nargs (exp);
+ tree arg;
+ /* There is good chance the current input_location points inside the
+ definition of the va_start macro (perhaps on the token for
+ builtin) in a system header, so warnings will not be emitted.
+ Use the location in real source code. */
+ source_location current_location =
+ linemap_unwind_to_first_non_reserved_loc (line_table, input_location,
+ NULL);
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE)
- || !validate_arg (size, INTEGER_TYPE)
- || !validate_arg (size, INTEGER_TYPE))
- return NULL_TREE;
+ if (!stdarg_p (fntype))
+ {
+ error ("%<va_start%> used in function with fixed args");
+ return true;
+ }
- 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 (va_start_p)
+ {
+ if (va_start_p && (nargs != 2))
+ {
+ error ("wrong number of arguments to function %<va_start%>");
+ return true;
+ }
+ arg = CALL_EXPR_ARG (exp, 1);
+ }
+ /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0)
+ when we checked the arguments and if needed issued a warning. */
+ else
+ {
+ if (nargs == 0)
+ {
+ /* Evidently an out of date version of <stdarg.h>; can't validate
+ va_start's second argument, but can still work as intended. */
+ warning_at (current_location,
+ OPT_Wvarargs,
+ "%<__builtin_next_arg%> called without an argument");
+ return true;
+ }
+ else if (nargs > 1)
+ {
+ error ("wrong number of arguments to function %<__builtin_next_arg%>");
+ return true;
+ }
+ arg = CALL_EXPR_ARG (exp, 0);
+ }
- if (! tree_fits_uhwi_p (size))
- return NULL_TREE;
+ if (TREE_CODE (arg) == SSA_NAME)
+ arg = SSA_NAME_VAR (arg);
- if (! integer_all_onesp (size))
+ /* We destructively modify the call to be __builtin_va_start (ap, 0)
+ or __builtin_next_arg (0) the first time we see it, after checking
+ the arguments and if needed issuing a warning. */
+ if (!integer_zerop (arg))
{
- 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))
+ tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
+
+ /* Strip off all nops for the sake of the comparison. This
+ is not quite the same as STRIP_NOPS. It does more.
+ We must also strip off INDIRECT_EXPR for C++ reference
+ parameters. */
+ while (CONVERT_EXPR_P (arg)
+ || TREE_CODE (arg) == INDIRECT_REF)
+ arg = TREE_OPERAND (arg, 0);
+ if (arg != last_parm)
+ {
+ /* FIXME: Sometimes with the tree optimizers we can get the
+ not the last argument even though the user used the last
+ argument. We just warn and set the arg to be the last
+ argument so that we will get wrong-code because of
+ it. */
+ warning_at (current_location,
+ OPT_Wvarargs,
+ "second parameter of %<va_start%> not last named argument");
+ }
+
+ /* Undefined by C99 7.15.1.4p4 (va_start):
+ "If the parameter parmN is declared with the register storage
+ class, with a function or array type, or with a type that is
+ not compatible with the type that results after application of
+ the default argument promotions, the behavior is undefined."
+ */
+ else if (DECL_REGISTER (arg))
{
- /* 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);
+ warning_at (current_location,
+ OPT_Wvarargs,
+ "undefined behaviour when second parameter of "
+ "%<va_start%> is declared with %<register%> storage");
}
- 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);
+ /* We want to verify the second parameter just once before the tree
+ optimizers are run and then avoid keeping it in the tree,
+ as otherwise we could warn even for correct code like:
+ void foo (int i, ...)
+ { va_list ap; i++; va_start (ap, i); va_end (ap); } */
+ if (va_start_p)
+ CALL_EXPR_ARG (exp, 1) = integer_zero_node;
+ else
+ CALL_EXPR_ARG (exp, 0) = integer_zero_node;
+ }
+ return false;
}
-/* 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;
+/* Expand a call EXP to __builtin_object_size. */
- if (!init_target_chars ())
- return NULL_TREE;
+static rtx
+expand_builtin_object_size (tree exp)
+{
+ tree ost;
+ int object_size_type;
+ tree fndecl = get_callee_fndecl (exp);
- /* Check whether the format is a literal string constant. */
- fmt_str = c_getstr (fmt);
- if (fmt_str != NULL)
+ if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
{
- /* 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;
- }
- }
- }
+ error ("%Kfirst argument of %D must be a pointer, second integer constant",
+ exp, fndecl);
+ expand_builtin_trap ();
+ return const0_rtx;
}
- if (! integer_all_onesp (size))
- {
- if (! len || ! tree_int_cst_lt (len, size))
- return NULL_TREE;
- }
+ ost = CALL_EXPR_ARG (exp, 1);
+ STRIP_NOPS (ost);
- /* 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 (TREE_CODE (ost) != INTEGER_CST
+ || tree_int_cst_sgn (ost) < 0
+ || compare_tree_int (ost, 3) > 0)
{
- if (fmt_str == NULL)
- return NULL_TREE;
- if (strchr (fmt_str, target_percent) != NULL
- && strcmp (fmt_str, target_percent_s))
- return NULL_TREE;
+ error ("%Klast argument of %D is not integer constant between 0 and 3",
+ exp, fndecl);
+ expand_builtin_trap ();
+ return const0_rtx;
}
- /* 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;
+ object_size_type = tree_to_shwi (ost);
- return rewrite_call_expr_array (loc, nargs, args, 4, fn, 2, dest, fmt);
+ return object_size_type < 2 ? constm1_rtx : const0_rtx;
}
-/* 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. */
+/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin.
+ FCODE is the BUILT_IN_* to use.
+ 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 tree
-fold_builtin_sprintf_chk (location_t loc, tree exp,
- enum built_in_function fcode)
+static rtx
+expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
+ 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. */
+ tree dest, src, len, size;
-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;
+ if (!validate_arglist (exp,
+ POINTER_TYPE,
+ fcode == BUILT_IN_MEMSET_CHK
+ ? INTEGER_TYPE : POINTER_TYPE,
+ INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return NULL_RTX;
- /* 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;
+ dest = CALL_EXPR_ARG (exp, 0);
+ src = CALL_EXPR_ARG (exp, 1);
+ len = CALL_EXPR_ARG (exp, 2);
+ size = CALL_EXPR_ARG (exp, 3);
if (! tree_fits_uhwi_p (size))
- return NULL_TREE;
+ return NULL_RTX;
- if (! integer_all_onesp (size))
+ if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
{
- if (! tree_fits_uhwi_p (len))
+ tree fn;
+
+ if (! integer_all_onesp (size) && tree_int_cst_lt (size, 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;
+ warning_at (tree_nonartificial_location (exp),
+ 0, "%Kcall to %D will always overflow destination buffer",
+ exp, get_callee_fndecl (exp));
+ return NULL_RTX;
}
- 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 (!init_target_chars ())
- return NULL_TREE;
+ if (! fn)
+ return NULL_RTX;
- /* 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;
+ fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 3, dest, src, len);
+ gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+ return expand_expr (fn, target, mode, EXPAND_NORMAL);
}
+ else if (fcode == BUILT_IN_MEMSET_CHK)
+ return NULL_RTX;
+ else
+ {
+ unsigned int dest_align = get_pointer_alignment (dest);
- /* 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;
+ /* If DEST is not a pointer type, call the normal function. */
+ if (dest_align == 0)
+ return NULL_RTX;
- return rewrite_call_expr_array (loc, nargs, args, 5, fn, 3, dest, len, fmt);
-}
+ /* If SRC and DEST are the same (and not volatile), do nothing. */
+ if (operand_equal_p (src, dest, 0))
+ {
+ tree expr;
-/* 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. */
+ if (fcode != BUILT_IN_MEMPCPY_CHK)
+ {
+ /* Evaluate and ignore LEN in case it has side-effects. */
+ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return expand_expr (dest, target, mode, EXPAND_NORMAL);
+ }
-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);
+ expr = fold_build_pointer_plus (dest, len);
+ return expand_expr (expr, target, mode, EXPAND_NORMAL);
+ }
+
+ /* __memmove_chk special case. */
+ if (fcode == BUILT_IN_MEMMOVE_CHK)
+ {
+ unsigned int src_align = get_pointer_alignment (src);
+
+ if (src_align == 0)
+ return NULL_RTX;
+
+ /* If src is categorized for a readonly section we can use
+ normal __memcpy_chk. */
+ if (readonly_data_expr (src))
+ {
+ tree fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+ if (!fn)
+ return NULL_RTX;
+ fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 4,
+ dest, src, len, size);
+ gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+ return expand_expr (fn, target, mode, EXPAND_NORMAL);
+ }
+ }
+ return NULL_RTX;
+ }
}
-/* 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. */
+/* Emit warning if a buffer overflow is detected at compile time. */
-static tree
-fold_builtin_varargs (location_t loc, tree fndecl, tree exp,
- bool ignore ATTRIBUTE_UNUSED)
+static void
+maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
{
- enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
- tree ret = NULL_TREE;
+ int is_strlen = 0;
+ tree len, size;
+ location_t loc = tree_nonartificial_location (exp);
switch (fcode)
{
- case BUILT_IN_SPRINTF_CHK:
- case BUILT_IN_VSPRINTF_CHK:
- ret = fold_builtin_sprintf_chk (loc, exp, fcode);
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STPCPY_CHK:
+ /* For __strcat_chk the warning will be emitted only if overflowing
+ by at least strlen (dest) + 1 bytes. */
+ case BUILT_IN_STRCAT_CHK:
+ len = CALL_EXPR_ARG (exp, 1);
+ size = CALL_EXPR_ARG (exp, 2);
+ is_strlen = 1;
+ break;
+ case BUILT_IN_STRNCAT_CHK:
+ case BUILT_IN_STRNCPY_CHK:
+ case BUILT_IN_STPNCPY_CHK:
+ len = CALL_EXPR_ARG (exp, 2);
+ size = CALL_EXPR_ARG (exp, 3);
break;
-
case BUILT_IN_SNPRINTF_CHK:
case BUILT_IN_VSNPRINTF_CHK:
- ret = fold_builtin_snprintf_chk (loc, exp, NULL_TREE, fcode);
+ len = CALL_EXPR_ARG (exp, 1);
+ size = CALL_EXPR_ARG (exp, 3);
break;
+ default:
+ gcc_unreachable ();
+ }
- case BUILT_IN_FPCLASSIFY:
- ret = fold_builtin_fpclassify (loc, exp);
- break;
+ if (!len || !size)
+ return;
+
+ if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
+ return;
- default:
- break;
+ if (is_strlen)
+ {
+ len = c_strlen (len, 1);
+ if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
+ return;
}
- if (ret)
+ else if (fcode == BUILT_IN_STRNCAT_CHK)
{
- ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
- SET_EXPR_LOCATION (ret, loc);
- TREE_NO_WARNING (ret) = 1;
- return ret;
+ tree src = CALL_EXPR_ARG (exp, 1);
+ if (! src || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
+ return;
+ src = c_strlen (src, 1);
+ if (! src || ! tree_fits_uhwi_p (src))
+ {
+ warning_at (loc, 0, "%Kcall to %D might overflow destination buffer",
+ exp, get_callee_fndecl (exp));
+ return;
+ }
+ else if (tree_int_cst_lt (src, size))
+ return;
}
- return NULL_TREE;
-}
+ else if (! tree_fits_uhwi_p (len) || ! tree_int_cst_lt (size, len))
+ return;
-/* 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.
+ warning_at (loc, 0, "%Kcall to %D will always overflow destination buffer",
+ exp, get_callee_fndecl (exp));
+}
- 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. */
+/* Emit warning if a buffer overflow is detected at compile time
+ in __sprintf_chk/__vsprintf_chk calls. */
-static tree
-fold_builtin_printf (location_t loc, tree fndecl, tree fmt,
- tree arg, bool ignore,
- enum built_in_function fcode)
+static void
+maybe_emit_sprintf_chk_warning (tree exp, 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;
+ tree size, len, fmt;
+ const char *fmt_str;
+ int nargs = call_expr_nargs (exp);
/* Verify the required arguments in the original call. */
- if (!validate_arg (fmt, POINTER_TYPE))
- return NULL_TREE;
+
+ if (nargs < 4)
+ return;
+ size = CALL_EXPR_ARG (exp, 2);
+ fmt = CALL_EXPR_ARG (exp, 3);
+
+ if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
+ return;
/* 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);
- }
+ return;
if (!init_target_chars ())
- return NULL_TREE;
+ return;
- if (strcmp (fmt_str, target_percent_s) == 0
- || strchr (fmt_str, target_percent) == NULL)
+ /* If the format doesn't contain % args or %%, we know its size. */
+ if (strchr (fmt_str, target_percent) == 0)
+ len = build_int_cstu (size_type_node, strlen (fmt_str));
+ /* If the format is "%s" and first ... argument is a string literal,
+ we know it too. */
+ else if (fcode == BUILT_IN_SPRINTF_CHK
+ && strcmp (fmt_str, target_percent_s) == 0)
{
- 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;
- }
+ tree arg;
- /* If the string was "", printf does nothing. */
- if (str[0] == '\0')
- return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0);
+ if (nargs < 5)
+ return;
+ arg = CALL_EXPR_ARG (exp, 4);
+ if (! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return;
- /* 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;
- }
+ len = c_strlen (arg, 1);
+ if (!len || ! tree_fits_uhwi_p (len))
+ return;
}
+ else
+ return;
- /* 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 (! tree_int_cst_lt (len, size))
+ warning_at (tree_nonartificial_location (exp),
+ 0, "%Kcall to %D will always overflow destination buffer",
+ exp, get_callee_fndecl (exp));
+}
- /* 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);
- }
+/* Emit warning if a free is called with address of a variable. */
- /* 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);
- }
+static void
+maybe_emit_free_warning (tree exp)
+{
+ tree arg = CALL_EXPR_ARG (exp, 0);
- if (!call)
- return NULL_TREE;
+ STRIP_NOPS (arg);
+ if (TREE_CODE (arg) != ADDR_EXPR)
+ return;
- return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), call);
-}
+ arg = get_base_address (TREE_OPERAND (arg, 0));
+ if (arg == NULL || INDIRECT_REF_P (arg) || TREE_CODE (arg) == MEM_REF)
+ return;
-/* 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.
+ if (SSA_VAR_P (arg))
+ warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object,
+ "%Kattempt to free a non-heap object %qD", exp, arg);
+ else
+ warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object,
+ "%Kattempt to free a non-heap object", exp);
+}
- 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. */
+/* Fold a call to __builtin_object_size with arguments PTR and OST,
+ if possible. */
static tree
-fold_builtin_fprintf (location_t loc, tree fndecl, tree fp,
- tree fmt, tree arg, bool ignore,
- enum built_in_function fcode)
+fold_builtin_object_size (tree ptr, tree ost)
{
- tree fn_fputc, fn_fputs, call = NULL_TREE;
- const char *fmt_str = NULL;
+ unsigned HOST_WIDE_INT bytes;
+ int object_size_type;
- /* If the return value is used, don't do the transformation. */
- if (! ignore)
+ if (!validate_arg (ptr, POINTER_TYPE)
+ || !validate_arg (ost, INTEGER_TYPE))
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;
+ STRIP_NOPS (ost);
- /* Check whether the format is a literal string constant. */
- fmt_str = c_getstr (fmt);
- if (fmt_str == NULL)
+ if (TREE_CODE (ost) != INTEGER_CST
+ || tree_int_cst_sgn (ost) < 0
+ || compare_tree_int (ost, 3) > 0)
return NULL_TREE;
- if (fcode == BUILT_IN_FPRINTF_UNLOCKED)
+ object_size_type = tree_to_shwi (ost);
+
+ /* __builtin_object_size doesn't evaluate side-effects in its arguments;
+ if there are any side-effects, it returns (size_t) -1 for types 0 and 1
+ and (size_t) 0 for types 2 and 3. */
+ if (TREE_SIDE_EFFECTS (ptr))
+ return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0);
+
+ if (TREE_CODE (ptr) == ADDR_EXPR)
{
- /* 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);
+ bytes = compute_builtin_object_size (ptr, object_size_type);
+ if (wi::fits_to_tree_p (bytes, size_type_node))
+ return build_int_cstu (size_type_node, bytes);
}
- else
+ else if (TREE_CODE (ptr) == SSA_NAME)
{
- fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC);
- fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS);
+ /* If object size is not known yet, delay folding until
+ later. Maybe subsequent passes will help determining
+ it. */
+ bytes = compute_builtin_object_size (ptr, object_size_type);
+ if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 ? -1 : 0)
+ && wi::fits_to_tree_p (bytes, size_type_node))
+ return build_int_cstu (size_type_node, bytes);
}
- 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);
- }
+ return NULL_TREE;
+}
- /* 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);
- }
+/* 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. */
- /* 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;
+static tree
+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;
- /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */
- else if (strcmp (fmt_str, target_percent_s) == 0)
+ switch (fcode)
{
- if (!arg || !validate_arg (arg, POINTER_TYPE))
- return NULL_TREE;
- if (fn_fputs)
- call = build_call_expr_loc (loc, fn_fputs, 2, arg, fp);
- }
+ case BUILT_IN_FPCLASSIFY:
+ ret = fold_builtin_fpclassify (loc, args, nargs);
+ break;
- /* If the format specifier was "%c", call __builtin_fputc (arg, fp). */
- else if (strcmp (fmt_str, target_percent_c) == 0)
+ default:
+ break;
+ }
+ if (ret)
{
- if (!arg || !validate_arg (arg, INTEGER_TYPE))
- return NULL_TREE;
- if (fn_fputc)
- call = build_call_expr_loc (loc, fn_fputc, 2, arg, fp);
+ ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
+ SET_EXPR_LOCATION (ret, loc);
+ TREE_NO_WARNING (ret) = 1;
+ return ret;
}
-
- if (!call)
- return NULL_TREE;
- return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), call);
+ return NULL_TREE;
}
/* Initialize format string characters in the target charset. */
-static bool
+bool
init_target_chars (void)
{
static bool init;
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);
}
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
case BUILT_IN_LABS:
case BUILT_IN_LLABS:
case BUILT_IN_PREFETCH:
+ case BUILT_IN_ACC_ON_DEVICE:
return true;
default: