/* Expand builtin functions.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
This file is part of GCC.
#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"
#include "value-prof.h"
#include "diagnostic.h"
+#ifndef SLOW_UNALIGNED_ACCESS
+#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) STRICT_ALIGNMENT
+#endif
+
#ifndef PAD_VARARGS_DOWN
#define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN
#endif
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);
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_arg3 (tree, tree, tree, tree,
int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t));
static tree do_mpfr_sincos (tree, tree, tree);
-#if MPFR_VERSION >= MPFR_VERSION_NUM(2,3,0)
static tree do_mpfr_bessel_n (tree, tree, tree,
int (*)(mpfr_ptr, long, mpfr_srcptr, mp_rnd_t),
const REAL_VALUE_TYPE *, bool);
static tree do_mpfr_remquo (tree, tree, tree);
static tree do_mpfr_lgamma_r (tree, tree, tree);
-#endif
-
-/* 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". */
-static bool called_as_built_in (tree node)
+bool
+is_builtin_name (const char *name)
{
- const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
if (strncmp (name, "__builtin_", 10) == 0)
return true;
if (strncmp (name, "__sync_", 7) == 0)
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". */
+
+static bool
+called_as_built_in (tree node)
+{
+ /* Note that we must use DECL_NAME, not DECL_ASSEMBLER_NAME_SET_P since
+ we want the name used to call the function, not the name it
+ will have. */
+ const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
+ return is_builtin_name (name);
+}
+
+/* Return the alignment in bits of EXP, an object.
+ Don't return more than MAX_ALIGN no matter what, ALIGN is the inital
+ guessed alignment e.g. from type alignment. */
+
+int
+get_object_alignment (tree exp, unsigned int align, unsigned int max_align)
+{
+ unsigned int inner;
+
+ inner = max_align;
+ if (handled_component_p (exp))
+ {
+ HOST_WIDE_INT bitsize, bitpos;
+ tree offset;
+ enum machine_mode mode;
+ int unsignedp, volatilep;
+
+ exp = get_inner_reference (exp, &bitsize, &bitpos, &offset,
+ &mode, &unsignedp, &volatilep, true);
+ if (bitpos)
+ inner = MIN (inner, (unsigned) (bitpos & -bitpos));
+ while (offset)
+ {
+ tree next_offset;
+
+ if (TREE_CODE (offset) == PLUS_EXPR)
+ {
+ next_offset = TREE_OPERAND (offset, 0);
+ offset = TREE_OPERAND (offset, 1);
+ }
+ else
+ next_offset = NULL;
+ if (host_integerp (offset, 1))
+ {
+ /* Any overflow in calculating offset_bits won't change
+ the alignment. */
+ unsigned offset_bits
+ = ((unsigned) tree_low_cst (offset, 1) * BITS_PER_UNIT);
+
+ if (offset_bits)
+ inner = MIN (inner, (offset_bits & -offset_bits));
+ }
+ else if (TREE_CODE (offset) == MULT_EXPR
+ && host_integerp (TREE_OPERAND (offset, 1), 1))
+ {
+ /* Any overflow in calculating offset_factor won't change
+ the alignment. */
+ unsigned offset_factor
+ = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1)
+ * BITS_PER_UNIT);
+
+ if (offset_factor)
+ inner = MIN (inner, (offset_factor & -offset_factor));
+ }
+ else
+ {
+ inner = MIN (inner, BITS_PER_UNIT);
+ break;
+ }
+ offset = next_offset;
+ }
+ }
+ if (DECL_P (exp))
+ align = MIN (inner, DECL_ALIGN (exp));
+#ifdef CONSTANT_ALIGNMENT
+ else if (CONSTANT_CLASS_P (exp))
+ align = MIN (inner, (unsigned)CONSTANT_ALIGNMENT (exp, align));
+#endif
+ else if (TREE_CODE (exp) == VIEW_CONVERT_EXPR
+ || TREE_CODE (exp) == INDIRECT_REF)
+ align = MIN (TYPE_ALIGN (TREE_TYPE (exp)), inner);
+ else
+ align = MIN (align, inner);
+ return MIN (align, max_align);
+}
+
/* Return the alignment in bits of EXP, a pointer valued expression.
But don't return more than MAX_ALIGN no matter what.
The alignment returned is, by default, the alignment of the thing that
case ADDR_EXPR:
/* See what we are pointing at and look at its alignment. */
- exp = TREE_OPERAND (exp, 0);
- inner = max_align;
- if (handled_component_p (exp))
- {
- HOST_WIDE_INT bitsize, bitpos;
- tree offset;
- enum machine_mode mode;
- int unsignedp, volatilep;
-
- exp = get_inner_reference (exp, &bitsize, &bitpos, &offset,
- &mode, &unsignedp, &volatilep, true);
- if (bitpos)
- inner = MIN (inner, (unsigned) (bitpos & -bitpos));
- if (offset && TREE_CODE (offset) == PLUS_EXPR
- && host_integerp (TREE_OPERAND (offset, 1), 1))
- {
- /* Any overflow in calculating offset_bits won't change
- the alignment. */
- unsigned offset_bits
- = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1)
- * BITS_PER_UNIT);
-
- if (offset_bits)
- inner = MIN (inner, (offset_bits & -offset_bits));
- offset = TREE_OPERAND (offset, 0);
- }
- if (offset && TREE_CODE (offset) == MULT_EXPR
- && host_integerp (TREE_OPERAND (offset, 1), 1))
- {
- /* Any overflow in calculating offset_factor won't change
- the alignment. */
- unsigned offset_factor
- = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1)
- * BITS_PER_UNIT);
-
- if (offset_factor)
- inner = MIN (inner, (offset_factor & -offset_factor));
- }
- else if (offset)
- inner = MIN (inner, BITS_PER_UNIT);
- }
- if (DECL_P (exp))
- align = MIN (inner, DECL_ALIGN (exp));
-#ifdef CONSTANT_ALIGNMENT
- else if (CONSTANT_CLASS_P (exp))
- align = MIN (inner, (unsigned)CONSTANT_ALIGNMENT (exp, align));
-#endif
- else if (TREE_CODE (exp) == VIEW_CONVERT_EXPR
- || TREE_CODE (exp) == INDIRECT_REF)
- align = MIN (TYPE_ALIGN (TREE_TYPE (exp)), inner);
- else
- align = MIN (align, inner);
- return MIN (align, max_align);
+ return get_object_alignment (TREE_OPERAND (exp, 0), align, max_align);
default:
return align;
else
offset = tree_low_cst (offset_node, 0);
- /* If the offset is known to be out of bounds, warn, and call strlen at
- runtime. */
+ /* If the offset is known to be out of bounds, the front-end should
+ have warned already. We call strlen at runtime.
+
+ ??? Perhaps we should turn this into an assert and force
+ front-ends to define offsets whtin boundaries. */
if (offset < 0 || offset > max)
{
- /* 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;
}
{
/* Now restore our arg pointer from the address at which it
was saved in our stack frame. */
- emit_move_insn (virtual_incoming_args_rtx,
+ emit_move_insn (crtl->args.internal_arg_pointer,
copy_to_reg (get_arg_pointer_save_area ()));
}
}
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 ();
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))
{
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))
static rtx
get_memory_rtx (tree exp, tree len)
{
- rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
- rtx mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
+ tree orig_exp = exp;
+ rtx addr, mem;
+ HOST_WIDE_INT off;
+
+ /* When EXP is not resolved SAVE_EXPR, MEM_ATTRS can be still derived
+ from its expression, for expr->a.b only <variable>.a.b is recorded. */
+ if (TREE_CODE (exp) == SAVE_EXPR && !SAVE_EXPR_RESOLVED_P (exp))
+ exp = TREE_OPERAND (exp, 0);
+
+ addr = expand_expr (orig_exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
+ mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
/* 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
&& POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0))))
exp = TREE_OPERAND (exp, 0);
- if (TREE_CODE (exp) == ADDR_EXPR)
+ off = 0;
+ if (TREE_CODE (exp) == POINTER_PLUS_EXPR
+ && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+ && host_integerp (TREE_OPERAND (exp, 1), 0)
+ && (off = tree_low_cst (TREE_OPERAND (exp, 1), 0)) > 0)
+ exp = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ else if (TREE_CODE (exp) == ADDR_EXPR)
exp = TREE_OPERAND (exp, 0);
else if (POINTER_TYPE_P (TREE_TYPE (exp)))
exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
{
set_mem_attributes (mem, exp, 0);
+ if (off)
+ mem = adjust_automodify_address_nv (mem, BLKmode, NULL, off);
+
/* Allow the string and memory builtins to overflow from one
field into another, see http://gcc.gnu.org/PR23561.
Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole
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
/* Put the insns after the NOTE that starts the function.
If this is inside a start_sequence, make the outer-level insn
chain current, so the code is placed at the start of the
- function. */
+ function. If internal_arg_pointer is a non-virtual pseudo,
+ it needs to be placed after the function that initializes
+ that pseudo. */
push_topmost_sequence ();
- emit_insn_before (seq, NEXT_INSN (entry_of_function ()));
+ if (REG_P (crtl->args.internal_arg_pointer)
+ && REGNO (crtl->args.internal_arg_pointer) > LAST_VIRTUAL_REGISTER)
+ emit_insn_before (seq, parm_birth_insn);
+ else
+ emit_insn_before (seq, NEXT_INSN (entry_of_function ()));
pop_topmost_sequence ();
return temp;
}
/* 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)
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;
side-effects more the once. */
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;
side-effects more the once. */
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))
{
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);
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, false))
+ 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, false, 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;
stpcpy. */
static rtx
-expand_builtin_mempcpy(tree exp, rtx target, enum machine_mode mode)
+expand_builtin_mempcpy (tree exp, rtx target, enum machine_mode mode)
{
if (!validate_arglist (exp,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
rtx target, enum machine_mode mode, int endp)
{
/* If return value is ignored, transform mempcpy into memcpy. */
- if (target == const0_rtx)
+ if (target == const0_rtx && implicit_built_in_decls[BUILT_IN_MEMCPY])
{
tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ tree result = build_call_expr (fn, 3, dest, src, len);
- if (!fn)
- return NULL_RTX;
-
- return expand_expr (build_call_expr (fn, 3, dest, src, len),
- target, mode, EXPAND_NORMAL);
+ while (TREE_CODE (result) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ result = TREE_OPERAND (result, 1);
+ }
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
else
{
&& 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, false))
+ 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,
- false, 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;
src = CALL_EXPR_ARG (exp, 1);
/* If return value is ignored, transform stpcpy into strcpy. */
- if (target == const0_rtx)
+ if (target == const0_rtx && implicit_built_in_decls[BUILT_IN_STRCPY])
{
tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
- if (!fn)
- return NULL_RTX;
+ tree result = build_call_expr (fn, 2, dst, src);
- return expand_expr (build_call_expr (fn, 2, dst, src),
- target, mode, EXPAND_NORMAL);
+ STRIP_NOPS (result);
+ while (TREE_CODE (result) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ result = TREE_OPERAND (result, 1);
+ }
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
else
{
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, false))
+ 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, false, 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;
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,
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. */
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);
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;
- inform ("(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");
+ /* Before the abort, allow the evaluation of the va_list
+ expression to exit or longjmp. */
+ gimplify_and_add (valist, pre_p);
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);
{
/* 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);
rtx val, mem;
enum machine_mode old_mode;
+ if (code == NOT && warn_sync_nand)
+ {
+ tree fndecl = get_callee_fndecl (exp);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+ static bool warned_f_a_n, warned_n_a_f;
+
+ switch (fcode)
+ {
+ case BUILT_IN_FETCH_AND_NAND_1:
+ case BUILT_IN_FETCH_AND_NAND_2:
+ case BUILT_IN_FETCH_AND_NAND_4:
+ case BUILT_IN_FETCH_AND_NAND_8:
+ case BUILT_IN_FETCH_AND_NAND_16:
+
+ if (warned_f_a_n)
+ break;
+
+ fndecl = implicit_built_in_decls[BUILT_IN_FETCH_AND_NAND_N];
+ inform (input_location,
+ "%qD changed semantics in GCC 4.4", fndecl);
+ warned_f_a_n = true;
+ break;
+
+ case BUILT_IN_NAND_AND_FETCH_1:
+ case BUILT_IN_NAND_AND_FETCH_2:
+ case BUILT_IN_NAND_AND_FETCH_4:
+ case BUILT_IN_NAND_AND_FETCH_8:
+ case BUILT_IN_NAND_AND_FETCH_16:
+
+ if (warned_n_a_f)
+ break;
+
+ fndecl = implicit_built_in_decls[BUILT_IN_NAND_AND_FETCH_N];
+ inform (input_location,
+ "%qD changed semantics in GCC 4.4", fndecl);
+ warned_n_a_f = true;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
}
#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
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;
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;
}
}
while (TREE_CODE (inner) == COMPONENT_REF
|| TREE_CODE (inner) == ARRAY_REF);
- if (DECL_P (inner) && DECL_WEAK (inner))
+ if ((TREE_CODE (inner) == VAR_DECL
+ || TREE_CODE (inner) == FUNCTION_DECL)
+ && DECL_WEAK (inner))
return NULL_TREE;
}
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 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),
- *get_real_const (rv_sqrt2));
+ = 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 arg0 = CALL_EXPR_ARG (arg, 0);
tree tree_root;
/* The inner root was either sqrt or cbrt. */
- REAL_VALUE_TYPE dconstroot =
- BUILTIN_SQRT_P (fcode) ? dconsthalf : *get_real_const (rv_third);
+ /* This was a conditional expression but it triggered a bug
+ in Sun C 5.5. */
+ REAL_VALUE_TYPE dconstroot;
+ if (BUILTIN_SQRT_P (fcode))
+ dconstroot = dconsthalf;
+ else
+ dconstroot = 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), *get_real_const (rv_third));
+ 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 = *get_real_const (rv_third);
+ REAL_VALUE_TYPE dconstroot = dconst_third ();
SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
REAL_VALUE_TYPE dconstroot;
real_arithmetic (&dconstroot, MULT_EXPR,
- get_real_const (rv_third),
- get_real_const (rv_third));
+ 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),
- *get_real_const (rv_third));
+ = 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);
call = build_call_expr (fn, 1, arg0);
call = builtin_save_expr (call);
- return build2 (COMPOUND_EXPR, type,
+ return build2 (COMPOUND_EXPR, void_type_node,
build2 (MODIFY_EXPR, void_type_node,
build_fold_indirect_ref (arg1),
build1 (IMAGPART_EXPR, type, call)),
return NULL_TREE;
}
-/* Return true if EXPR is the real constant contained in VALUE. */
-
-static bool
-real_dconstp (tree expr, const REAL_VALUE_TYPE *value)
-{
- STRIP_NOPS (expr);
-
- return ((TREE_CODE (expr) == REAL_CST
- && !TREE_OVERFLOW (expr)
- && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), *value))
- || (TREE_CODE (expr) == COMPLEX_CST
- && real_dconstp (TREE_REALPART (expr), value)
- && real_zerop (TREE_IMAGPART (expr))));
-}
-
/* 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. */
tree res;
const enum built_in_function fcode = builtin_mathfn_code (arg);
- /* Optimize log(e) = 1.0. We're never passed an exact 'e',
- instead we'll look for 'e' truncated to MODE. So only do
- this if flag_unsafe_math_optimizations is set. */
- if (flag_unsafe_math_optimizations && func == mpfr_log)
- {
- const REAL_VALUE_TYPE e_truncated =
- real_value_truncate (TYPE_MODE (type), *get_real_const (rv_e));
- if (real_dconstp (arg, &e_truncated))
- return build_real (type, dconst1);
- }
-
/* Calculate the result when the argument is a constant. */
if ((res = do_mpfr_arg1 (arg, type, func, &dconst0, NULL, false)))
return res;
{
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),
- *get_real_const (rv_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(cbrt(x) -> (1/3)*logN(x). */
x = CALL_EXPR_ARG (arg, 0);
exponent = build_real (type, real_value_truncate (TYPE_MODE (type),
- *get_real_const (rv_third)));
+ 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), *get_real_const (rv_sqrt2));
+ = 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),
- *get_real_const (rv_third));
+ = real_value_truncate (TYPE_MODE (type), dconst_third ());
if (REAL_VALUES_EQUAL (c, dconstroot))
{
real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
if (real_identical (&c, &cint))
{
- /* Attempt to evaluate pow at compile-time. */
+ /* Attempt to evaluate pow at compile-time, unless this should
+ raise an exception. */
if (TREE_CODE (arg0) == REAL_CST
- && !TREE_OVERFLOW (arg0))
+ && !TREE_OVERFLOW (arg0)
+ && (n > 0
+ || (!flag_trapping_math && !flag_errno_math)
+ || !REAL_VALUES_EQUAL (TREE_REAL_CST (arg0), dconst0)))
{
REAL_VALUE_TYPE x;
bool inexact;
if (tree_expr_nonnegative_p (arg))
{
const REAL_VALUE_TYPE dconstroot
- = real_value_truncate (TYPE_MODE (type),
- *get_real_const (rv_third));
+ = 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);
else
{
tree srctype, desttype;
+ int src_align, dest_align;
+
if (endp == 3)
{
- int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
- int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+ src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
+ dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
/* Both DEST and SRC must be pointer types.
??? This is what old code did. Is the testing for pointer types
really mandatory?
If either SRC is readonly or length is 1, we can use memcpy. */
- if (dest_align && src_align
- && (readonly_data_expr (src)
- || (host_integerp (len, 1)
- && (MIN (src_align, dest_align) / BITS_PER_UNIT >=
- tree_low_cst (len, 1)))))
+ if (!dest_align || !src_align)
+ return NULL_TREE;
+ if (readonly_data_expr (src)
+ || (host_integerp (len, 1)
+ && (MIN (src_align, dest_align) / BITS_PER_UNIT
+ >= tree_low_cst (len, 1))))
{
tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
if (!fn)
return NULL_TREE;
return build_call_expr (fn, 3, dest, src, len);
}
+
+ /* If *src and *dest can't overlap, optimize into memcpy as well. */
+ srcvar = build_fold_indirect_ref (src);
+ destvar = build_fold_indirect_ref (dest);
+ if (srcvar
+ && !TREE_THIS_VOLATILE (srcvar)
+ && destvar
+ && !TREE_THIS_VOLATILE (destvar))
+ {
+ 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;
+
+ src_base = srcvar;
+ if (handled_component_p (src_base))
+ src_base = get_ref_base_and_extent (src_base, &src_offset,
+ &size, &maxsize);
+ dest_base = destvar;
+ if (handled_component_p (dest_base))
+ dest_base = get_ref_base_and_extent (dest_base, &dest_offset,
+ &size, &maxsize);
+ if (host_integerp (len, 1))
+ {
+ maxsize = tree_low_cst (len, 1);
+ if (maxsize
+ > INTTYPE_MAXIMUM (HOST_WIDE_INT) / BITS_PER_UNIT)
+ maxsize = -1;
+ else
+ maxsize *= BITS_PER_UNIT;
+ }
+ else
+ maxsize = -1;
+ 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) == INDIRECT_REF
+ && TREE_CODE (dest_base) == INDIRECT_REF)
+ {
+ if (! operand_equal_p (TREE_OPERAND (src_base, 0),
+ TREE_OPERAND (dest_base, 0), 0)
+ || ranges_overlap_p (src_offset, maxsize,
+ dest_offset, maxsize))
+ return NULL_TREE;
+ }
+ else
+ return NULL_TREE;
+
+ fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ if (!fn)
+ return NULL_TREE;
+ return build_call_expr (fn, 3, dest, src, len);
+ }
return NULL_TREE;
}
|| !TYPE_SIZE_UNIT (desttype)
|| TREE_CODE (TYPE_SIZE_UNIT (srctype)) != INTEGER_CST
|| TREE_CODE (TYPE_SIZE_UNIT (desttype)) != INTEGER_CST
- || !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len)
- || !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+ || TYPE_VOLATILE (srctype)
+ || TYPE_VOLATILE (desttype))
return NULL_TREE;
- if (get_pointer_alignment (dest, BIGGEST_ALIGNMENT)
- < (int) TYPE_ALIGN (desttype)
- || (get_pointer_alignment (src, BIGGEST_ALIGNMENT)
- < (int) TYPE_ALIGN (srctype)))
+ src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
+ dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+ if (dest_align < (int) TYPE_ALIGN (desttype)
+ || src_align < (int) TYPE_ALIGN (srctype))
return NULL_TREE;
if (!ignore)
dest = builtin_save_expr (dest);
- srcvar = build_fold_indirect_ref (src);
- if (TREE_THIS_VOLATILE (srcvar))
- return NULL_TREE;
- 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
- it use conflicting alias set to hold argument for the memcpy call.
- 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
- to char_var='t'; */
- || is_gimple_min_invariant (srcvar)
- || readonly_data_expr (src))
- return NULL_TREE;
+ srcvar = NULL_TREE;
+ if (tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
+ {
+ srcvar = build_fold_indirect_ref (src);
+ if (TREE_THIS_VOLATILE (srcvar))
+ return NULL_TREE;
+ else if (!tree_int_cst_equal (lang_hooks.expr_size (srcvar), len))
+ srcvar = 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 it use conflicting alias set to hold argument for the
+ memcpy call. This check is probably unnecessary with
+ -fno-strict-aliasing. Similarly for destvar. See also
+ PR29286. */
+ else if (!var_decl_component_p (srcvar))
+ srcvar = NULL_TREE;
+ }
+
+ destvar = NULL_TREE;
+ if (tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+ {
+ destvar = build_fold_indirect_ref (dest);
+ if (TREE_THIS_VOLATILE (destvar))
+ return NULL_TREE;
+ else if (!tree_int_cst_equal (lang_hooks.expr_size (destvar), len))
+ destvar = NULL_TREE;
+ else if (!var_decl_component_p (destvar))
+ destvar = NULL_TREE;
+ }
- destvar = build_fold_indirect_ref (dest);
- if (TREE_THIS_VOLATILE (destvar))
- return NULL_TREE;
- if (!tree_int_cst_equal (lang_hooks.expr_size (destvar), len))
- return NULL_TREE;
- if (!var_decl_component_p (destvar))
+ if (srcvar == NULL_TREE && destvar == NULL_TREE)
return NULL_TREE;
+ if (srcvar == NULL_TREE)
+ {
+ tree srcptype;
+ if (TREE_ADDRESSABLE (TREE_TYPE (destvar)))
+ return NULL_TREE;
+
+ srctype = build_qualified_type (desttype, 0);
+ if (src_align < (int) TYPE_ALIGN (srctype))
+ {
+ if (AGGREGATE_TYPE_P (srctype)
+ || SLOW_UNALIGNED_ACCESS (TYPE_MODE (srctype), src_align))
+ return NULL_TREE;
+
+ srctype = build_variant_type_copy (srctype);
+ TYPE_ALIGN (srctype) = src_align;
+ TYPE_USER_ALIGN (srctype) = 1;
+ TYPE_PACKED (srctype) = 1;
+ }
+ srcptype = build_pointer_type_for_mode (srctype, ptr_mode, true);
+ src = fold_convert (srcptype, src);
+ srcvar = build_fold_indirect_ref (src);
+ }
+ else if (destvar == NULL_TREE)
+ {
+ tree destptype;
+ if (TREE_ADDRESSABLE (TREE_TYPE (srcvar)))
+ return NULL_TREE;
+
+ desttype = build_qualified_type (srctype, 0);
+ if (dest_align < (int) TYPE_ALIGN (desttype))
+ {
+ if (AGGREGATE_TYPE_P (desttype)
+ || SLOW_UNALIGNED_ACCESS (TYPE_MODE (desttype), dest_align))
+ return NULL_TREE;
+
+ desttype = build_variant_type_copy (desttype);
+ TYPE_ALIGN (desttype) = dest_align;
+ TYPE_USER_ALIGN (desttype) = 1;
+ TYPE_PACKED (desttype) = 1;
+ }
+ destptype = build_pointer_type_for_mode (desttype, ptr_mode, true);
+ dest = fold_convert (destptype, dest);
+ destvar = build_fold_indirect_ref (dest);
+ }
+
if (srctype == desttype
|| (gimple_in_ssa_p (cfun)
&& useless_type_conversion_p (desttype, srctype)))
len = fold_build2 (MINUS_EXPR, TREE_TYPE (len), len,
ssize_int (1));
+ len = fold_convert (sizetype, len);
dest = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (dest), dest, len);
dest = fold_convert (type, dest);
if (expr)
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);
&dconstm1, NULL, false);
break;
-#if MPFR_VERSION >= MPFR_VERSION_NUM(2,3,0)
CASE_FLT_FN (BUILT_IN_J0):
if (validate_arg (arg0, REAL_TYPE))
return do_mpfr_arg1 (arg0, type, mpfr_j0,
return do_mpfr_arg1 (arg0, type, mpfr_y1,
&dconst0, NULL, false);
break;
-#endif
CASE_FLT_FN (BUILT_IN_NAN):
case BUILT_IN_NAND32:
switch (fcode)
{
-#if MPFR_VERSION >= MPFR_VERSION_NUM(2,3,0)
CASE_FLT_FN (BUILT_IN_JN):
if (validate_arg (arg0, INTEGER_TYPE)
&& validate_arg (arg1, REAL_TYPE))
&& validate_arg(arg1, POINTER_TYPE))
return do_mpfr_lgamma_r (arg0, arg1, type);
break;
-#endif
CASE_FLT_FN (BUILT_IN_ATAN2):
if (validate_arg (arg0, REAL_TYPE)
case BUILT_IN_STRCPY:
return fold_builtin_strcpy (fndecl, arg0, arg1, NULL_TREE);
+ case BUILT_IN_STPCPY:
+ if (ignore)
+ {
+ tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ if (!fn)
+ break;
+
+ return build_call_expr (fn, 2, arg0, arg1);
+ }
+ break;
+
case BUILT_IN_STRCMP:
return fold_builtin_strcmp (arg0, arg1);
return do_mpfr_arg3 (arg0, arg1, arg2, type, mpfr_fma);
break;
-#if MPFR_VERSION >= MPFR_VERSION_NUM(2,3,0)
CASE_FLT_FN (BUILT_IN_REMQUO):
if (validate_arg (arg0, REAL_TYPE)
&& validate_arg(arg1, REAL_TYPE)
&& validate_arg(arg2, POINTER_TYPE))
return do_mpfr_remquo (arg0, arg1, arg2);
break;
-#endif
case BUILT_IN_MEMSET:
return fold_builtin_memset (arg0, arg1, arg2, type, ignore);
}
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;
}
return NULL_TREE;
}
+/* 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
+ been inlined, otherwise e.g. -D_FORTIFY_SOURCE checking
+ might not be performed. */
+
+static bool
+avoid_folding_inline_builtin (tree fndecl)
+{
+ return (DECL_DECLARED_INLINE_P (fndecl)
+ && DECL_DISREGARD_INLINE_LIMITS (fndecl)
+ && cfun
+ && !cfun->always_inline_functions_inlined
+ && lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)));
+}
+
/* 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. */
return NULL_TREE;
}
+ if (avoid_folding_inline_builtin (fndecl))
+ 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);
if (CAN_HAVE_LOCATION_P (realret)
&& !EXPR_HAS_LOCATION (realret))
SET_EXPR_LOCATION (realret, EXPR_LOCATION (exp));
- return realret;
}
return ret;
}
&& DECL_FUNCTION_CODE (fndecl2) == BUILT_IN_VA_ARG_PACK)
return build_call_array (type, fn, n, argarray);
}
+ if (avoid_folding_inline_builtin (fndecl))
+ return build_call_array (type, fn, n, argarray);
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
{
tree arglist = NULL_TREE;
ret = targetm.fold_builtin (fndecl, arglist, false);
if (ret)
return ret;
+ return build_call_array (type, fn, n, argarray);
}
else if (n <= MAX_ARGS_TO_FOLD_BUILTIN)
{
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);
/* 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. */
+ 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_arglist (const_tree callexpr, ...)
+validate_gimple_arglist (const_gimple call, ...)
{
enum tree_code code;
bool res = 0;
va_list ap;
- const_call_expr_arg_iterator iter;
const_tree arg;
+ size_t i;
- va_start (ap, callexpr);
- init_const_call_expr_arg_iterator (callexpr, &iter);
+ va_start (ap, call);
+ i = 0;
do
{
case VOID_TYPE:
/* This signifies an endlink, if no arguments remain, return
true, otherwise return false. */
- res = !more_const_call_expr_args_p (&iter);
+ 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 = next_const_call_expr_arg (&iter);
+ arg = gimple_call_arg (call, i++);
if (!validate_arg (arg, code))
goto end;
break;
return res;
}
-/* Default target-specific builtin expander that does nothing. */
+/* 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. */
-rtx
-default_expand_builtin (tree exp ATTRIBUTE_UNUSED,
- rtx target ATTRIBUTE_UNUSED,
- rtx subtarget ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- int ignore ATTRIBUTE_UNUSED)
+bool
+validate_arglist (const_tree callexpr, ...)
{
- return NULL_RTX;
-}
-
+ enum tree_code code;
+ bool res = 0;
+ va_list ap;
+ const_call_expr_arg_iterator iter;
+ const_tree arg;
+
+ va_start (ap, callexpr);
+ init_const_call_expr_arg_iterator (callexpr, &iter);
+
+ 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 = !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_const_call_expr_arg (&iter);
+ 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;
+}
+
+/* Default target-specific builtin expander that does nothing. */
+
+rtx
+default_expand_builtin (tree exp ATTRIBUTE_UNUSED,
+ rtx target ATTRIBUTE_UNUSED,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ enum 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. */
if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
/* Evaluate and ignore both arguments in case either one has
side-effects. */
- return omit_two_operands (integer_type_node, integer_zero_node,
+ return omit_two_operands (size_type_node, size_zero_node,
s1, s2);
return NULL_TREE;
}
{
/* Evaluate and ignore argument s2 in case it has
side-effects. */
- return omit_one_operand (integer_type_node,
- integer_zero_node, s2);
+ return omit_one_operand (size_type_node,
+ size_zero_node, s2);
}
/* If the second argument is "", return __builtin_strlen(s1). */
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)
{
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:
if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
{
- warning (0, "%Kcall to %D will always overflow destination buffer",
- exp, get_callee_fndecl (exp));
+ warning_at (tree_nonartificial_location (exp),
+ 0, "%Kcall to %D will always overflow destination buffer",
+ exp, get_callee_fndecl (exp));
return NULL_RTX;
}
{
int is_strlen = 0;
tree len, size;
+ location_t loc = tree_nonartificial_location (exp);
switch (fcode)
{
src = c_strlen (src, 1);
if (! src || ! host_integerp (src, 1))
{
- warning (0, "%Kcall to %D might overflow destination buffer",
- exp, get_callee_fndecl (exp));
+ 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))
else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len))
return;
- warning (0, "%Kcall to %D will always overflow destination buffer",
- exp, get_callee_fndecl (exp));
+ 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
return;
if (! tree_int_cst_lt (len, size))
- {
- warning (0, "%Kcall to %D will always overflow destination buffer",
- exp, get_callee_fndecl (exp));
- }
+ 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. */
+
+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_at (tree_nonartificial_location (exp),
+ 0, "%Kattempt to free a non-heap object %qD", exp, arg);
+ else
+ warning_at (tree_nonartificial_location (exp),
+ 0, "%Kattempt to free a non-heap object", exp);
}
/* Fold a call to __builtin_object_size with arguments PTR and OST,
{
/* 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);
return result;
}
-#if MPFR_VERSION >= MPFR_VERSION_NUM(2,3,0)
/* If argument ARG1 is an INTEGER_CST and ARG2 is a REAL_CST, call the
two-argument mpfr order N Bessel function FUNC on them and return
the resulting value as a tree with type TYPE. The mpfr precision
&& 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);
+
+ if (avoid_folding_inline_builtin (fndecl))
+ return NULL_TREE;
+ /* 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;
+}