/* Expand builtin functions.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "real.h"
#include "rtl.h"
#include "tree.h"
-#include "tree-gimple.h"
+#include "gimple.h"
#include "flags.h"
#include "regs.h"
#include "hard-reg-set.h"
static rtx expand_builtin_interclass_mathfn (tree, rtx, rtx);
static rtx expand_builtin_sincos (tree);
static rtx expand_builtin_cexpi (tree, rtx, rtx);
-static rtx expand_builtin_int_roundingfn (tree, rtx, rtx);
-static rtx expand_builtin_int_roundingfn_2 (tree, rtx, rtx);
+static rtx expand_builtin_int_roundingfn (tree, rtx);
+static rtx expand_builtin_int_roundingfn_2 (tree, rtx);
static rtx expand_builtin_args_info (tree);
static rtx expand_builtin_next_arg (void);
static rtx expand_builtin_va_start (tree);
static rtx expand_builtin_strcpy (tree, tree, rtx, enum machine_mode);
static rtx expand_builtin_strcpy_args (tree, tree, tree, rtx, enum machine_mode);
static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode);
-static rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode);
static rtx expand_builtin_strncpy (tree, rtx, enum machine_mode);
static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode);
static rtx expand_builtin_memset (tree, rtx, enum machine_mode);
static tree stabilize_va_list (tree, int);
static rtx expand_builtin_expect (tree, rtx);
static tree fold_builtin_constant_p (tree);
-static tree fold_builtin_expect (tree);
+static tree fold_builtin_expect (tree, tree);
static tree fold_builtin_classify_type (tree);
static tree fold_builtin_strlen (tree);
static tree fold_builtin_inf (tree, int);
static tree fold_builtin_nan (tree, tree, int);
static tree rewrite_call_expr (tree, int, tree, int, ...);
-static bool validate_arg (tree, enum tree_code code);
+static bool validate_arg (const_tree, enum tree_code code);
static bool integer_valued_real_p (tree);
static tree fold_trunc_transparent_mathfn (tree, tree);
static bool readonly_data_expr (tree);
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 (tree, tree, tree, tree);
static tree fold_builtin_strncat_chk (tree, tree, tree, tree, tree);
static tree do_mpfr_lgamma_r (tree, tree, tree);
#endif
-/* This array records the insn_code of insns to imlement the signbit
- function. */
-enum insn_code signbit_optab[NUM_MACHINE_MODES];
-
-
/* 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". */
{
switch (TREE_CODE (exp))
{
- case NOP_EXPR:
- case CONVERT_EXPR:
- case NON_LVALUE_EXPR:
+ CASE_CONVERT:
exp = TREE_OPERAND (exp, 0);
if (! POINTER_TYPE_P (TREE_TYPE (exp)))
return align;
runtime. */
if (offset < 0 || offset > max)
{
- warning (0, "offset outside bounds of constant string");
+ /* Suppress multiple warnings for propagated constant strings. */
+ if (! TREE_NO_WARNING (src))
+ {
+ warning (0, "offset outside bounds of constant string");
+ TREE_NO_WARNING (src) = 1;
+ }
return NULL_TREE;
}
tem = hard_frame_pointer_rtx;
/* Tell reload not to eliminate the frame pointer. */
- current_function_accesses_prior_frames = 1;
+ crtl->accesses_prior_frames = 1;
}
#endif
}
/* Alias set used for setjmp buffer. */
-static HOST_WIDE_INT setjmp_alias_set = -1;
+static alias_set_type setjmp_alias_set = -1;
/* Construct the leading half of a __builtin_setjmp call. Control will
return to RECEIVER_LABEL. This is also called directly by the SJLJ
/* Tell optimize_save_area_alloca that extra work is going to
need to go on during alloca. */
- current_function_calls_setjmp = 1;
+ cfun->calls_setjmp = 1;
/* We have a nonlocal label. */
- current_function_has_nonlocal_label = 1;
+ cfun->has_nonlocal_label = 1;
}
/* Construct the trailing part of a __builtin_setjmp call. This is
{
/* Clobber the FP when we get here, so we have to make sure it's
marked as used by this function. */
- emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
+ emit_use (hard_frame_pointer_rtx);
/* Mark the static chain as clobbered here so life information
doesn't get messed up for it. */
- emit_insn (gen_rtx_CLOBBER (VOIDmode, static_chain_rtx));
+ emit_clobber (static_chain_rtx);
/* Now put in the code to restore the frame pointer, and argument
pointer, if needed. */
emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
/* This might change the hard frame pointer in ways that aren't
apparent to early optimization passes, so force a clobber. */
- emit_insn (gen_rtx_CLOBBER (VOIDmode, hard_frame_pointer_rtx));
+ emit_clobber (hard_frame_pointer_rtx);
}
#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
{
/* Now restore our arg pointer from the address at which it
was saved in our stack frame. */
- emit_move_insn (virtual_incoming_args_rtx,
- copy_to_reg (get_arg_pointer_save_area (cfun)));
+ emit_move_insn (crtl->args.internal_arg_pointer,
+ copy_to_reg (get_arg_pointer_save_area ()));
}
}
#endif
rtx fp, lab, stack, insn, last;
enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
+ /* DRAP is needed for stack realign if longjmp is expanded to current
+ function */
+ if (SUPPORTS_STACK_ALIGNMENT)
+ crtl->need_drap = true;
+
if (setjmp_alias_set == -1)
setjmp_alias_set = new_alias_set ();
{
lab = copy_to_reg (lab);
- emit_insn (gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_MEM (BLKmode,
- gen_rtx_SCRATCH (VOIDmode))));
- emit_insn (gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_MEM (BLKmode,
- hard_frame_pointer_rtx)));
+ emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)));
+ emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx));
emit_move_insn (hard_frame_pointer_rtx, fp);
emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX);
- emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
- emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx));
+ emit_use (hard_frame_pointer_rtx);
+ emit_use (stack_pointer_rtx);
emit_indirect_jump (lab);
}
}
if (JUMP_P (insn))
{
- REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO, const0_rtx,
- REG_NOTES (insn));
+ add_reg_note (insn, REG_NON_LOCAL_GOTO, const0_rtx);
break;
}
else if (CALL_P (insn))
r_label = convert_memory_address (Pmode, r_label);
r_save_area = expand_normal (t_save_area);
r_save_area = convert_memory_address (Pmode, r_save_area);
+ /* Copy the address of the save location to a register just in case it was based
+ on the frame pointer. */
+ r_save_area = copy_to_reg (r_save_area);
r_fp = gen_rtx_MEM (Pmode, r_save_area);
r_sp = gen_rtx_MEM (STACK_SAVEAREA_MODE (SAVE_NONLOCAL),
plus_constant (r_save_area, GET_MODE_SIZE (Pmode)));
- current_function_has_nonlocal_goto = 1;
+ crtl->has_nonlocal_goto = 1;
#ifdef HAVE_nonlocal_goto
/* ??? We no longer need to pass the static chain value, afaik. */
{
r_label = copy_to_reg (r_label);
- emit_insn (gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_MEM (BLKmode,
- gen_rtx_SCRATCH (VOIDmode))));
-
- emit_insn (gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_MEM (BLKmode,
- hard_frame_pointer_rtx)));
+ emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)));
+ emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx));
/* Restore frame pointer for containing function.
This sets the actual hard register used for the frame pointer
/* USE of hard_frame_pointer_rtx added for consistency;
not clear if really needed. */
- emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
- emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx));
+ emit_use (hard_frame_pointer_rtx);
+ emit_use (stack_pointer_rtx);
+
+ /* If the architecture is using a GP register, we must
+ conservatively assume that the target function makes use of it.
+ The prologue of functions with nonlocal gotos must therefore
+ initialize the GP register to the appropriate value, and we
+ must then make sure that this value is live at the point
+ of the jump. (Note that this doesn't necessarily apply
+ to targets with a nonlocal_goto pattern; they are free
+ to implement it in their own way. Note also that this is
+ a no-op if the GP register is a global invariant.) */
+ if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
+ && fixed_regs[PIC_OFFSET_TABLE_REGNUM])
+ emit_use (pic_offset_table_rtx);
+
emit_indirect_jump (r_label);
}
{
if (JUMP_P (insn))
{
- REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO,
- const0_rtx, REG_NOTES (insn));
+ add_reg_note (insn, REG_NON_LOCAL_GOTO, const0_rtx);
break;
}
else if (CALL_P (insn))
/* Get an expression we can use to find the attributes to assign to MEM.
If it is an ADDR_EXPR, use the operand. Otherwise, dereference it if
we can. First remove any nops. */
- while ((TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR
- || TREE_CODE (exp) == NON_LVALUE_EXPR)
+ while (CONVERT_EXPR_P (exp)
&& POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0))))
exp = TREE_OPERAND (exp, 0);
tree inner = exp;
while (TREE_CODE (inner) == ARRAY_REF
- || TREE_CODE (inner) == NOP_EXPR
- || TREE_CODE (inner) == CONVERT_EXPR
- || TREE_CODE (inner) == NON_LVALUE_EXPR
+ || CONVERT_EXPR_P (inner)
|| TREE_CODE (inner) == VIEW_CONVERT_EXPR
|| TREE_CODE (inner) == SAVE_EXPR)
inner = TREE_OPERAND (inner, 0);
while (TREE_CODE (inner) == COMPONENT_REF)
{
tree field = TREE_OPERAND (inner, 1);
- gcc_assert (! DECL_BIT_FIELD (field));
gcc_assert (TREE_CODE (mem_expr) == COMPONENT_REF);
gcc_assert (field == TREE_OPERAND (mem_expr, 1));
+ /* Bitfields are generally not byte-addressable. */
+ gcc_assert (!DECL_BIT_FIELD (field)
+ || ((tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1)
+ % BITS_PER_UNIT) == 0
+ && host_integerp (DECL_SIZE (field), 0)
+ && (TREE_INT_CST_LOW (DECL_SIZE (field))
+ % BITS_PER_UNIT) == 0));
+
+ /* If we can prove that the memory starting at XEXP (mem, 0) and
+ ending at XEXP (mem, 0) + LENGTH will fit into this field, we
+ can keep the COMPONENT_REF in MEM_EXPR. But be careful with
+ fields without DECL_SIZE_UNIT like flexible array members. */
if (length >= 0
- && TYPE_SIZE_UNIT (TREE_TYPE (inner))
- && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0))
+ && DECL_SIZE_UNIT (field)
+ && host_integerp (DECL_SIZE_UNIT (field), 0))
{
HOST_WIDE_INT size
- = tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0);
- /* If we can prove the memory starting at XEXP (mem, 0)
- and ending at XEXP (mem, 0) + LENGTH will fit into
- this field, we can keep that COMPONENT_REF in MEM_EXPR. */
+ = TREE_INT_CST_LOW (DECL_SIZE_UNIT (field));
if (offset <= size
&& length <= size
&& offset + length <= size)
if (offset >= 0
&& host_integerp (DECL_FIELD_OFFSET (field), 0))
- offset += tree_low_cst (DECL_FIELD_OFFSET (field), 0)
+ offset += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field))
+ tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1)
/ BITS_PER_UNIT;
else
int regno, size, align, nelts;
enum machine_mode mode;
rtx reg, mem;
- rtx *savevec = alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
+ rtx *savevec = XALLOCAVEC (rtx, FIRST_PSEUDO_REGISTER);
size = nelts = 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
}
/* Save the arg pointer to the block. */
- tem = copy_to_reg (virtual_incoming_args_rtx);
+ 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 (tem, current_function_pretend_args_size),
+ = force_operand (plus_constant (tem, crtl->args.pretend_args_size),
NULL_RTX);
#endif
emit_move_insn (adjust_address (registers, Pmode, 0), tem);
/* Allocate a block of memory onto the stack and copy the memory
arguments to the outgoing arguments address. */
allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT);
+
+ /* Set DRAP flag to true, even though allocate_dynamic_stack_space
+ may have already set current_function_calls_alloca to true.
+ current_function_calls_alloca won't be set if argsize is zero,
+ so we have to guarantee need_drap is true here. */
+ if (SUPPORTS_STACK_ALIGNMENT)
+ crtl->need_drap = true;
+
dest = virtual_outgoing_args_rtx;
#ifndef STACK_GROWS_DOWNWARD
if (GET_CODE (argsize) == CONST_INT)
emit_move_insn (reg, adjust_address (result, mode, size));
push_to_sequence (call_fusage);
- emit_insn (gen_rtx_USE (VOIDmode, reg));
+ emit_use (reg);
call_fusage = get_insns ();
end_sequence ();
size += GET_MODE_SIZE (mode);
fcodel = BUILT_IN_MATHFN##L_R ; break;
/* Return mathematic function equivalent to FN but operating directly
- on TYPE, if available. If we can't do the conversion, return zero. */
-tree
-mathfn_built_in (tree type, enum built_in_function fn)
+ on TYPE, if available. If IMPLICIT is true find the function in
+ implicit_built_in_decls[], otherwise use built_in_decls[]. If we
+ can't do the conversion, return zero. */
+
+static tree
+mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit)
{
+ tree const *const fn_arr
+ = implicit ? implicit_built_in_decls : built_in_decls;
enum built_in_function fcode, fcodef, fcodel;
switch (fn)
CASE_MATHFN (BUILT_IN_SCALB)
CASE_MATHFN (BUILT_IN_SCALBLN)
CASE_MATHFN (BUILT_IN_SCALBN)
+ CASE_MATHFN (BUILT_IN_SIGNBIT)
CASE_MATHFN (BUILT_IN_SIGNIFICAND)
CASE_MATHFN (BUILT_IN_SIN)
CASE_MATHFN (BUILT_IN_SINCOS)
}
if (TYPE_MAIN_VARIANT (type) == double_type_node)
- return implicit_built_in_decls[fcode];
+ return fn_arr[fcode];
else if (TYPE_MAIN_VARIANT (type) == float_type_node)
- return implicit_built_in_decls[fcodef];
+ return fn_arr[fcodef];
else if (TYPE_MAIN_VARIANT (type) == long_double_type_node)
- return implicit_built_in_decls[fcodel];
+ return fn_arr[fcodel];
else
return NULL_TREE;
}
+/* Like mathfn_built_in_1(), but always use the implicit array. */
+
+tree
+mathfn_built_in (tree type, enum built_in_function fn)
+{
+ return mathfn_built_in_1 (type, fn, /*implicit=*/ 1);
+}
+
/* If errno must be maintained, expand the RTL to check if the result,
TARGET, of a built-in function call, EXP, is NaN, and if so set
errno to EDOM. */
}
#endif
+ /* Make sure the library call isn't expanded as a tail call. */
+ CALL_EXPR_TAILCALL (exp) = 0;
+
/* We can't set errno=EDOM directly; let the library call do it.
Pop the arguments right away in case the call gets deleted. */
NO_DEFER_POP;
tree fndecl = get_callee_fndecl (exp);
enum machine_mode mode;
bool errno_set = false;
- tree arg, narg;
+ tree arg;
if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
return NULL_RTX;
errno_set = false;
/* Before working hard, check whether the instruction is available. */
- if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ if (optab_handler (builtin_optab, mode)->insn_code != CODE_FOR_nothing)
{
target = gen_reg_rtx (mode);
/* Wrap the computation of the argument in a SAVE_EXPR, as we may
need to expand the argument again. This way, we will not perform
side-effects more the once. */
- narg = builtin_save_expr (arg);
- if (narg != arg)
- {
- arg = narg;
- exp = build_call_expr (fndecl, 1, arg);
- }
+ CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
before_call = get_last_insn ();
- target = expand_call (exp, target, target == const0_rtx);
-
- /* If this is a sqrt operation and we don't care about errno, try to
- attach a REG_EQUAL note with a SQRT rtx to the emitted libcall.
- This allows the semantics of the libcall to be visible to the RTL
- optimizers. */
- if (builtin_optab == sqrt_optab && !errno_set)
- {
- /* Search backwards through the insns emitted by expand_call looking
- for the instruction with the REG_RETVAL note. */
- rtx last = get_last_insn ();
- while (last != before_call)
- {
- if (find_reg_note (last, REG_RETVAL, NULL))
- {
- rtx note = find_reg_note (last, REG_EQUAL, NULL);
- /* Check that the REQ_EQUAL note is an EXPR_LIST with
- two elements, i.e. symbol_ref(sqrt) and the operand. */
- if (note
- && GET_CODE (note) == EXPR_LIST
- && GET_CODE (XEXP (note, 0)) == EXPR_LIST
- && XEXP (XEXP (note, 0), 1) != NULL_RTX
- && XEXP (XEXP (XEXP (note, 0), 1), 1) == NULL_RTX)
- {
- rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0);
- /* Check operand is a register with expected mode. */
- if (operand
- && REG_P (operand)
- && GET_MODE (operand) == mode)
- {
- /* Replace the REG_EQUAL note with a SQRT rtx. */
- rtx equiv = gen_rtx_SQRT (mode, operand);
- set_unique_reg_note (last, REG_EQUAL, equiv);
- }
- }
- break;
- }
- last = PREV_INSN (last);
- }
- }
-
- return target;
+ return expand_call (exp, target, target == const0_rtx);
}
/* Expand a call to the builtin binary math functions (pow and atan2).
rtx op0, op1, insns;
int op1_type = REAL_TYPE;
tree fndecl = get_callee_fndecl (exp);
- tree arg0, arg1, narg;
+ tree arg0, arg1;
enum machine_mode mode;
bool errno_set = true;
- bool stable = true;
switch (DECL_FUNCTION_CODE (fndecl))
{
mode = TYPE_MODE (TREE_TYPE (exp));
/* Before working hard, check whether the instruction is available. */
- if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+ if (optab_handler (builtin_optab, mode)->insn_code == CODE_FOR_nothing)
return NULL_RTX;
target = gen_reg_rtx (mode);
errno_set = false;
/* Always stabilize the argument list. */
- narg = builtin_save_expr (arg1);
- if (narg != arg1)
- {
- arg1 = narg;
- stable = false;
- }
- narg = builtin_save_expr (arg0);
- if (narg != arg0)
- {
- arg0 = narg;
- stable = false;
- }
-
- if (! stable)
- exp = build_call_expr (fndecl, 2, arg0, arg1);
+ CALL_EXPR_ARG (exp, 0) = arg0 = builtin_save_expr (arg0);
+ CALL_EXPR_ARG (exp, 1) = arg1 = builtin_save_expr (arg1);
op0 = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL);
op1 = expand_normal (arg1);
rtx op0, insns;
tree fndecl = get_callee_fndecl (exp);
enum machine_mode mode;
- tree arg, narg;
+ tree arg;
if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
return NULL_RTX;
/* Check if sincos insn is available, otherwise fallback
to sin or cos insn. */
- if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+ if (optab_handler (builtin_optab, mode)->insn_code == CODE_FOR_nothing)
switch (DECL_FUNCTION_CODE (fndecl))
{
CASE_FLT_FN (BUILT_IN_SIN):
}
/* Before working hard, check whether the instruction is available. */
- if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ if (optab_handler (builtin_optab, mode)->insn_code != CODE_FOR_nothing)
{
target = gen_reg_rtx (mode);
/* Wrap the computation of the argument in a SAVE_EXPR, as we may
need to expand the argument again. This way, we will not perform
side-effects more the once. */
- narg = save_expr (arg);
- if (narg != arg)
- {
- arg = narg;
- exp = build_call_expr (fndecl, 1, arg);
- }
+ CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
tree fndecl = get_callee_fndecl (exp);
enum machine_mode mode;
bool errno_set = false;
- tree arg, narg;
+ tree arg;
if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
return NULL_RTX;
mode = TYPE_MODE (TREE_TYPE (arg));
if (builtin_optab)
- icode = builtin_optab->handlers[(int) mode].insn_code;
+ icode = optab_handler (builtin_optab, mode)->insn_code;
/* Before working hard, check whether the instruction is available. */
if (icode != CODE_FOR_nothing)
/* Wrap the computation of the argument in a SAVE_EXPR, as we may
need to expand the argument again. This way, we will not perform
side-effects more the once. */
- narg = builtin_save_expr (arg);
- if (narg != arg)
- {
- arg = narg;
- exp = build_call_expr (fndecl, 1, arg);
- }
+ CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
mode = TYPE_MODE (TREE_TYPE (arg));
/* Check if sincos insn is available, otherwise emit the call. */
- if (sincos_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+ if (optab_handler (sincos_optab, mode)->insn_code == CODE_FOR_nothing)
return NULL_RTX;
target1 = gen_reg_rtx (mode);
/* Try expanding via a sincos optab, fall back to emitting a libcall
to sincos or cexp. We are sure we have sincos or cexp because cexpi
is only generated from sincos, cexp or if we have either of them. */
- if (sincos_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ if (optab_handler (sincos_optab, mode)->insn_code != CODE_FOR_nothing)
{
op1 = gen_reg_rtx (mode);
op2 = gen_reg_rtx (mode);
do not need to worry about setting errno to EDOM.
If expanding via optab fails, lower expression to (int)(floor(x)).
EXP is the expression that is a call to the builtin function;
- if convenient, the result should be placed in TARGET. SUBTARGET may
- be used as the target for computing one of EXP's operands. */
+ if convenient, the result should be placed in TARGET. */
static rtx
-expand_builtin_int_roundingfn (tree exp, rtx target, rtx subtarget)
+expand_builtin_int_roundingfn (tree exp, rtx target)
{
convert_optab builtin_optab;
rtx op0, insns, tmp;
enum built_in_function fallback_fn;
tree fallback_fndecl;
enum machine_mode mode;
- tree arg, narg;
+ tree arg;
if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
gcc_unreachable ();
/* Wrap the computation of the argument in a SAVE_EXPR, as we may
need to expand the argument again. This way, we will not perform
side-effects more the once. */
- narg = builtin_save_expr (arg);
- if (narg != arg)
- {
- arg = narg;
- exp = build_call_expr (fndecl, 1, arg);
- }
+ CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
- op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
+ op0 = expand_expr (arg, NULL, VOIDmode, EXPAND_NORMAL);
start_sequence ();
conversion (lrint).
Return 0 if a normal call should be emitted rather than expanding the
function in-line. EXP is the expression that is a call to the builtin
- function; if convenient, the result should be placed in TARGET.
- SUBTARGET may be used as the target for computing one of EXP's operands. */
+ function; if convenient, the result should be placed in TARGET. */
static rtx
-expand_builtin_int_roundingfn_2 (tree exp, rtx target, rtx subtarget)
+expand_builtin_int_roundingfn_2 (tree exp, rtx target)
{
convert_optab builtin_optab;
rtx op0, insns;
tree fndecl = get_callee_fndecl (exp);
- tree arg, narg;
+ tree arg;
enum machine_mode mode;
/* There's no easy way to detect the case we need to set EDOM. */
/* Wrap the computation of the argument in a SAVE_EXPR, as we may
need to expand the argument again. This way, we will not perform
side-effects more the once. */
- narg = builtin_save_expr (arg);
- if (narg != arg)
- {
- arg = narg;
- exp = build_call_expr (fndecl, 1, arg);
- }
+ CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
- op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
+ op0 = expand_expr (arg, NULL, VOIDmode, EXPAND_NORMAL);
start_sequence ();
if (real_identical (&c, &cint)
&& ((n >= -1 && n <= 2)
|| (flag_unsafe_math_optimizations
- && !optimize_size
+ && optimize_insn_for_speed_p ()
&& powi_cost (n) <= POWI_MAX_MULTS)))
{
op = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL);
real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
if (real_identical (&c2, &cint)
&& ((flag_unsafe_math_optimizations
- && !optimize_size
+ && optimize_insn_for_speed_p ()
&& powi_cost (n/2) <= POWI_MAX_MULTS)
|| n == 1))
{
tree call_expr = build_call_expr (fn, 1, narg0);
- op = expand_builtin (call_expr, NULL_RTX, subtarget, mode, 0);
+ /* Use expand_expr in case the newly built call expression
+ was folded to a non-call. */
+ op = expand_expr (call_expr, subtarget, mode, EXPAND_NORMAL);
if (n != 1)
{
op2 = expand_expr (narg0, subtarget, VOIDmode, EXPAND_NORMAL);
&& (tree_expr_nonnegative_p (arg0)
|| !HONOR_NANS (mode)))
{
+ REAL_VALUE_TYPE dconst3;
+ real_from_integer (&dconst3, VOIDmode, 3, 0, 0);
real_arithmetic (&c2, MULT_EXPR, &c, &dconst3);
real_round (&c2, mode, &c2);
n = real_to_integer (&c2);
real_arithmetic (&c2, RDIV_EXPR, &cint, &dconst3);
real_convert (&c2, mode, &c2);
if (real_identical (&c2, &c)
- && ((!optimize_size
+ && ((optimize_insn_for_speed_p ()
&& powi_cost (n/3) <= POWI_MAX_MULTS)
|| n == 1))
{
if ((TREE_INT_CST_HIGH (arg1) == 0
|| TREE_INT_CST_HIGH (arg1) == -1)
&& ((n >= -1 && n <= 2)
- || (! optimize_size
+ || (optimize_insn_for_speed_p ()
&& powi_cost (n) <= POWI_MAX_MULTS)))
{
op0 = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL);
if (GET_MODE (op1) != mode2)
op1 = convert_to_mode (mode2, op1, 0);
- target = emit_library_call_value (powi_optab->handlers[(int) mode].libfunc,
- target, LCT_CONST_MAKE_BLOCK, mode, 2,
+ target = emit_library_call_value (optab_libfunc (powi_optab, mode),
+ target, LCT_CONST, mode, 2,
op0, mode, op1, mode2);
return target;
/* Bail out if we can't compute strlen in the right mode. */
while (insn_mode != VOIDmode)
{
- icode = strlen_optab->handlers[(int) insn_mode].insn_code;
+ icode = optab_handler (strlen_optab, insn_mode)->insn_code;
if (icode != CODE_FOR_nothing)
break;
false, /*endp=*/0);
HOST_WIDE_INT expected_size = -1;
unsigned int expected_align = 0;
+ tree_ann_common_t ann;
if (result)
{
if (src_align == 0)
return NULL_RTX;
- stringop_block_profile (exp, &expected_align, &expected_size);
+ ann = tree_common_ann (exp);
+ if (ann)
+ stringop_block_profile (ann->stmt, &expected_align, &expected_size);
+
if (expected_align < dest_align)
expected_align = dest_align;
dest_mem = get_memory_rtx (dest, len);
&& GET_CODE (len_rtx) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
&& can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
- (void *) src_str, dest_align))
+ CONST_CAST (char *, src_str),
+ dest_align, false))
{
dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
builtin_memcpy_read_str,
- (void *) src_str, dest_align, 0);
+ CONST_CAST (char *, src_str),
+ dest_align, false, 0);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
&& GET_CODE (len_rtx) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
&& can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
- (void *) src_str, dest_align))
+ CONST_CAST (char *, src_str),
+ dest_align, false))
{
dest_mem = get_memory_rtx (dest, len);
set_mem_align (dest_mem, dest_align);
dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
builtin_memcpy_read_str,
- (void *) src_str, dest_align, endp);
+ CONST_CAST (char *, src_str),
+ dest_align, false, endp);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
bytes from constant string DATA + OFFSET and return it as target
constant. */
-static rtx
+rtx
builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
enum machine_mode mode)
{
if (!p || dest_align == 0 || !host_integerp (len, 1)
|| !can_store_by_pieces (tree_low_cst (len, 1),
builtin_strncpy_read_str,
- (void *) p, dest_align))
+ CONST_CAST (char *, p),
+ dest_align, false))
return NULL_RTX;
dest_mem = get_memory_rtx (dest, len);
store_by_pieces (dest_mem, tree_low_cst (len, 1),
builtin_strncpy_read_str,
- (void *) p, dest_align, 0);
+ CONST_CAST (char *, p), dest_align, false, 0);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
enum machine_mode mode)
{
const char *c = (const char *) data;
- char *p = alloca (GET_MODE_SIZE (mode));
+ char *p = XALLOCAVEC (char, GET_MODE_SIZE (mode));
memset (p, *c, GET_MODE_SIZE (mode));
if (size == 1)
return (rtx) data;
- p = alloca (size);
+ p = XALLOCAVEC (char, size);
memset (p, 1, size);
coeff = c_readstr (p, mode);
rtx dest_mem, dest_addr, len_rtx;
HOST_WIDE_INT expected_size = -1;
unsigned int expected_align = 0;
+ tree_ann_common_t ann;
dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
if (dest_align == 0)
return NULL_RTX;
- stringop_block_profile (orig_exp, &expected_align, &expected_size);
+ ann = tree_common_ann (orig_exp);
+ if (ann)
+ stringop_block_profile (ann->stmt, &expected_align, &expected_size);
+
if (expected_align < dest_align)
expected_align = dest_align;
* We can't pass builtin_memset_gen_str as that emits RTL. */
c = 1;
if (host_integerp (len, 1)
- && !(optimize_size && tree_low_cst (len, 1) > 1)
&& can_store_by_pieces (tree_low_cst (len, 1),
- builtin_memset_read_str, &c, dest_align))
+ builtin_memset_read_str, &c, dest_align,
+ true))
{
val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node),
val_rtx);
store_by_pieces (dest_mem, tree_low_cst (len, 1),
- builtin_memset_gen_str, val_rtx, dest_align, 0);
+ builtin_memset_gen_str, val_rtx, dest_align,
+ true, 0);
}
else if (!set_storage_via_setmem (dest_mem, len_rtx, val_rtx,
dest_align, expected_align,
if (c)
{
if (host_integerp (len, 1)
- && !(optimize_size && tree_low_cst (len, 1) > 1)
&& can_store_by_pieces (tree_low_cst (len, 1),
- builtin_memset_read_str, &c, dest_align))
+ builtin_memset_read_str, &c, dest_align,
+ true))
store_by_pieces (dest_mem, tree_low_cst (len, 1),
- builtin_memset_read_str, &c, dest_align, 0);
+ builtin_memset_read_str, &c, dest_align, true, 0);
else if (!set_storage_via_setmem (dest_mem, len_rtx, GEN_INT (c),
dest_align, expected_align,
expected_size))
if (insn)
emit_insn (insn);
else
- emit_library_call_value (memcmp_libfunc, result, LCT_PURE_MAKE_BLOCK,
+ emit_library_call_value (memcmp_libfunc, result, LCT_PURE,
TYPE_MODE (integer_type_node), 3,
XEXP (arg1_rtx, 0), Pmode,
XEXP (arg2_rtx, 0), Pmode,
if (p && *p == '\0')
return expand_expr (dst, target, mode, EXPAND_NORMAL);
- if (!optimize_size)
+ if (optimize_insn_for_speed_p ())
{
/* See if we can store by pieces into (dst + strlen(dst)). */
tree newsrc, newdst,
expand_builtin_args_info (tree exp)
{
int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int);
- int *word_ptr = (int *) ¤t_function_args_info;
+ int *word_ptr = (int *) &crtl->args.info;
gcc_assert (sizeof (CUMULATIVE_ARGS) % sizeof (int) == 0);
/* Checking arguments is already done in fold_builtin_next_arg
that must be called before this function. */
return expand_binop (ptr_mode, add_optab,
- current_function_internal_arg_pointer,
- current_function_arg_offset_rtx,
+ crtl->args.internal_arg_pointer,
+ crtl->args.arg_offset_rtx,
NULL_RTX, 0, OPTAB_LIB_WIDEN);
}
static tree
stabilize_va_list (tree valist, int needs_lvalue)
{
- if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
+ tree vatype = targetm.canonical_va_list_type (TREE_TYPE (valist));
+
+ gcc_assert (vatype != NULL_TREE);
+
+ if (TREE_CODE (vatype) == ARRAY_TYPE)
{
if (TREE_SIDE_EFFECTS (valist))
valist = save_expr (valist);
/* For this case, the backends will be expecting a pointer to
- TREE_TYPE (va_list_type_node), but it's possible we've
- actually been given an array (an actual va_list_type_node).
+ vatype, but it's possible we've actually been given an array
+ (an actual TARGET_CANONICAL_VA_LIST_TYPE (valist)).
So fix it. */
if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
{
- tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node));
+ tree p1 = build_pointer_type (TREE_TYPE (vatype));
valist = build_fold_addr_expr_with_type (valist, p1);
}
}
if (! TREE_SIDE_EFFECTS (valist))
return valist;
- pt = build_pointer_type (va_list_type_node);
+ pt = build_pointer_type (vatype);
valist = fold_build1 (ADDR_EXPR, pt, valist);
TREE_SIDE_EFFECTS (valist) = 1;
}
return ptr_type_node;
}
+/* The "standard" abi va_list is va_list_type_node. */
+
+tree
+std_fn_abi_va_list (tree fndecl ATTRIBUTE_UNUSED)
+{
+ return va_list_type_node;
+}
+
+/* The "standard" type of va_list is va_list_type_node. */
+
+tree
+std_canonical_va_list_type (tree type)
+{
+ tree wtype, htype;
+
+ if (INDIRECT_REF_P (type))
+ type = TREE_TYPE (type);
+ else if (POINTER_TYPE_P (type) && POINTER_TYPE_P (TREE_TYPE(type)))
+ type = TREE_TYPE (type);
+ wtype = va_list_type_node;
+ htype = type;
+ /* Treat structure va_list types. */
+ if (TREE_CODE (wtype) == RECORD_TYPE && POINTER_TYPE_P (htype))
+ htype = TREE_TYPE (htype);
+ else if (TREE_CODE (wtype) == ARRAY_TYPE)
+ {
+ /* If va_list is an array type, the argument may have decayed
+ to a pointer type, e.g. by being passed to another function.
+ In that case, unwrap both types so that we can compare the
+ underlying records. */
+ if (TREE_CODE (htype) == ARRAY_TYPE
+ || POINTER_TYPE_P (htype))
+ {
+ wtype = TREE_TYPE (wtype);
+ htype = TREE_TYPE (htype);
+ }
+ }
+ if (TYPE_MAIN_VARIANT (wtype) == TYPE_MAIN_VARIANT (htype))
+ return va_list_type_node;
+
+ return NULL_TREE;
+}
+
/* The "standard" implementation of va_start: just assign `nextarg' to
the variable. */
nextarg = expand_builtin_next_arg ();
valist = stabilize_va_list (CALL_EXPR_ARG (exp, 0), 1);
-#ifdef EXPAND_BUILTIN_VA_START
- EXPAND_BUILTIN_VA_START (valist, nextarg);
-#else
- std_expand_builtin_va_start (valist, nextarg);
-#endif
+ if (targetm.expand_builtin_va_start)
+ targetm.expand_builtin_va_start (valist, nextarg);
+ else
+ std_expand_builtin_va_start (valist, nextarg);
return const0_rtx;
}
current (padded) address and increment by the (padded) size. */
tree
-std_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
+std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p)
{
tree addr, t, type_size, rounded_size, valist_tmp;
unsigned HOST_WIDE_INT align, boundary;
type = build_pointer_type (type);
align = PARM_BOUNDARY / BITS_PER_UNIT;
- boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type) / BITS_PER_UNIT;
+ boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type);
+
+ /* When we align parameter on stack for caller, if the parameter
+ alignment is beyond MAX_SUPPORTED_STACK_ALIGNMENT, it will be
+ aligned at MAX_SUPPORTED_STACK_ALIGNMENT. We will match callee
+ here with caller. */
+ if (boundary > MAX_SUPPORTED_STACK_ALIGNMENT)
+ boundary = MAX_SUPPORTED_STACK_ALIGNMENT;
+
+ boundary /= BITS_PER_UNIT;
/* Hoist the valist value into a temporary for the moment. */
valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
/* If the actual alignment is less than the alignment of the type,
adjust the type accordingly so that we don't assume strict alignment
- when deferencing the pointer. */
+ when dereferencing the pointer. */
boundary *= BITS_PER_UNIT;
if (boundary < TYPE_ALIGN (type))
{
builtin function, but a very special sort of operator. */
enum gimplify_status
-gimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p)
+gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
{
- tree promoted_type, want_va_type, have_va_type;
+ tree promoted_type, have_va_type;
tree valist = TREE_OPERAND (*expr_p, 0);
tree type = TREE_TYPE (*expr_p);
tree t;
/* Verify that valist is of the proper type. */
- want_va_type = va_list_type_node;
have_va_type = TREE_TYPE (valist);
-
if (have_va_type == error_mark_node)
return GS_ERROR;
+ have_va_type = targetm.canonical_va_list_type (have_va_type);
- if (TREE_CODE (want_va_type) == ARRAY_TYPE)
- {
- /* If va_list is an array type, the argument may have decayed
- to a pointer type, e.g. by being passed to another function.
- In that case, unwrap both types so that we can compare the
- underlying records. */
- if (TREE_CODE (have_va_type) == ARRAY_TYPE
- || POINTER_TYPE_P (have_va_type))
- {
- want_va_type = TREE_TYPE (want_va_type);
- have_va_type = TREE_TYPE (have_va_type);
- }
- }
-
- if (TYPE_MAIN_VARIANT (want_va_type) != TYPE_MAIN_VARIANT (have_va_type))
+ if (have_va_type == NULL_TREE)
{
error ("first argument to %<va_arg%> not of type %<va_list%>");
return GS_ERROR;
/* Generate a diagnostic for requesting data of a type that cannot
be passed through `...' due to type promotion at the call site. */
- else if ((promoted_type = lang_hooks.types.type_promotes_to (type))
+ if ((promoted_type = lang_hooks.types.type_promotes_to (type))
!= type)
{
static bool gave_help;
+ bool warned;
/* Unfortunately, this is merely undefined, rather than a constraint
violation, so we cannot make this an error. If this call is never
executed, the program is still strictly conforming. */
- warning (0, "%qT is promoted to %qT when passed through %<...%>",
- type, promoted_type);
- if (! gave_help)
+ warned = warning (0, "%qT is promoted to %qT when passed through %<...%>",
+ type, promoted_type);
+ if (!gave_help && warned)
{
gave_help = true;
- warning (0, "(so you should pass %qT not %qT to %<va_arg%>)",
+ inform (input_location, "(so you should pass %qT not %qT to %<va_arg%>)",
promoted_type, type);
}
/* We can, however, treat "undefined" any way we please.
Call abort to encourage the user to fix the program. */
- inform ("if this code is reached, the program will abort");
+ if (warned)
+ inform (input_location, "if this code is reached, the program will abort");
t = build_call_expr (implicit_built_in_decls[BUILT_IN_TRAP], 0);
- append_to_statement_list (t, pre_p);
+ gimplify_and_add (t, pre_p);
/* This is dead code, but go ahead and finish so that the
mode of the result comes out right. */
{
/* Make it easier for the backends by protecting the valist argument
from multiple evaluations. */
- if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
+ if (TREE_CODE (have_va_type) == ARRAY_TYPE)
{
/* For this case, the backends will be expecting a pointer to
- TREE_TYPE (va_list_type_node), but it's possible we've
- actually been given an array (an actual va_list_type_node).
+ TREE_TYPE (abi), but it's possible we've
+ actually been given an array (an actual TARGET_FN_ABI_VA_LIST).
So fix it. */
if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
{
- tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node));
+ tree p1 = build_pointer_type (TREE_TYPE (have_va_type));
valist = build_fold_addr_expr_with_type (valist, p1);
}
+
gimplify_expr (&valist, pre_p, post_p, is_gimple_val, fb_rvalue);
}
else
gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
if (!targetm.gimplify_va_arg_expr)
- /* FIXME:Once most targets are converted we should merely
+ /* FIXME: Once most targets are converted we should merely
assert this is non-null. */
return GS_ALL_DONE;
dst = stabilize_va_list (dst, 1);
src = stabilize_va_list (src, 0);
- if (TREE_CODE (va_list_type_node) != ARRAY_TYPE)
+ gcc_assert (cfun != NULL && cfun->decl != NULL_TREE);
+
+ if (TREE_CODE (targetm.fn_abi_va_list (cfun->decl)) != ARRAY_TYPE)
{
- t = build2 (MODIFY_EXPR, va_list_type_node, dst, src);
+ t = build2 (MODIFY_EXPR, targetm.fn_abi_va_list (cfun->decl), dst, src);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
/* Evaluate to pointers. */
dstb = expand_expr (dst, NULL_RTX, Pmode, EXPAND_NORMAL);
srcb = expand_expr (src, NULL_RTX, Pmode, EXPAND_NORMAL);
- size = expand_expr (TYPE_SIZE_UNIT (va_list_type_node), NULL_RTX,
- VOIDmode, EXPAND_NORMAL);
+ size = expand_expr (TYPE_SIZE_UNIT (targetm.fn_abi_va_list (cfun->decl)),
+ NULL_RTX, VOIDmode, EXPAND_NORMAL);
dstb = convert_memory_address (Pmode, dstb);
srcb = convert_memory_address (Pmode, srcb);
/* "Dereference" to BLKmode memories. */
dstb = gen_rtx_MEM (BLKmode, dstb);
set_mem_alias_set (dstb, get_alias_set (TREE_TYPE (TREE_TYPE (dst))));
- set_mem_align (dstb, TYPE_ALIGN (va_list_type_node));
+ set_mem_align (dstb, TYPE_ALIGN (targetm.fn_abi_va_list (cfun->decl)));
srcb = gen_rtx_MEM (BLKmode, srcb);
set_mem_alias_set (srcb, get_alias_set (TREE_TYPE (TREE_TYPE (src))));
- set_mem_align (srcb, TYPE_ALIGN (va_list_type_node));
+ set_mem_align (srcb, TYPE_ALIGN (targetm.fn_abi_va_list (cfun->decl)));
/* Copy. */
emit_block_move (dstb, srcb, size, BLOCK_OP_NORMAL);
return NULL_RTX;
arg = CALL_EXPR_ARG (exp, 0);
+ CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
mode = TYPE_MODE (TREE_TYPE (arg));
op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1));
t = build_string (len, str);
elem = build_type_variant (char_type_node, 1, 0);
- index = build_index_type (build_int_cst (NULL_TREE, len - 1));
+ index = build_index_type (size_int (len - 1));
type = build_array_type (elem, index);
TREE_TYPE (t) = type;
TREE_CONSTANT (t) = 1;
- TREE_INVARIANT (t) = 1;
TREE_READONLY (t) = 1;
TREE_STATIC (t) = 1;
- type = build_pointer_type (type);
- t = build1 (ADDR_EXPR, type, t);
-
type = build_pointer_type (elem);
- t = build1 (NOP_EXPR, type, t);
+ t = build1 (ADDR_EXPR, type,
+ build4 (ARRAY_REF, elem,
+ t, integer_zero_node, NULL_TREE, NULL_TREE));
return t;
}
{
/* Create a NUL-terminated string that's one char shorter
than the original, stripping off the trailing '\n'. */
- char *newstr = alloca (len);
+ char *newstr = XALLOCAVEC (char, len);
memcpy (newstr, fmt_str, len - 1);
newstr[len - 1] = 0;
arg = build_string_literal (len, newstr);
static rtx
expand_builtin_profile_func (bool exitp)
{
- rtx this, which;
+ rtx this_rtx, which;
- this = DECL_RTL (current_function_decl);
- gcc_assert (MEM_P (this));
- this = XEXP (this, 0);
+ this_rtx = DECL_RTL (current_function_decl);
+ gcc_assert (MEM_P (this_rtx));
+ this_rtx = XEXP (this_rtx, 0);
if (exitp)
which = profile_function_exit_libfunc;
else
which = profile_function_entry_libfunc;
- emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this, Pmode,
+ emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this_rtx, Pmode,
expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
0),
Pmode);
HOST_WIDE_INT hi, lo;
tree arg;
int word, bitpos;
- enum insn_code signbit_insn_code;
+ enum insn_code icode;
rtx temp;
if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
/* Check if the back end provides an insn that handles signbit for the
argument's mode. */
- signbit_insn_code = signbit_optab [(int) fmode];
- if (signbit_insn_code != CODE_FOR_nothing)
+ icode = signbit_optab->handlers [(int) fmode].insn_code;
+ if (icode != CODE_FOR_nothing)
{
target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
- emit_unop_insn (signbit_insn_code, target, temp, UNKNOWN);
+ emit_unop_insn (icode, target, temp, UNKNOWN);
return target;
}
lo = 0;
}
- if (imode != rmode)
+ if (GET_MODE_SIZE (imode) > GET_MODE_SIZE (rmode))
temp = gen_lowpart (rmode, temp);
temp = expand_binop (rmode, and_optab, temp,
immed_double_const (lo, hi, rmode),
}
#endif
+ if (synchronize_libfunc != NULL_RTX)
+ {
+ emit_library_call (synchronize_libfunc, LCT_NORMAL, VOIDmode, 0);
+ return;
+ }
+
/* If no explicit memory barrier instruction is available, create an
empty asm stmt with a memory clobber. */
x = build4 (ASM_EXPR, void_type_node, build_string (0, ""), NULL, NULL,
if (!optimize
&& !called_as_built_in (fndecl)
&& DECL_ASSEMBLER_NAME_SET_P (fndecl)
- && fcode != BUILT_IN_ALLOCA)
+ && fcode != BUILT_IN_ALLOCA
+ && fcode != BUILT_IN_FREE)
return expand_call (exp, target, ignore);
/* The built-in function expanders test for target == const0_rtx
none of its arguments are volatile, we can avoid expanding the
built-in call and just evaluate the arguments for side-effects. */
if (target == const0_rtx
- && (DECL_IS_PURE (fndecl) || TREE_READONLY (fndecl)))
+ && (DECL_PURE_P (fndecl) || TREE_READONLY (fndecl)))
{
bool volatilep = false;
tree arg;
CASE_FLT_FN (BUILT_IN_LLCEIL):
CASE_FLT_FN (BUILT_IN_LFLOOR):
CASE_FLT_FN (BUILT_IN_LLFLOOR):
- target = expand_builtin_int_roundingfn (exp, target, subtarget);
+ target = expand_builtin_int_roundingfn (exp, target);
if (target)
return target;
break;
CASE_FLT_FN (BUILT_IN_LLRINT):
CASE_FLT_FN (BUILT_IN_LROUND):
CASE_FLT_FN (BUILT_IN_LLROUND):
- target = expand_builtin_int_roundingfn_2 (exp, target, subtarget);
+ target = expand_builtin_int_roundingfn_2 (exp, target);
if (target)
return target;
break;
case BUILT_IN_ARGS_INFO:
return expand_builtin_args_info (exp);
+ case BUILT_IN_VA_ARG_PACK:
+ /* All valid uses of __builtin_va_arg_pack () are removed during
+ inlining. */
+ error ("%Kinvalid use of %<__builtin_va_arg_pack ()%>", exp);
+ return const0_rtx;
+
+ case BUILT_IN_VA_ARG_PACK_LEN:
+ /* All valid uses of __builtin_va_arg_pack_len () are removed during
+ inlining. */
+ error ("%Kinvalid use of %<__builtin_va_arg_pack_len ()%>", exp);
+ return const0_rtx;
+
/* Return the address of the first anonymous stack arg. */
case BUILT_IN_NEXT_ARG:
if (fold_builtin_next_arg (exp, false))
return expand_builtin_extend_pointer (CALL_EXPR_ARG (exp, 0));
case BUILT_IN_VA_START:
- case BUILT_IN_STDARG_START:
return expand_builtin_va_start (exp);
case BUILT_IN_VA_END:
return expand_builtin_va_end (exp);
maybe_emit_sprintf_chk_warning (exp, fcode);
break;
+ case BUILT_IN_FREE:
+ maybe_emit_free_warning (exp);
+ break;
+
default: /* just do library call, if unknown builtin */
break;
}
Otherwise the return value is END_BUILTINS. */
enum built_in_function
-builtin_mathfn_code (tree t)
+builtin_mathfn_code (const_tree t)
{
- tree fndecl, arg, parmlist;
- tree argtype, parmtype;
- call_expr_arg_iterator iter;
+ 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 END_BUILTINS;
parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
- init_call_expr_arg_iterator (t, &iter);
+ init_const_call_expr_arg_iterator (t, &iter);
for (; parmlist; parmlist = TREE_CHAIN (parmlist))
{
/* If a function doesn't take a variable number of arguments,
parmtype = TREE_VALUE (parmlist);
if (VOID_TYPE_P (parmtype))
{
- if (more_call_expr_args_p (&iter))
+ if (more_const_call_expr_args_p (&iter))
return END_BUILTINS;
return DECL_FUNCTION_CODE (fndecl);
}
- if (! more_call_expr_args_p (&iter))
+ if (! more_const_call_expr_args_p (&iter))
return END_BUILTINS;
- arg = next_call_expr_arg (&iter);
+ arg = next_const_call_expr_arg (&iter);
argtype = TREE_TYPE (arg);
if (SCALAR_FLOAT_TYPE_P (parmtype))
return NULL_TREE;
}
-/* Fold a call to __builtin_expect with argument ARG, if we expect that a
- comparison against the argument will fold to a constant. In practice,
- this means a true constant or the address of a non-weak symbol. */
+/* Create builtin_expect with PRED and EXPECTED as its arguments and
+ return it as a truthvalue. */
+
+static tree
+build_builtin_expect_predicate (tree pred, tree expected)
+{
+ tree fn, arg_types, pred_type, expected_type, call_expr, ret_type;
+
+ fn = built_in_decls[BUILT_IN_EXPECT];
+ arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
+ ret_type = TREE_TYPE (TREE_TYPE (fn));
+ pred_type = TREE_VALUE (arg_types);
+ expected_type = TREE_VALUE (TREE_CHAIN (arg_types));
+
+ pred = fold_convert (pred_type, pred);
+ expected = fold_convert (expected_type, expected);
+ call_expr = build_call_expr (fn, 2, pred, expected);
+
+ 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 (tree arg)
+fold_builtin_expect (tree arg0, tree arg1)
{
- tree inner;
+ tree inner, fndecl;
+ enum tree_code code;
+
+ /* If this is a builtin_expect within a builtin_expect keep the
+ inner one. See through a comparison against a constant. It
+ might have been added to create a thruthvalue. */
+ inner = arg0;
+ if (COMPARISON_CLASS_P (inner)
+ && TREE_CODE (TREE_OPERAND (inner, 1)) == INTEGER_CST)
+ inner = TREE_OPERAND (inner, 0);
- /* If the argument isn't invariant, then there's nothing we can do. */
- if (!TREE_INVARIANT (arg))
+ if (TREE_CODE (inner) == CALL_EXPR
+ && (fndecl = get_callee_fndecl (inner))
+ && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_EXPECT)
+ return arg0;
+
+ /* Distribute the expected value over short-circuiting operators.
+ See through the cast from truthvalue_type_node to long. */
+ inner = arg0;
+ while (TREE_CODE (inner) == NOP_EXPR
+ && INTEGRAL_TYPE_P (TREE_TYPE (inner))
+ && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (inner, 0))))
+ inner = TREE_OPERAND (inner, 0);
+
+ code = TREE_CODE (inner);
+ if (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR)
+ {
+ tree op0 = TREE_OPERAND (inner, 0);
+ tree op1 = TREE_OPERAND (inner, 1);
+
+ op0 = build_builtin_expect_predicate (op0, arg1);
+ op1 = build_builtin_expect_predicate (op1, arg1);
+ inner = build2 (code, TREE_TYPE (inner), op0, op1);
+
+ return fold_convert (TREE_TYPE (arg0), inner);
+ }
+
+ /* If the argument isn't invariant then there's nothing else we can do. */
+ if (!TREE_CONSTANT (arg0))
return NULL_TREE;
- /* If we're looking at an address of a weak decl, then do not fold. */
- inner = arg;
+ /* If we expect that a comparison against the argument will fold to
+ a constant return the constant. In practice, this means a true
+ constant or the address of a non-weak symbol. */
+ inner = arg0;
STRIP_NOPS (inner);
if (TREE_CODE (inner) == ADDR_EXPR)
{
return NULL_TREE;
}
- /* Otherwise, ARG already has the proper type for the return value. */
- return arg;
+ /* Otherwise, ARG0 already has the proper type for the return value. */
+ return arg0;
}
/* Fold a call to __builtin_classify_type with argument ARG. */
Thus we pedwarn to ensure this constraint violation is
diagnosed. */
if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn)
- pedwarn ("target format does not support infinity");
+ pedwarn (input_location, 0, "target format does not support infinity");
real_inf (&real);
return build_real (type, real);
case ABS_EXPR:
case SAVE_EXPR:
- case NON_LVALUE_EXPR:
return integer_valued_real_p (TREE_OPERAND (t, 0));
case COMPOUND_EXPR:
case MODIFY_EXPR:
case BIND_EXPR:
- return integer_valued_real_p (GENERIC_TREE_OPERAND (t, 1));
+ return integer_valued_real_p (TREE_OPERAND (t, 1));
case PLUS_EXPR:
case MINUS_EXPR:
&& operand_equal_p (real, imag, OEP_PURE_SAME))
{
const REAL_VALUE_TYPE sqrt2_trunc
- = real_value_truncate (TYPE_MODE (type), dconstsqrt2);
+ = real_value_truncate (TYPE_MODE (type), dconst_sqrt2 ());
STRIP_NOPS (real);
return fold_build2 (MULT_EXPR, type,
fold_build1 (ABS_EXPR, type, real),
/* Don't do this when optimizing for size. */
if (flag_unsafe_math_optimizations
- && optimize && !optimize_size)
+ && optimize && optimize_function_for_speed_p (cfun))
{
tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
tree tree_root;
/* The inner root was either sqrt or cbrt. */
REAL_VALUE_TYPE dconstroot =
- BUILTIN_SQRT_P (fcode) ? dconsthalf : dconstthird;
+ BUILTIN_SQRT_P (fcode) ? dconsthalf : dconst_third ();
/* Adjust for the outer root. */
SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
{
tree expfn = TREE_OPERAND (CALL_EXPR_FN (arg), 0);
const REAL_VALUE_TYPE third_trunc =
- real_value_truncate (TYPE_MODE (type), dconstthird);
+ real_value_truncate (TYPE_MODE (type), dconst_third ());
arg = fold_build2 (MULT_EXPR, type,
CALL_EXPR_ARG (arg, 0),
build_real (type, third_trunc));
{
tree arg0 = CALL_EXPR_ARG (arg, 0);
tree tree_root;
- REAL_VALUE_TYPE dconstroot = dconstthird;
+ REAL_VALUE_TYPE dconstroot = dconst_third ();
SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
tree tree_root;
REAL_VALUE_TYPE dconstroot;
- real_arithmetic (&dconstroot, MULT_EXPR, &dconstthird, &dconstthird);
+ real_arithmetic (&dconstroot, MULT_EXPR,
+ dconst_third_ptr (), dconst_third_ptr ());
dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
tree_root = build_real (type, dconstroot);
return build_call_expr (powfn, 2, arg0, tree_root);
{
tree powfn = TREE_OPERAND (CALL_EXPR_FN (arg), 0);
const REAL_VALUE_TYPE dconstroot
- = real_value_truncate (TYPE_MODE (type), dconstthird);
+ = real_value_truncate (TYPE_MODE (type), dconst_third ());
tree narg01 = fold_build2 (MULT_EXPR, type, arg01,
build_real (type, dconstroot));
return build_call_expr (powfn, 2, arg00, narg01);
icall = builtin_save_expr (icall);
rcall = build_call_expr (rfn, 1, realp);
rcall = builtin_save_expr (rcall);
- return build2 (COMPLEX_EXPR, type,
- build2 (MULT_EXPR, rtype,
- rcall,
- build1 (REALPART_EXPR, rtype, icall)),
- build2 (MULT_EXPR, rtype,
- rcall,
- build1 (IMAGPART_EXPR, rtype, icall)));
+ return fold_build2 (COMPLEX_EXPR, type,
+ fold_build2 (MULT_EXPR, rtype,
+ rcall,
+ fold_build1 (REALPART_EXPR, rtype, icall)),
+ fold_build2 (MULT_EXPR, rtype,
+ rcall,
+ fold_build1 (IMAGPART_EXPR, rtype, icall)));
}
return NULL_TREE;
if (flag_unsafe_math_optimizations && func == mpfr_log)
{
const REAL_VALUE_TYPE e_truncated =
- real_value_truncate (TYPE_MODE (type), dconste);
+ real_value_truncate (TYPE_MODE (type), dconst_e ());
if (real_dconstp (arg, &e_truncated))
return build_real (type, dconst1);
}
{
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), dconste));
+ 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):
CASE_FLT_FN (BUILT_IN_EXP10):
CASE_FLT_FN (BUILT_IN_POW10):
/* Prepare to do logN(exp10(exponent) -> exponent*logN(10). */
- x = build_real (type, dconst10);
+ {
+ 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(cbrt(x) -> (1/3)*logN(x). */
x = CALL_EXPR_ARG (arg, 0);
exponent = build_real (type, real_value_truncate (TYPE_MODE (type),
- dconstthird));
+ dconst_third ()));
break;
CASE_FLT_FN (BUILT_IN_POW):
/* Prepare to do logN(pow(x,exponent) -> exponent*logN(x). */
&& operand_equal_p (arg0, arg1, OEP_PURE_SAME))
{
const REAL_VALUE_TYPE sqrt2_trunc
- = real_value_truncate (TYPE_MODE (type), dconstsqrt2);
+ = real_value_truncate (TYPE_MODE (type), dconst_sqrt2 ());
return fold_build2 (MULT_EXPR, type,
fold_build1 (ABS_EXPR, type, arg0),
build_real (type, sqrt2_trunc));
if (flag_unsafe_math_optimizations)
{
const REAL_VALUE_TYPE dconstroot
- = real_value_truncate (TYPE_MODE (type), dconstthird);
+ = real_value_truncate (TYPE_MODE (type), dconst_third ());
if (REAL_VALUES_EQUAL (c, dconstroot))
{
if (tree_expr_nonnegative_p (arg))
{
const REAL_VALUE_TYPE dconstroot
- = real_value_truncate (TYPE_MODE (type), dconstthird);
+ = real_value_truncate (TYPE_MODE (type), dconst_third ());
tree narg1 = fold_build2 (MULT_EXPR, type, arg1,
build_real (type, dconstroot));
return build_call_expr (fndecl, 2, arg, narg1);
if (!tree_int_cst_equal (lang_hooks.expr_size (srcvar), len))
return NULL_TREE;
/* With memcpy, it is possible to bypass aliasing rules, so without
- this check i. e. execute/20060930-2.c would be misoptimized, because
+ this check i.e. execute/20060930-2.c would be misoptimized, because
it use conflicting alias set to hold argument for the memcpy call.
- This check is probably unnecesary with -fno-strict-aliasing.
+ This check is probably unnecessary with -fno-strict-aliasing.
Similarly for destvar. See also PR29286. */
if (!var_decl_component_p (srcvar)
/* Accept: memcpy (*char_var, "test", 1); that simplify
if (operand_equal_p (src, dest, 0))
return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest);
- if (optimize_size)
+ if (optimize_function_for_size_p (cfun))
return NULL_TREE;
fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
if (target_char_cast (arg2, &c))
return NULL_TREE;
- r = memchr (p1, c, tree_low_cst (len, 1));
+ r = (char *) memchr (p1, c, tree_low_cst (len, 1));
if (r == NULL)
return build_int_cst (TREE_TYPE (arg1), 0);
REAL_VALUE_TYPE r;
if (!validate_arg (arg, REAL_TYPE))
- {
- error ("non-floating-point argument to function %qs",
- IDENTIFIER_POINTER (DECL_NAME (fndecl)));
- return error_mark_node;
- }
+ return NULL_TREE;
switch (builtin_index)
{
return NULL_TREE;
+ 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 = built_in_decls[BUILT_IN_ISINF];
+ tree tmp = NULL_TREE;
+
+ arg = builtin_save_expr (arg);
+
+ if (signbit_fn && isinf_fn)
+ {
+ tree signbit_call = build_call_expr (signbit_fn, 1, arg);
+ tree isinf_call = build_call_expr (isinf_fn, 1, arg);
+
+ signbit_call = fold_build2 (NE_EXPR, integer_type_node,
+ signbit_call, integer_zero_node);
+ isinf_call = fold_build2 (NE_EXPR, integer_type_node,
+ isinf_call, integer_zero_node);
+
+ tmp = fold_build3 (COND_EXPR, integer_type_node, signbit_call,
+ integer_minus_one_node, integer_one_node);
+ tmp = fold_build3 (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))))
}
}
+/* 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 (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 (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 (EQ_EXPR, integer_type_node, arg,
+ build_real (type, dconst0));
+ res = fold_build3 (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 (GE_EXPR, integer_type_node, arg, build_real (type, r));
+ res = fold_build3 (COND_EXPR, integer_type_node, tmp, fp_normal, res);
+
+ if (HONOR_INFINITIES (mode))
+ {
+ real_inf (&r);
+ tmp = fold_build2 (EQ_EXPR, integer_type_node, arg,
+ build_real (type, r));
+ res = fold_build3 (COND_EXPR, integer_type_node, tmp, fp_infinite, res);
+ }
+
+ if (HONOR_NANS (mode))
+ {
+ tmp = fold_build2 (ORDERED_EXPR, integer_type_node, arg, arg);
+ res = fold_build3 (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.
cmp_type = type0;
else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
cmp_type = type1;
- else
- {
- error ("non-floating-point argument to function %qs",
- IDENTIFIER_POINTER (DECL_NAME (fndecl)));
- return error_mark_node;
- }
arg0 = fold_convert (cmp_type, arg0);
arg1 = fold_convert (cmp_type, arg1);
case BUILT_IN_ISINFD128:
return fold_builtin_classify (fndecl, arg0, BUILT_IN_ISINF);
+ case BUILT_IN_ISINF_SIGN:
+ return fold_builtin_classify (fndecl, arg0, BUILT_IN_ISINF_SIGN);
+
CASE_FLT_FN (BUILT_IN_ISNAN):
case BUILT_IN_ISNAND32:
case BUILT_IN_ISNAND64:
return fold_builtin_strpbrk (arg0, arg1, type);
case BUILT_IN_EXPECT:
- return fold_builtin_expect (arg0);
+ return fold_builtin_expect (arg0, arg1);
CASE_FLT_FN (BUILT_IN_POW):
return fold_builtin_pow (fndecl, arg0, arg1, type);
fold_builtin_n (tree fndecl, tree *args, int nargs, bool ignore)
{
tree ret = NULL_TREE;
+
switch (nargs)
{
case 0:
}
if (ret)
{
- ret = build1 (NOP_EXPR, GENERIC_TREE_TYPE (ret), ret);
+ ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
TREE_NO_WARNING (ret) = 1;
return ret;
}
case BUILT_IN_SNPRINTF_CHK:
case BUILT_IN_VSNPRINTF_CHK:
ret = fold_builtin_snprintf_chk (exp, NULL_TREE, fcode);
+ break;
+
+ case BUILT_IN_FPCLASSIFY:
+ ret = fold_builtin_fpclassify (exp);
+ break;
default:
break;
tree fndecl = get_callee_fndecl (exp);
if (fndecl
&& TREE_CODE (fndecl) == FUNCTION_DECL
- && DECL_BUILT_IN (fndecl))
- {
+ && DECL_BUILT_IN (fndecl)
+ /* If CALL_EXPR_VA_ARG_PACK is set, the arguments aren't finalized
+ yet. Defer folding until we see all the arguments
+ (after inlining). */
+ && !CALL_EXPR_VA_ARG_PACK (exp))
+ {
+ int nargs = call_expr_nargs (exp);
+
+ /* Before gimplification CALL_EXPR_VA_ARG_PACK is not set, but
+ instead last argument is __builtin_va_arg_pack (). Defer folding
+ even in that case, until arguments are finalized. */
+ if (nargs && TREE_CODE (CALL_EXPR_ARG (exp, nargs - 1)) == CALL_EXPR)
+ {
+ tree fndecl2 = get_callee_fndecl (CALL_EXPR_ARG (exp, nargs - 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;
+ }
+
/* FIXME: Don't use a list in this interface. */
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return targetm.fold_builtin (fndecl, CALL_EXPR_ARGS (exp), ignore);
else
{
- int nargs = call_expr_nargs (exp);
if (nargs <= MAX_ARGS_TO_FOLD_BUILTIN)
{
tree *args = CALL_EXPR_ARGP (exp);
if (CAN_HAVE_LOCATION_P (realret)
&& !EXPR_HAS_LOCATION (realret))
SET_EXPR_LOCATION (realret, EXPR_LOCATION (exp));
+ return realret;
}
return ret;
}
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 (type, fn, n, argarray);
+ }
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
{
tree arglist = NULL_TREE;
int i, j;
va_list ap;
- buffer = alloca (nargs * sizeof (tree));
+ buffer = XALLOCAVEC (tree, nargs);
va_start (ap, n);
for (i = 0; i < n; i++)
buffer[i] = va_arg (ap, tree);
a type. */
static bool
-validate_arg (tree arg, enum tree_code code)
+validate_arg (const_tree arg, enum tree_code code)
{
if (!arg)
return false;
else if (code == POINTER_TYPE)
return POINTER_TYPE_P (TREE_TYPE (arg));
+ else if (code == INTEGER_TYPE)
+ return INTEGRAL_TYPE_P (TREE_TYPE (arg));
return code == TREE_CODE (TREE_TYPE (arg));
}
/* This function validates the types of a function call argument list
against a specified list of tree_codes. If the last specifier is a 0,
that represents an ellipses, otherwise the last specifier must be a
+ VOID_TYPE.
+
+ This is the GIMPLE version of validate_arglist. Eventually we want to
+ completely convert builtins.c to work from GIMPLEs and the tree based
+ validate_arglist will then be removed. */
+
+bool
+validate_gimple_arglist (const_gimple call, ...)
+{
+ enum tree_code code;
+ bool res = 0;
+ va_list ap;
+ const_tree arg;
+ size_t i;
+
+ va_start (ap, call);
+ i = 0;
+
+ do
+ {
+ code = va_arg (ap, enum tree_code);
+ switch (code)
+ {
+ case 0:
+ /* This signifies an ellipses, any further arguments are all ok. */
+ res = true;
+ goto end;
+ case VOID_TYPE:
+ /* This signifies an endlink, if no arguments remain, return
+ true, otherwise return false. */
+ res = (i == gimple_call_num_args (call));
+ goto end;
+ default:
+ /* If no parameters remain or the parameter's code does not
+ match the specified code, return false. Otherwise continue
+ checking any remaining arguments. */
+ arg = gimple_call_arg (call, i++);
+ if (!validate_arg (arg, code))
+ goto end;
+ break;
+ }
+ }
+ while (1);
+
+ /* We need gotos here since we can only have one VA_CLOSE in a
+ function. */
+ end: ;
+ va_end (ap);
+
+ return res;
+}
+
+/* This function validates the types of a function call argument list
+ against a specified list of tree_codes. If the last specifier is a 0,
+ that represents an ellipses, otherwise the last specifier must be a
VOID_TYPE. */
bool
-validate_arglist (tree callexpr, ...)
+validate_arglist (const_tree callexpr, ...)
{
enum tree_code code;
bool res = 0;
va_list ap;
- call_expr_arg_iterator iter;
- tree arg;
+ const_call_expr_arg_iterator iter;
+ const_tree arg;
va_start (ap, callexpr);
- init_call_expr_arg_iterator (callexpr, &iter);
+ init_const_call_expr_arg_iterator (callexpr, &iter);
do
{
case VOID_TYPE:
/* This signifies an endlink, if no arguments remain, return
true, otherwise return false. */
- res = !more_call_expr_args_p (&iter);
+ res = !more_const_call_expr_args_p (&iter);
goto end;
default:
/* If no parameters remain or the parameter's code does not
match the specified code, return false. Otherwise continue
checking any remaining arguments. */
- arg = next_call_expr_arg (&iter);
+ arg = next_const_call_expr_arg (&iter);
if (!validate_arg (arg, code))
goto end;
break;
case 1: /* length is greater than 1, call fwrite. */
{
/* If optimizing for size keep fputs. */
- if (optimize_size)
+ if (optimize_function_for_size_p (cfun))
return NULL_TREE;
/* New argument list transforming fputs(string, stream) to
fwrite(string, 1, len, stream). */
/* 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)
{
is not quite the same as STRIP_NOPS. It does more.
We must also strip off INDIRECT_EXPR for C++ reference
parameters. */
- while (TREE_CODE (arg) == NOP_EXPR
- || TREE_CODE (arg) == CONVERT_EXPR
- || TREE_CODE (arg) == NON_LVALUE_EXPR
+ while (CONVERT_EXPR_P (arg)
|| TREE_CODE (arg) == INDIRECT_REF)
arg = TREE_OPERAND (arg, 0);
if (arg != last_parm)
it. */
warning (0, "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 (0, "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:
tree ost;
int object_size_type;
tree fndecl = get_callee_fndecl (exp);
- location_t locus = EXPR_LOCATION (exp);
if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
{
- error ("%Hfirst argument of %D must be a pointer, second integer constant",
- &locus, fndecl);
+ error ("%Kfirst argument of %D must be a pointer, second integer constant",
+ exp, fndecl);
expand_builtin_trap ();
return const0_rtx;
}
|| tree_int_cst_sgn (ost) < 0
|| compare_tree_int (ost, 3) > 0)
{
- error ("%Hlast argument of %D is not integer constant between 0 and 3",
- &locus, fndecl);
+ error ("%Klast argument of %D is not integer constant between 0 and 3",
+ exp, fndecl);
expand_builtin_trap ();
return const0_rtx;
}
if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
{
- location_t locus = EXPR_LOCATION (exp);
- warning (0, "%Hcall to %D will always overflow destination buffer",
- &locus, get_callee_fndecl (exp));
+ warning (0, "%Kcall to %D will always overflow destination buffer",
+ exp, get_callee_fndecl (exp));
return NULL_RTX;
}
return NULL_RTX;
fn = build_call_expr (fn, 3, dest, src, len);
+ STRIP_TYPE_NOPS (fn);
+ while (TREE_CODE (fn) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (fn, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ fn = TREE_OPERAND (fn, 1);
+ }
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
return expand_expr (fn, target, mode, EXPAND_NORMAL);
if (!fn)
return NULL_RTX;
fn = build_call_expr (fn, 4, dest, src, len, size);
+ STRIP_TYPE_NOPS (fn);
+ while (TREE_CODE (fn) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (fn, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ fn = TREE_OPERAND (fn, 1);
+ }
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
return expand_expr (fn, target, mode, EXPAND_NORMAL);
{
int is_strlen = 0;
tree len, size;
- location_t locus;
switch (fcode)
{
src = c_strlen (src, 1);
if (! src || ! host_integerp (src, 1))
{
- locus = EXPR_LOCATION (exp);
- warning (0, "%Hcall to %D might overflow destination buffer",
- &locus, get_callee_fndecl (exp));
+ warning (0, "%Kcall to %D might overflow destination buffer",
+ exp, get_callee_fndecl (exp));
return;
}
else if (tree_int_cst_lt (src, size))
else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len))
return;
- locus = EXPR_LOCATION (exp);
- warning (0, "%Hcall to %D will always overflow destination buffer",
- &locus, get_callee_fndecl (exp));
+ warning (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
if (! tree_int_cst_lt (len, size))
{
- location_t locus = EXPR_LOCATION (exp);
- warning (0, "%Hcall to %D will always overflow destination buffer",
- &locus, get_callee_fndecl (exp));
+ warning (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. */
+
+static void
+maybe_emit_free_warning (tree exp)
+{
+ tree arg = CALL_EXPR_ARG (exp, 0);
+
+ STRIP_NOPS (arg);
+ if (TREE_CODE (arg) != ADDR_EXPR)
+ return;
+
+ arg = get_base_address (TREE_OPERAND (arg, 0));
+ if (arg == NULL || INDIRECT_REF_P (arg))
+ return;
+
+ if (SSA_VAR_P (arg))
+ warning (0, "%Kattempt to free a non-heap object %qD", exp, arg);
+ else
+ warning (0, "%Kattempt to free a non-heap object", exp);
+}
+
/* Fold a call to __builtin_object_size with arguments PTR and OST,
if possible. */
{
/* Create a NUL-terminated string that's one char shorter
than the original, stripping off the trailing '\n'. */
- char *newstr = alloca (len);
+ char *newstr = XALLOCAVEC (char, len);
memcpy (newstr, str, len - 1);
newstr[len - 1] = 0;
&& (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min))
&& (!max || real_compare (inclusive ? LE_EXPR: LT_EXPR , ra, max)))
{
- const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+ const int prec = fmt->p;
+ const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
int inexact;
mpfr_t m;
mpfr_init2 (m, prec);
mpfr_from_real (m, ra, GMP_RNDN);
mpfr_clear_flags ();
- inexact = func (m, m, GMP_RNDN);
+ inexact = func (m, m, rnd);
result = do_mpfr_ckconv (m, type, inexact);
mpfr_clear (m);
}
if (real_isfinite (ra1) && real_isfinite (ra2))
{
- const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+ const int prec = fmt->p;
+ const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
int inexact;
mpfr_t m1, m2;
mpfr_from_real (m1, ra1, GMP_RNDN);
mpfr_from_real (m2, ra2, GMP_RNDN);
mpfr_clear_flags ();
- inexact = func (m1, m1, m2, GMP_RNDN);
+ inexact = func (m1, m1, m2, rnd);
result = do_mpfr_ckconv (m1, type, inexact);
mpfr_clears (m1, m2, NULL);
}
if (real_isfinite (ra1) && real_isfinite (ra2) && real_isfinite (ra3))
{
- const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+ const int prec = fmt->p;
+ const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
int inexact;
mpfr_t m1, m2, m3;
mpfr_from_real (m2, ra2, GMP_RNDN);
mpfr_from_real (m3, ra3, GMP_RNDN);
mpfr_clear_flags ();
- inexact = func (m1, m1, m2, m3, GMP_RNDN);
+ inexact = func (m1, m1, m2, m3, rnd);
result = do_mpfr_ckconv (m1, type, inexact);
mpfr_clears (m1, m2, m3, NULL);
}
if (real_isfinite (ra))
{
- const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+ const int prec = fmt->p;
+ const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
tree result_s, result_c;
int inexact;
mpfr_t m, ms, mc;
mpfr_inits2 (prec, m, ms, mc, NULL);
mpfr_from_real (m, ra, GMP_RNDN);
mpfr_clear_flags ();
- inexact = mpfr_sin_cos (ms, mc, m, GMP_RNDN);
+ inexact = mpfr_sin_cos (ms, mc, m, rnd);
result_s = do_mpfr_ckconv (ms, type, inexact);
result_c = do_mpfr_ckconv (mc, type, inexact);
mpfr_clears (m, ms, mc, NULL);
&& real_isfinite (ra)
&& (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min)))
{
- const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+ const int prec = fmt->p;
+ const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
int inexact;
mpfr_t m;
mpfr_init2 (m, prec);
mpfr_from_real (m, ra, GMP_RNDN);
mpfr_clear_flags ();
- inexact = func (m, n, m, GMP_RNDN);
+ inexact = func (m, n, m, rnd);
result = do_mpfr_ckconv (m, type, inexact);
mpfr_clear (m);
}
if (real_isfinite (ra0) && real_isfinite (ra1))
{
- const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+ const int prec = fmt->p;
+ const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
tree result_rem;
long integer_quo;
mpfr_t m0, m1;
mpfr_from_real (m0, ra0, GMP_RNDN);
mpfr_from_real (m1, ra1, GMP_RNDN);
mpfr_clear_flags ();
- mpfr_remquo (m0, &integer_quo, m0, m1, GMP_RNDN);
+ mpfr_remquo (m0, &integer_quo, m0, m1, rnd);
/* Remquo is independent of the rounding mode, so pass
inexact=0 to do_mpfr_ckconv(). */
result_rem = do_mpfr_ckconv (m0, type, /*inexact=*/ 0);
&& ra->cl != rvc_zero
&& !(real_isneg(ra) && real_isinteger(ra, TYPE_MODE (type))))
{
- const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+ const int prec = fmt->p;
+ const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
int inexact, sg;
mpfr_t m;
tree result_lg;
mpfr_init2 (m, prec);
mpfr_from_real (m, ra, GMP_RNDN);
mpfr_clear_flags ();
- inexact = mpfr_lgamma (m, &sg, m, GMP_RNDN);
+ inexact = mpfr_lgamma (m, &sg, m, rnd);
result_lg = do_mpfr_ckconv (m, type, inexact);
mpfr_clear (m);
if (result_lg)
return result;
}
#endif
+
+/* FIXME tuples.
+ The functions below provide an alternate interface for folding
+ builtin function calls presented as GIMPLE_CALL statements rather
+ than as CALL_EXPRs. The folded result is still expressed as a
+ tree. There is too much code duplication in the handling of
+ varargs functions, and a more intrusive re-factoring would permit
+ better sharing of code between the tree and statement-based
+ versions of these functions. */
+
+/* Construct a new CALL_EXPR using the tail of the argument list of STMT
+ along with N new arguments specified as the "..." parameters. SKIP
+ is the number of arguments in STMT to be omitted. This function is used
+ to do varargs-to-varargs transformations. */
+
+static tree
+gimple_rewrite_call_expr (gimple stmt, int skip, tree fndecl, int n, ...)
+{
+ int oldnargs = gimple_call_num_args (stmt);
+ int nargs = oldnargs - skip + n;
+ tree fntype = TREE_TYPE (fndecl);
+ tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
+ tree *buffer;
+ int i, j;
+ va_list ap;
+
+ buffer = XALLOCAVEC (tree, nargs);
+ va_start (ap, n);
+ for (i = 0; i < n; i++)
+ buffer[i] = va_arg (ap, tree);
+ va_end (ap);
+ for (j = skip; j < oldnargs; j++, i++)
+ buffer[i] = gimple_call_arg (stmt, j);
+
+ return fold (build_call_array (TREE_TYPE (fntype), fn, nargs, buffer));
+}
+
+/* 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)
+{
+ tree dest, size, len, fn, fmt, flag;
+ const char *fmt_str;
+ int nargs = gimple_call_num_args (stmt);
+
+ /* Verify the required arguments in the original call. */
+ if (nargs < 4)
+ return NULL_TREE;
+ dest = gimple_call_arg (stmt, 0);
+ if (!validate_arg (dest, POINTER_TYPE))
+ return NULL_TREE;
+ flag = gimple_call_arg (stmt, 1);
+ if (!validate_arg (flag, INTEGER_TYPE))
+ return NULL_TREE;
+ size = gimple_call_arg (stmt, 2);
+ if (!validate_arg (size, INTEGER_TYPE))
+ return NULL_TREE;
+ fmt = gimple_call_arg (stmt, 3);
+ if (!validate_arg (fmt, POINTER_TYPE))
+ return NULL_TREE;
+
+ if (! host_integerp (size, 1))
+ return NULL_TREE;
+
+ len = NULL_TREE;
+
+ if (!init_target_chars ())
+ return NULL_TREE;
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str != NULL)
+ {
+ /* If the format doesn't contain % args or %%, we know the size. */
+ if (strchr (fmt_str, target_percent) == 0)
+ {
+ if (fcode != BUILT_IN_SPRINTF_CHK || nargs == 4)
+ len = build_int_cstu (size_type_node, strlen (fmt_str));
+ }
+ /* If the format is "%s" and first ... argument is a string literal,
+ we know the size too. */
+ else if (fcode == BUILT_IN_SPRINTF_CHK
+ && strcmp (fmt_str, target_percent_s) == 0)
+ {
+ tree arg;
+
+ if (nargs == 5)
+ {
+ arg = gimple_call_arg (stmt, 4);
+ if (validate_arg (arg, POINTER_TYPE))
+ {
+ len = c_strlen (arg, 1);
+ if (! len || ! host_integerp (len, 1))
+ len = NULL_TREE;
+ }
+ }
+ }
+ }
+
+ if (! integer_all_onesp (size))
+ {
+ if (! len || ! tree_int_cst_lt (len, size))
+ return NULL_TREE;
+ }
+
+ /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0
+ or if format doesn't contain % chars or is "%s". */
+ if (! integer_zerop (flag))
+ {
+ if (fmt_str == NULL)
+ return NULL_TREE;
+ if (strchr (fmt_str, target_percent) != NULL
+ && strcmp (fmt_str, target_percent_s))
+ return NULL_TREE;
+ }
+
+ /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */
+ fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK
+ ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF];
+ if (!fn)
+ return NULL_TREE;
+
+ return gimple_rewrite_call_expr (stmt, 4, fn, 2, dest, fmt);
+}
+
+/* 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)
+{
+ tree dest, size, len, fn, fmt, flag;
+ const char *fmt_str;
+
+ /* Verify the required arguments in the original call. */
+ if (gimple_call_num_args (stmt) < 5)
+ return NULL_TREE;
+ dest = gimple_call_arg (stmt, 0);
+ if (!validate_arg (dest, POINTER_TYPE))
+ return NULL_TREE;
+ len = gimple_call_arg (stmt, 1);
+ if (!validate_arg (len, INTEGER_TYPE))
+ return NULL_TREE;
+ flag = gimple_call_arg (stmt, 2);
+ if (!validate_arg (flag, INTEGER_TYPE))
+ return NULL_TREE;
+ size = gimple_call_arg (stmt, 3);
+ if (!validate_arg (size, INTEGER_TYPE))
+ return NULL_TREE;
+ fmt = gimple_call_arg (stmt, 4);
+ if (!validate_arg (fmt, POINTER_TYPE))
+ return NULL_TREE;
+
+ if (! host_integerp (size, 1))
+ return NULL_TREE;
+
+ if (! integer_all_onesp (size))
+ {
+ if (! host_integerp (len, 1))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+ return NULL_TREE;
+ }
+ else
+ maxlen = len;
+
+ if (tree_int_cst_lt (size, maxlen))
+ return NULL_TREE;
+ }
+
+ if (!init_target_chars ())
+ return NULL_TREE;
+
+ /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0
+ or if format doesn't contain % chars or is "%s". */
+ if (! integer_zerop (flag))
+ {
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return NULL_TREE;
+ if (strchr (fmt_str, target_percent) != NULL
+ && strcmp (fmt_str, target_percent_s))
+ return NULL_TREE;
+ }
+
+ /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is
+ available. */
+ fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK
+ ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF];
+ if (!fn)
+ return NULL_TREE;
+
+ return gimple_rewrite_call_expr (stmt, 5, fn, 3, dest, len, fmt);
+}
+
+/* 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)
+{
+ tree ret = NULL_TREE;
+ tree fndecl = gimple_call_fndecl (stmt);
+ if (fndecl
+ && TREE_CODE (fndecl) == FUNCTION_DECL
+ && DECL_BUILT_IN (fndecl)
+ && !gimple_call_va_arg_pack_p (stmt))
+ {
+ int nargs = gimple_call_num_args (stmt);
+
+ /* FIXME: Don't use a list in this interface. */
+ if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+ {
+ tree arglist = NULL_TREE;
+ int i;
+ for (i = nargs - 1; i >= 0; i--)
+ arglist = tree_cons (NULL_TREE, gimple_call_arg (stmt, i), arglist);
+ return targetm.fold_builtin (fndecl, arglist, ignore);
+ }
+ else
+ {
+ if (nargs <= MAX_ARGS_TO_FOLD_BUILTIN)
+ {
+ tree args[MAX_ARGS_TO_FOLD_BUILTIN];
+ int i;
+ for (i = 0; i < nargs; i++)
+ args[i] = gimple_call_arg (stmt, i);
+ ret = fold_builtin_n (fndecl, args, nargs, ignore);
+ }
+ if (!ret)
+ ret = gimple_fold_builtin_varargs (fndecl, stmt, ignore);
+ if (ret)
+ {
+ /* Propagate location information from original call to
+ expansion of builtin. Otherwise things like
+ maybe_emit_chk_warning, that operate on the expansion
+ of a builtin, will use the wrong location information. */
+ if (gimple_has_location (stmt))
+ {
+ tree realret = ret;
+ if (TREE_CODE (ret) == NOP_EXPR)
+ realret = TREE_OPERAND (ret, 0);
+ if (CAN_HAVE_LOCATION_P (realret)
+ && !EXPR_HAS_LOCATION (realret))
+ SET_EXPR_LOCATION (realret, gimple_location (stmt));
+ return realret;
+ }
+ return ret;
+ }
+ }
+ }
+ return NULL_TREE;
+}