/* Expand builtin functions.
- Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 1988-2013 Free Software Foundation, Inc.
This file is part of GCC.
#include "target.h"
#include "langhooks.h"
#include "basic-block.h"
-#include "tree-mudflap.h"
-#include "tree-flow.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
#include "value-prof.h"
#include "diagnostic-core.h"
#include "builtins.h"
+#include "ubsan.h"
+#include "cilk.h"
-#ifndef PAD_VARARGS_DOWN
-#define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN
-#endif
static tree do_mpc_arg1 (tree, tree, int (*)(mpc_ptr, mpc_srcptr, mpc_rnd_t));
struct target_builtins default_target_builtins;
#endif
/* Define the names of the builtin function types and codes. */
-const char *const built_in_class_names[4]
+const char *const built_in_class_names[BUILT_IN_LAST]
= {"NOT_BUILT_IN", "BUILT_IN_FRONTEND", "BUILT_IN_MD", "BUILT_IN_NORMAL"};
#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM, COND) #X,
/* Setup an array of _DECL trees, make sure each element is
initialized to NULL_TREE. */
-tree built_in_decls[(int) END_BUILTINS];
-/* Declarations used when constructing the builtin implicitly in the compiler.
- It may be NULL_TREE when this is invalid (for instance runtime is not
- required to implement the function call in all cases). */
-tree implicit_built_in_decls[(int) END_BUILTINS];
+builtin_info_type builtin_info;
+
+/* Non-zero if __builtin_constant_p should be folded right away. */
+bool force_folding_builtin_constant_p;
static const char *c_getstr (tree);
static rtx c_readstr (const char *, enum machine_mode);
const REAL_VALUE_TYPE *, bool);
static tree do_mpfr_remquo (tree, tree, tree);
static tree do_mpfr_lgamma_r (tree, tree, tree);
+static void expand_builtin_sync_synchronize (void);
/* Return true if NAME starts with __builtin_ or __sync_. */
-bool
+static bool
is_builtin_name (const char *name)
{
if (strncmp (name, "__builtin_", 10) == 0)
return true;
if (strncmp (name, "__sync_", 7) == 0)
return true;
+ if (strncmp (name, "__atomic_", 9) == 0)
+ return true;
+ if (flag_enable_cilkplus
+ && (!strcmp (name, "__cilkrts_detach")
+ || !strcmp (name, "__cilkrts_pop_frame")))
+ return true;
return false;
}
return TREE_CODE (decl) == FUNCTION_DECL && DECL_BUILT_IN (decl);
}
+/* By default we assume that c99 functions are present at the runtime,
+ but sincos is not. */
+bool
+default_libc_has_function (enum function_class fn_class)
+{
+ if (fn_class == function_c94
+ || fn_class == function_c99_misc
+ || fn_class == function_c99_math_complex)
+ return true;
+
+ return false;
+}
+
+bool
+gnu_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED)
+{
+ return true;
+}
+
+bool
+no_c99_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED)
+{
+ return false;
+}
/* Return true if NODE should be considered for inline expansion regardless
of the optimization level. This means whenever a function is invoked with
return is_builtin_name (name);
}
-/* Compute values M and N such that M divides (address of EXP - N) and
- such that N < M. Store N in *BITPOSP and return M.
+/* Compute values M and N such that M divides (address of EXP - N) and such
+ that N < M. If these numbers can be determined, store M in alignp and N in
+ *BITPOSP and return true. Otherwise return false and store BITS_PER_UNIT to
+ *alignp and any bit-offset to *bitposp.
Note that the address (and thus the alignment) computed here is based
on the address to which a symbol resolves, whereas DECL_ALIGN is based
on the address at which an object is actually located. These two
addresses are not always the same. For example, on ARM targets,
the address &foo of a Thumb function foo() has the lowest bit set,
- whereas foo() itself starts on an even address. */
+ whereas foo() itself starts on an even address.
-unsigned int
-get_object_alignment_1 (tree exp, unsigned HOST_WIDE_INT *bitposp)
+ If ADDR_P is true we are taking the address of the memory reference EXP
+ and thus cannot rely on the access taking place. */
+
+static bool
+get_object_alignment_2 (tree exp, unsigned int *alignp,
+ unsigned HOST_WIDE_INT *bitposp, bool addr_p)
{
HOST_WIDE_INT bitsize, bitpos;
tree offset;
enum machine_mode mode;
int unsignedp, volatilep;
- unsigned int align, inner;
+ unsigned int align = BITS_PER_UNIT;
+ bool known_alignment = false;
/* Get the innermost object and the constant (bitpos) and possibly
variable (offset) offset of the access. */
/* Extract alignment information from the innermost object and
possibly adjust bitpos and offset. */
- if (TREE_CODE (exp) == CONST_DECL)
- exp = DECL_INITIAL (exp);
- if (DECL_P (exp)
- && TREE_CODE (exp) != LABEL_DECL)
- {
- if (TREE_CODE (exp) == FUNCTION_DECL)
- {
- /* Function addresses can encode extra information besides their
- alignment. However, if TARGET_PTRMEMFUNC_VBIT_LOCATION
- allows the low bit to be used as a virtual bit, we know
- that the address itself must be 2-byte aligned. */
- if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn)
- align = 2 * BITS_PER_UNIT;
- else
- align = BITS_PER_UNIT;
- }
- else
- align = DECL_ALIGN (exp);
+ if (TREE_CODE (exp) == FUNCTION_DECL)
+ {
+ /* Function addresses can encode extra information besides their
+ alignment. However, if TARGET_PTRMEMFUNC_VBIT_LOCATION
+ allows the low bit to be used as a virtual bit, we know
+ that the address itself must be at least 2-byte aligned. */
+ if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn)
+ align = 2 * BITS_PER_UNIT;
}
- else if (CONSTANT_CLASS_P (exp))
+ else if (TREE_CODE (exp) == LABEL_DECL)
+ ;
+ else if (TREE_CODE (exp) == CONST_DECL)
{
+ /* The alignment of a CONST_DECL is determined by its initializer. */
+ exp = DECL_INITIAL (exp);
align = TYPE_ALIGN (TREE_TYPE (exp));
#ifdef CONSTANT_ALIGNMENT
- align = (unsigned)CONSTANT_ALIGNMENT (exp, align);
+ if (CONSTANT_CLASS_P (exp))
+ align = (unsigned) CONSTANT_ALIGNMENT (exp, align);
#endif
+ known_alignment = true;
+ }
+ else if (DECL_P (exp))
+ {
+ align = DECL_ALIGN (exp);
+ known_alignment = true;
}
else if (TREE_CODE (exp) == VIEW_CONVERT_EXPR)
- align = TYPE_ALIGN (TREE_TYPE (exp));
- else if (TREE_CODE (exp) == INDIRECT_REF)
- align = TYPE_ALIGN (TREE_TYPE (exp));
- else if (TREE_CODE (exp) == MEM_REF)
+ {
+ align = TYPE_ALIGN (TREE_TYPE (exp));
+ }
+ else if (TREE_CODE (exp) == INDIRECT_REF
+ || TREE_CODE (exp) == MEM_REF
+ || TREE_CODE (exp) == TARGET_MEM_REF)
{
tree addr = TREE_OPERAND (exp, 0);
- struct ptr_info_def *pi;
+ unsigned ptr_align;
+ unsigned HOST_WIDE_INT ptr_bitpos;
+
if (TREE_CODE (addr) == BIT_AND_EXPR
&& TREE_CODE (TREE_OPERAND (addr, 1)) == INTEGER_CST)
{
align *= BITS_PER_UNIT;
addr = TREE_OPERAND (addr, 0);
}
- else
- align = BITS_PER_UNIT;
- if (TREE_CODE (addr) == SSA_NAME
- && (pi = SSA_NAME_PTR_INFO (addr)))
- {
- bitpos += (pi->misalign * BITS_PER_UNIT) & ~(align - 1);
- align = MAX (pi->align * BITS_PER_UNIT, align);
- }
- else if (TREE_CODE (addr) == ADDR_EXPR)
- align = MAX (align, get_object_alignment (TREE_OPERAND (addr, 0)));
- bitpos += mem_ref_offset (exp).low * BITS_PER_UNIT;
- }
- else if (TREE_CODE (exp) == TARGET_MEM_REF)
- {
- struct ptr_info_def *pi;
- tree addr = TMR_BASE (exp);
- if (TREE_CODE (addr) == BIT_AND_EXPR
- && TREE_CODE (TREE_OPERAND (addr, 1)) == INTEGER_CST)
+
+ known_alignment
+ = get_pointer_alignment_1 (addr, &ptr_align, &ptr_bitpos);
+ align = MAX (ptr_align, align);
+
+ /* The alignment of the pointer operand in a TARGET_MEM_REF
+ has to take the variable offset parts into account. */
+ if (TREE_CODE (exp) == TARGET_MEM_REF)
{
- align = (TREE_INT_CST_LOW (TREE_OPERAND (addr, 1))
- & -TREE_INT_CST_LOW (TREE_OPERAND (addr, 1)));
- align *= BITS_PER_UNIT;
- addr = TREE_OPERAND (addr, 0);
+ if (TMR_INDEX (exp))
+ {
+ unsigned HOST_WIDE_INT step = 1;
+ if (TMR_STEP (exp))
+ step = TREE_INT_CST_LOW (TMR_STEP (exp));
+ align = MIN (align, (step & -step) * BITS_PER_UNIT);
+ }
+ if (TMR_INDEX2 (exp))
+ align = BITS_PER_UNIT;
+ known_alignment = false;
}
+
+ /* When EXP is an actual memory reference then we can use
+ TYPE_ALIGN of a pointer indirection to derive alignment.
+ Do so only if get_pointer_alignment_1 did not reveal absolute
+ alignment knowledge and if using that alignment would
+ improve the situation. */
+ if (!addr_p && !known_alignment
+ && TYPE_ALIGN (TREE_TYPE (exp)) > align)
+ align = TYPE_ALIGN (TREE_TYPE (exp));
else
- align = BITS_PER_UNIT;
- if (TREE_CODE (addr) == SSA_NAME
- && (pi = SSA_NAME_PTR_INFO (addr)))
{
- bitpos += (pi->misalign * BITS_PER_UNIT) & ~(align - 1);
- align = MAX (pi->align * BITS_PER_UNIT, align);
+ /* Else adjust bitpos accordingly. */
+ bitpos += ptr_bitpos;
+ if (TREE_CODE (exp) == MEM_REF
+ || TREE_CODE (exp) == TARGET_MEM_REF)
+ bitpos += mem_ref_offset (exp).low * BITS_PER_UNIT;
}
- else if (TREE_CODE (addr) == ADDR_EXPR)
- align = MAX (align, get_object_alignment (TREE_OPERAND (addr, 0)));
- if (TMR_OFFSET (exp))
- bitpos += TREE_INT_CST_LOW (TMR_OFFSET (exp)) * BITS_PER_UNIT;
- if (TMR_INDEX (exp) && TMR_STEP (exp))
- {
- unsigned HOST_WIDE_INT step = TREE_INT_CST_LOW (TMR_STEP (exp));
- align = MIN (align, (step & -step) * BITS_PER_UNIT);
- }
- else if (TMR_INDEX (exp))
- align = BITS_PER_UNIT;
- if (TMR_INDEX2 (exp))
- align = BITS_PER_UNIT;
}
- else
- align = BITS_PER_UNIT;
+ else if (TREE_CODE (exp) == STRING_CST)
+ {
+ /* STRING_CST are the only constant objects we allow to be not
+ wrapped inside a CONST_DECL. */
+ align = TYPE_ALIGN (TREE_TYPE (exp));
+#ifdef CONSTANT_ALIGNMENT
+ if (CONSTANT_CLASS_P (exp))
+ align = (unsigned) CONSTANT_ALIGNMENT (exp, align);
+#endif
+ known_alignment = true;
+ }
/* If there is a non-constant offset part extract the maximum
alignment that can prevail. */
- inner = ~0U;
- while (offset)
+ if (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))
+ int trailing_zeros = tree_ctz (offset);
+ if (trailing_zeros < HOST_BITS_PER_INT)
{
- /* 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;
+ unsigned int inner = (1U << trailing_zeros) * BITS_PER_UNIT;
+ if (inner)
+ align = MIN (align, inner);
}
- offset = next_offset;
}
- /* Alignment is innermost object alignment adjusted by the constant
- and non-constant offset parts. */
- align = MIN (align, inner);
- bitpos = bitpos & (align - 1);
+ *alignp = align;
+ *bitposp = bitpos & (*alignp - 1);
+ return known_alignment;
+}
- *bitposp = bitpos;
- return align;
+/* For a memory reference expression EXP compute values M and N such that M
+ divides (&EXP - N) and such that N < M. If these numbers can be determined,
+ store M in alignp and N in *BITPOSP and return true. Otherwise return false
+ and store BITS_PER_UNIT to *alignp and any bit-offset to *bitposp. */
+
+bool
+get_object_alignment_1 (tree exp, unsigned int *alignp,
+ unsigned HOST_WIDE_INT *bitposp)
+{
+ return get_object_alignment_2 (exp, alignp, bitposp, false);
}
/* Return the alignment in bits of EXP, an object. */
unsigned HOST_WIDE_INT bitpos = 0;
unsigned int align;
- align = get_object_alignment_1 (exp, &bitpos);
+ get_object_alignment_1 (exp, &align, &bitpos);
/* align and bitpos now specify known low bits of the pointer.
ptr & (align - 1) == bitpos. */
if (bitpos != 0)
align = (bitpos & -bitpos);
-
return align;
}
-/* Return the alignment in bits of EXP, a pointer valued expression.
- The alignment returned is, by default, the alignment of the thing that
- EXP points to. If it is not a POINTER_TYPE, 0 is returned.
+/* For a pointer valued expression EXP compute values M and N such that M
+ divides (EXP - N) and such that N < M. If these numbers can be determined,
+ store M in alignp and N in *BITPOSP and return true. Return false if
+ the results are just a conservative approximation.
- Otherwise, look at the expression to see if we can do better, i.e., if the
- expression is actually pointing at an object whose alignment is tighter. */
+ If EXP is not a pointer, false is returned too. */
-unsigned int
-get_pointer_alignment (tree exp)
+bool
+get_pointer_alignment_1 (tree exp, unsigned int *alignp,
+ unsigned HOST_WIDE_INT *bitposp)
{
STRIP_NOPS (exp);
if (TREE_CODE (exp) == ADDR_EXPR)
- return get_object_alignment (TREE_OPERAND (exp, 0));
+ return get_object_alignment_2 (TREE_OPERAND (exp, 0),
+ alignp, bitposp, true);
else if (TREE_CODE (exp) == SSA_NAME
&& POINTER_TYPE_P (TREE_TYPE (exp)))
{
+ unsigned int ptr_align, ptr_misalign;
struct ptr_info_def *pi = SSA_NAME_PTR_INFO (exp);
- unsigned align;
- if (!pi)
- return BITS_PER_UNIT;
- if (pi->misalign != 0)
- align = (pi->misalign & -pi->misalign);
+
+ if (pi && get_ptr_info_alignment (pi, &ptr_align, &ptr_misalign))
+ {
+ *bitposp = ptr_misalign * BITS_PER_UNIT;
+ *alignp = ptr_align * BITS_PER_UNIT;
+ /* We cannot really tell whether this result is an approximation. */
+ return true;
+ }
else
- align = pi->align;
- return align * BITS_PER_UNIT;
+ {
+ *bitposp = 0;
+ *alignp = BITS_PER_UNIT;
+ return false;
+ }
}
+ else if (TREE_CODE (exp) == INTEGER_CST)
+ {
+ *alignp = BIGGEST_ALIGNMENT;
+ *bitposp = ((TREE_INT_CST_LOW (exp) * BITS_PER_UNIT)
+ & (BIGGEST_ALIGNMENT - 1));
+ return true;
+ }
+
+ *bitposp = 0;
+ *alignp = BITS_PER_UNIT;
+ return false;
+}
+
+/* Return the alignment in bits of EXP, a pointer valued expression.
+ The alignment returned is, by default, the alignment of the thing that
+ EXP points to. If it is not a POINTER_TYPE, 0 is returned.
+
+ Otherwise, look at the expression to see if we can do better, i.e., if the
+ expression is actually pointing at an object whose alignment is tighter. */
+
+unsigned int
+get_pointer_alignment (tree exp)
+{
+ unsigned HOST_WIDE_INT bitpos = 0;
+ unsigned int align;
+
+ get_pointer_alignment_1 (exp, &align, &bitpos);
- return POINTER_TYPE_P (TREE_TYPE (exp)) ? BITS_PER_UNIT : 0;
+ /* align and bitpos now specify known low bits of the pointer.
+ ptr & (align - 1) == bitpos. */
+
+ if (bitpos != 0)
+ align = (bitpos & -bitpos);
+
+ return align;
}
/* Compute the length of a C string. TREE_STRING_LENGTH is not the right
&& GET_MODE_SIZE (mode) >= UNITS_PER_WORD)
j = j + UNITS_PER_WORD - 2 * (j % UNITS_PER_WORD) - 1;
j *= BITS_PER_UNIT;
- gcc_assert (j < 2 * HOST_BITS_PER_WIDE_INT);
+ gcc_assert (j < HOST_BITS_PER_DOUBLE_INT);
if (ch)
ch = (unsigned char) str[i];
tem = RETURN_ADDR_RTX (count, tem);
#else
tem = memory_address (Pmode,
- plus_constant (tem, GET_MODE_SIZE (Pmode)));
+ plus_constant (Pmode, tem, GET_MODE_SIZE (Pmode)));
tem = gen_frame_mem (Pmode, tem);
#endif
return tem;
set_mem_alias_set (mem, setjmp_alias_set);
emit_move_insn (mem, targetm.builtin_setjmp_frame_value ());
- mem = gen_rtx_MEM (Pmode, plus_constant (buf_addr, GET_MODE_SIZE (Pmode))),
+ mem = gen_rtx_MEM (Pmode, plus_constant (Pmode, buf_addr,
+ GET_MODE_SIZE (Pmode))),
set_mem_alias_set (mem, setjmp_alias_set);
emit_move_insn (validize_mem (mem),
force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label)));
stack_save = gen_rtx_MEM (sa_mode,
- plus_constant (buf_addr,
+ plus_constant (Pmode, buf_addr,
2 * GET_MODE_SIZE (Pmode)));
set_mem_alias_set (stack_save, setjmp_alias_set);
emit_stack_save (SAVE_NONLOCAL, &stack_save);
}
/* Construct the trailing part of a __builtin_setjmp call. This is
- also called directly by the SJLJ exception handling code. */
+ also called directly by the SJLJ exception handling code.
+ If RECEIVER_LABEL is NULL, instead contruct a nonlocal goto handler. */
void
expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED)
{
rtx chain;
- /* Clobber the FP when we get here, so we have to make sure it's
+ /* Mark the FP as used when we get here, so we have to make sure it's
marked as used by this function. */
emit_use (hard_frame_pointer_rtx);
#ifdef HAVE_nonlocal_goto
if (! HAVE_nonlocal_goto)
#endif
- {
- 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_clobber (hard_frame_pointer_rtx);
- }
+ /* First adjust our frame pointer to its actual value. It was
+ previously set to the start of the virtual area corresponding to
+ the stacked variables when we branched here and now needs to be
+ adjusted to the actual hardware fp value.
+
+ Assignments to virtual registers are converted by
+ instantiate_virtual_regs into the corresponding assignment
+ to the underlying register (fp in this case) that makes
+ the original assignment true.
+ So the following insn will actually be decrementing fp by
+ STARTING_FRAME_OFFSET. */
+ emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
#if !HARD_FRAME_POINTER_IS_ARG_POINTER
if (fixed_regs[ARG_POINTER_REGNUM])
{
#ifdef ELIMINABLE_REGS
+ /* If the argument pointer can be eliminated in favor of the
+ frame pointer, we don't need to restore it. We assume here
+ that if such an elimination is present, it can always be used.
+ This is the case on all known machines; if we don't make this
+ assumption, we do unnecessary saving on many machines. */
size_t i;
static const struct elims {const int from, to;} elim_regs[] = ELIMINABLE_REGS;
#endif
#ifdef HAVE_builtin_setjmp_receiver
- if (HAVE_builtin_setjmp_receiver)
+ if (receiver_label != NULL && HAVE_builtin_setjmp_receiver)
emit_insn (gen_builtin_setjmp_receiver (receiver_label));
else
#endif
/* We must not allow the code we just generated to be reordered by
scheduling. Specifically, the update of the frame pointer must
- happen immediately, not later. */
+ happen immediately, not later. Similarly, we must block
+ (frame-related) register values to be used across this code. */
emit_insn (gen_blockage ());
}
#endif
{
fp = gen_rtx_MEM (Pmode, buf_addr);
- lab = gen_rtx_MEM (Pmode, plus_constant (buf_addr,
+ lab = gen_rtx_MEM (Pmode, plus_constant (Pmode, buf_addr,
GET_MODE_SIZE (Pmode)));
- stack = gen_rtx_MEM (sa_mode, plus_constant (buf_addr,
+ stack = gen_rtx_MEM (sa_mode, plus_constant (Pmode, buf_addr,
2 * GET_MODE_SIZE (Pmode)));
set_mem_alias_set (fp, setjmp_alias_set);
set_mem_alias_set (lab, setjmp_alias_set);
}
}
+static inline bool
+more_const_call_expr_args_p (const const_call_expr_arg_iterator *iter)
+{
+ return (iter->i < iter->n);
+}
+
+/* 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. */
+
+static bool
+validate_arglist (const_tree callexpr, ...)
+{
+ 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 = (enum tree_code) va_arg (ap, int);
+ 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;
+}
+
/* Expand a call to __builtin_nonlocal_goto. We're passed the target label
and the address of the save area. */
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)));
+ plus_constant (Pmode, r_save_area,
+ GET_MODE_SIZE (Pmode)));
crtl->has_nonlocal_goto = 1;
= gen_rtx_MEM (sa_mode,
memory_address
(sa_mode,
- plus_constant (buf_addr, 2 * GET_MODE_SIZE (Pmode))));
+ plus_constant (Pmode, buf_addr,
+ 2 * GET_MODE_SIZE (Pmode))));
emit_stack_save (SAVE_NONLOCAL, &stack_save);
}
{
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. */
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
- we can. First remove any nops. */
+ First remove any nops. */
while (CONVERT_EXPR_P (exp)
&& POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0))))
exp = TREE_OPERAND (exp, 0);
- 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);
- else
- exp = NULL;
-
- /* Honor attributes derived from exp, except for the alias set
- (as builtin stringops may alias with anything) and the size
- (as stringops may access multiple array elements). */
- if (exp)
- {
+ /* Build a MEM_REF representing the whole accessed area as a byte blob,
+ (as builtin stringops may alias with anything). */
+ exp = fold_build2 (MEM_REF,
+ build_array_type (char_type_node,
+ build_range_type (sizetype,
+ size_one_node, len)),
+ exp, build_int_cst (ptr_type_node, 0));
+
+ /* If the MEM_REF has no acceptable address, try to get the base object
+ from the original address we got, and build an all-aliasing
+ unknown-sized access to that one. */
+ if (is_gimple_mem_ref_addr (TREE_OPERAND (exp, 0)))
+ set_mem_attributes (mem, exp, 0);
+ else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+ && (exp = get_base_address (TREE_OPERAND (TREE_OPERAND (exp, 0),
+ 0))))
+ {
+ exp = build_fold_addr_expr (exp);
+ exp = fold_build2 (MEM_REF,
+ build_array_type (char_type_node,
+ build_range_type (sizetype,
+ size_zero_node,
+ NULL)),
+ exp, build_int_cst (ptr_type_node, 0));
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
- memory accessed by the string or memory builtin will fit
- within the field. */
- if (MEM_EXPR (mem) && TREE_CODE (MEM_EXPR (mem)) == COMPONENT_REF)
- {
- tree mem_expr = MEM_EXPR (mem);
- HOST_WIDE_INT offset = -1, length = -1;
- tree inner = exp;
-
- while (TREE_CODE (inner) == ARRAY_REF
- || CONVERT_EXPR_P (inner)
- || TREE_CODE (inner) == VIEW_CONVERT_EXPR
- || TREE_CODE (inner) == SAVE_EXPR)
- inner = TREE_OPERAND (inner, 0);
-
- gcc_assert (TREE_CODE (inner) == COMPONENT_REF);
-
- if (MEM_OFFSET_KNOWN_P (mem))
- offset = MEM_OFFSET (mem);
-
- if (offset >= 0 && len && host_integerp (len, 0))
- length = tree_low_cst (len, 0);
-
- while (TREE_CODE (inner) == COMPONENT_REF)
- {
- tree field = TREE_OPERAND (inner, 1);
- 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
- && DECL_SIZE_UNIT (field)
- && host_integerp (DECL_SIZE_UNIT (field), 0))
- {
- HOST_WIDE_INT size
- = TREE_INT_CST_LOW (DECL_SIZE_UNIT (field));
- if (offset <= size
- && length <= size
- && offset + length <= size)
- break;
- }
-
- if (offset >= 0
- && host_integerp (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
- {
- offset = -1;
- length = -1;
- }
-
- mem_expr = TREE_OPERAND (mem_expr, 0);
- inner = TREE_OPERAND (inner, 0);
- }
-
- if (mem_expr == NULL)
- offset = -1;
- if (mem_expr != MEM_EXPR (mem))
- {
- set_mem_expr (mem, mem_expr);
- if (offset >= 0)
- set_mem_offset (mem, offset);
- else
- clear_mem_offset (mem);
- }
- }
- set_mem_alias_set (mem, 0);
- clear_mem_size (mem);
}
-
+ set_mem_alias_set (mem, 0);
return mem;
}
\f
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, crtl->args.pretend_args_size),
+ = force_operand (plus_constant (Pmode, tem, crtl->args.pretend_args_size),
NULL_RTX);
#endif
emit_move_insn (adjust_address (registers, Pmode, 0), tem);
dest = virtual_outgoing_args_rtx;
#ifndef STACK_GROWS_DOWNWARD
if (CONST_INT_P (argsize))
- dest = plus_constant (dest, -INTVAL (argsize));
+ dest = plus_constant (Pmode, dest, -INTVAL (argsize));
else
dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize));
#endif
else
#endif
emit_stack_restore (SAVE_BLOCK, old_stack_level);
- fixup_args_size_notes (call_insn, get_last_insn(), 0);
+ fixup_args_size_notes (call_insn, get_last_insn (), 0);
OK_DEFER_POP;
fcode = BUILT_IN_MATHFN##_R; fcodef = BUILT_IN_MATHFN##F_R ; \
fcodel = BUILT_IN_MATHFN##L_R ; break;
-/* Return mathematic function equivalent to FN but operating directly
- 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. */
+/* Return mathematic function equivalent to FN but operating directly on TYPE,
+ if available. If IMPLICIT is true use the implicit builtin declaration,
+ otherwise use the explicit declaration. If we can't do the conversion,
+ return zero. */
static tree
-mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit)
+mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit_p)
{
- tree const *const fn_arr
- = implicit ? implicit_built_in_decls : built_in_decls;
- enum built_in_function fcode, fcodef, fcodel;
+ enum built_in_function fcode, fcodef, fcodel, fcode2;
switch (fn)
{
}
if (TYPE_MAIN_VARIANT (type) == double_type_node)
- return fn_arr[fcode];
+ fcode2 = fcode;
else if (TYPE_MAIN_VARIANT (type) == float_type_node)
- return fn_arr[fcodef];
+ fcode2 = fcodef;
else if (TYPE_MAIN_VARIANT (type) == long_double_type_node)
- return fn_arr[fcodel];
+ fcode2 = fcodel;
else
return NULL_TREE;
+
+ if (implicit_p && !builtin_decl_implicit_p (fcode2))
+ return NULL_TREE;
+
+ return builtin_decl_explicit (fcode2);
}
/* Like mathfn_built_in_1(), but always use the implicit array. */
rtx errno_rtx
= gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno"));
#endif
- emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM));
+ emit_move_insn (errno_rtx,
+ gen_int_mode (TARGET_EDOM, GET_MODE (errno_rtx)));
emit_label (lab);
return;
}
tree fndecl = get_callee_fndecl (exp);
enum machine_mode mode;
bool errno_set = false;
+ bool try_widening = false;
tree arg;
if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
{
CASE_FLT_FN (BUILT_IN_SQRT):
errno_set = ! tree_expr_nonnegative_p (arg);
+ try_widening = true;
builtin_optab = sqrt_optab;
break;
CASE_FLT_FN (BUILT_IN_EXP):
if (! flag_errno_math || ! HONOR_NANS (mode))
errno_set = false;
- /* Before working hard, check whether the instruction is available. */
- if (optab_handler (builtin_optab, mode) != CODE_FOR_nothing
+ /* Before working hard, check whether the instruction is available, but try
+ to widen the mode for specific operations. */
+ if ((optab_handler (builtin_optab, mode) != CODE_FOR_nothing
+ || (try_widening && !excess_precision_type (TREE_TYPE (exp))))
&& (!errno_set || !optimize_insn_for_size_p ()))
{
- target = gen_reg_rtx (mode);
+ rtx result = 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
start_sequence ();
- /* Compute into TARGET.
- Set TARGET to wherever the result comes back. */
- target = expand_unop (mode, builtin_optab, op0, target, 0);
+ /* Compute into RESULT.
+ Set RESULT to wherever the result comes back. */
+ result = expand_unop (mode, builtin_optab, op0, result, 0);
- if (target != 0)
+ if (result != 0)
{
if (errno_set)
- expand_errno_check (exp, target);
+ expand_errno_check (exp, result);
/* Output the entire sequence. */
insns = get_insns ();
end_sequence ();
emit_insn (insns);
- return target;
+ return result;
}
/* If we were unable to expand via the builtin, stop the sequence
expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
{
optab builtin_optab;
- rtx op0, op1, insns;
+ rtx op0, op1, insns, result;
int op1_type = REAL_TYPE;
tree fndecl = get_callee_fndecl (exp);
tree arg0, arg1;
if (optab_handler (builtin_optab, mode) == CODE_FOR_nothing)
return NULL_RTX;
- target = gen_reg_rtx (mode);
+ result = gen_reg_rtx (mode);
if (! flag_errno_math || ! HONOR_NANS (mode))
errno_set = false;
start_sequence ();
- /* Compute into TARGET.
- Set TARGET to wherever the result comes back. */
- target = expand_binop (mode, builtin_optab, op0, op1,
- target, 0, OPTAB_DIRECT);
+ /* Compute into RESULT.
+ Set RESULT to wherever the result comes back. */
+ result = expand_binop (mode, builtin_optab, op0, op1,
+ result, 0, OPTAB_DIRECT);
/* If we were unable to expand via the builtin, stop the sequence
(without outputting the insns) and call to the library function
with the stabilized argument list. */
- if (target == 0)
+ if (result == 0)
{
end_sequence ();
return expand_call (exp, target, target == const0_rtx);
}
if (errno_set)
- expand_errno_check (exp, target);
+ expand_errno_check (exp, result);
/* Output the entire sequence. */
insns = get_insns ();
end_sequence ();
emit_insn (insns);
- return target;
+ return result;
}
/* Expand a call to the builtin trinary math functions (fma).
expand_builtin_mathfn_ternary (tree exp, rtx target, rtx subtarget)
{
optab builtin_optab;
- rtx op0, op1, op2, insns;
+ rtx op0, op1, op2, insns, result;
tree fndecl = get_callee_fndecl (exp);
tree arg0, arg1, arg2;
enum machine_mode mode;
if (optab_handler (builtin_optab, mode) == CODE_FOR_nothing)
return NULL_RTX;
- target = gen_reg_rtx (mode);
+ result = gen_reg_rtx (mode);
/* Always stabilize the argument list. */
CALL_EXPR_ARG (exp, 0) = arg0 = builtin_save_expr (arg0);
start_sequence ();
- /* Compute into TARGET.
- Set TARGET to wherever the result comes back. */
- target = expand_ternary_op (mode, builtin_optab, op0, op1, op2,
- target, 0);
+ /* Compute into RESULT.
+ Set RESULT to wherever the result comes back. */
+ result = expand_ternary_op (mode, builtin_optab, op0, op1, op2,
+ result, 0);
/* If we were unable to expand via the builtin, stop the sequence
(without outputting the insns) and call to the library function
with the stabilized argument list. */
- if (target == 0)
+ if (result == 0)
{
end_sequence ();
return expand_call (exp, target, target == const0_rtx);
end_sequence ();
emit_insn (insns);
- return target;
+ return result;
}
/* Expand a call to the builtin sin and cos math functions.
/* Before working hard, check whether the instruction is available. */
if (optab_handler (builtin_optab, mode) != CODE_FOR_nothing)
{
- target = gen_reg_rtx (mode);
+ rtx result = 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
start_sequence ();
- /* Compute into TARGET.
- Set TARGET to wherever the result comes back. */
+ /* Compute into RESULT.
+ Set RESULT to wherever the result comes back. */
if (builtin_optab == sincos_optab)
{
- int result;
+ int ok;
switch (DECL_FUNCTION_CODE (fndecl))
{
CASE_FLT_FN (BUILT_IN_SIN):
- result = expand_twoval_unop (builtin_optab, op0, 0, target, 0);
+ ok = expand_twoval_unop (builtin_optab, op0, 0, result, 0);
break;
CASE_FLT_FN (BUILT_IN_COS):
- result = expand_twoval_unop (builtin_optab, op0, target, 0, 0);
+ ok = expand_twoval_unop (builtin_optab, op0, result, 0, 0);
break;
default:
gcc_unreachable ();
}
- gcc_assert (result);
+ gcc_assert (ok);
}
else
- {
- target = expand_unop (mode, builtin_optab, op0, target, 0);
- }
+ result = expand_unop (mode, builtin_optab, op0, result, 0);
- if (target != 0)
+ if (result != 0)
{
/* Output the entire sequence. */
insns = get_insns ();
end_sequence ();
emit_insn (insns);
- return target;
+ return result;
}
/* If we were unable to expand via the builtin, stop the sequence
end_sequence ();
}
- target = expand_call (exp, target, target == const0_rtx);
-
- return target;
+ return expand_call (exp, target, target == const0_rtx);
}
/* Given an interclass math builtin decl FNDECL and it's argument ARG
interclass_mathfn_icode (tree arg, tree fndecl)
{
bool errno_set = false;
- optab builtin_optab = 0;
+ optab builtin_optab = unknown_optab;
enum machine_mode mode;
switch (DECL_FUNCTION_CODE (fndecl))
/* Compute into op1 and op2. */
expand_twoval_unop (sincos_optab, op0, op2, op1, 0);
}
- else if (TARGET_HAS_SINCOS)
+ else if (targetm.libc_has_function (function_sincos))
{
tree call, fn = NULL_TREE;
tree top1, top2;
rtx op1a, op2a;
if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPIF)
- fn = built_in_decls[BUILT_IN_SINCOSF];
+ fn = builtin_decl_explicit (BUILT_IN_SINCOSF);
else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPI)
- fn = built_in_decls[BUILT_IN_SINCOS];
+ fn = builtin_decl_explicit (BUILT_IN_SINCOS);
else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPIL)
- fn = built_in_decls[BUILT_IN_SINCOSL];
+ fn = builtin_decl_explicit (BUILT_IN_SINCOSL);
else
gcc_unreachable ();
- op1 = assign_temp (TREE_TYPE (arg), 0, 1, 1);
- op2 = assign_temp (TREE_TYPE (arg), 0, 1, 1);
- op1a = copy_to_mode_reg (Pmode, XEXP (op1, 0));
- op2a = copy_to_mode_reg (Pmode, XEXP (op2, 0));
+ op1 = assign_temp (TREE_TYPE (arg), 1, 1);
+ op2 = assign_temp (TREE_TYPE (arg), 1, 1);
+ op1a = copy_addr_to_reg (XEXP (op1, 0));
+ op2a = copy_addr_to_reg (XEXP (op2, 0));
top1 = make_tree (build_pointer_type (TREE_TYPE (arg)), op1a);
top2 = make_tree (build_pointer_type (TREE_TYPE (arg)), op2a);
tree ctype = build_complex_type (type);
if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPIF)
- fn = built_in_decls[BUILT_IN_CEXPF];
+ fn = builtin_decl_explicit (BUILT_IN_CEXPF);
else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPI)
- fn = built_in_decls[BUILT_IN_CEXP];
+ fn = builtin_decl_explicit (BUILT_IN_CEXP);
else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPIL)
- fn = built_in_decls[BUILT_IN_CEXPL];
+ fn = builtin_decl_explicit (BUILT_IN_CEXPL);
else
gcc_unreachable ();
exp = build_call_nofold_loc (EXPR_LOCATION (exp), fallback_fndecl, 1, arg);
tmp = expand_normal (exp);
+ tmp = maybe_emit_group_store (tmp, TREE_TYPE (exp));
/* Truncate the result of floating point optab to integer
via expand_fix (). */
tree fndecl = get_callee_fndecl (exp);
tree arg;
enum machine_mode mode;
-
- /* There's no easy way to detect the case we need to set EDOM. */
- if (flag_errno_math)
- return NULL_RTX;
+ enum built_in_function fallback_fn = BUILT_IN_NONE;
if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
gcc_unreachable ();
switch (DECL_FUNCTION_CODE (fndecl))
{
CASE_FLT_FN (BUILT_IN_IRINT):
+ fallback_fn = BUILT_IN_LRINT;
+ /* FALLTHRU */
CASE_FLT_FN (BUILT_IN_LRINT):
CASE_FLT_FN (BUILT_IN_LLRINT):
- builtin_optab = lrint_optab; break;
+ builtin_optab = lrint_optab;
+ break;
CASE_FLT_FN (BUILT_IN_IROUND):
+ fallback_fn = BUILT_IN_LROUND;
+ /* FALLTHRU */
CASE_FLT_FN (BUILT_IN_LROUND):
CASE_FLT_FN (BUILT_IN_LLROUND):
- builtin_optab = lround_optab; break;
+ builtin_optab = lround_optab;
+ break;
default:
gcc_unreachable ();
}
+ /* There's no easy way to detect the case we need to set EDOM. */
+ if (flag_errno_math && fallback_fn == BUILT_IN_NONE)
+ return NULL_RTX;
+
/* Make a suitable register to place result in. */
mode = TYPE_MODE (TREE_TYPE (exp));
- 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. */
- CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
+ /* There's no easy way to detect the case we need to set EDOM. */
+ if (!flag_errno_math)
+ {
+ rtx result = gen_reg_rtx (mode);
- op0 = expand_expr (arg, NULL, VOIDmode, EXPAND_NORMAL);
+ /* 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. */
+ CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
- start_sequence ();
+ op0 = expand_expr (arg, NULL, VOIDmode, EXPAND_NORMAL);
- if (expand_sfix_optab (target, op0, builtin_optab))
- {
- /* Output the entire sequence. */
- insns = get_insns ();
- end_sequence ();
- emit_insn (insns);
- return target;
- }
+ start_sequence ();
- /* If we were unable to expand via the builtin, stop the sequence
- (without outputting the insns) and call to the library function
- with the stabilized argument list. */
- end_sequence ();
+ if (expand_sfix_optab (result, op0, builtin_optab))
+ {
+ /* Output the entire sequence. */
+ insns = get_insns ();
+ end_sequence ();
+ emit_insn (insns);
+ return result;
+ }
- target = expand_call (exp, target, target == const0_rtx);
+ /* If we were unable to expand via the builtin, stop the sequence
+ (without outputting the insns) and call to the library function
+ with the stabilized argument list. */
+ end_sequence ();
+ }
- return target;
+ if (fallback_fn != BUILT_IN_NONE)
+ {
+ /* Fall back to rounding to long int. Use implicit_p 0 - for non-C99
+ targets, (int) round (x) should never be transformed into
+ BUILT_IN_IROUND and if __builtin_iround is called directly, emit
+ a call to lround in the hope that the target provides at least some
+ C99 functions. This should result in the best user experience for
+ not full C99 targets. */
+ tree fallback_fndecl = mathfn_built_in_1 (TREE_TYPE (arg),
+ fallback_fn, 0);
+
+ exp = build_call_nofold_loc (EXPR_LOCATION (exp),
+ fallback_fndecl, 1, arg);
+
+ target = expand_call (exp, NULL_RTX, target == const0_rtx);
+ target = maybe_emit_group_store (target, TREE_TYPE (exp));
+ return convert_to_mode (mode, target, 0);
+ }
+
+ return expand_call (exp, target, target == const0_rtx);
}
/* Expand a call to the powi built-in mathematical function. Return NULL_RTX if
rtx target, enum machine_mode mode, int endp)
{
/* If return value is ignored, transform mempcpy into memcpy. */
- if (target == const0_rtx && implicit_built_in_decls[BUILT_IN_MEMCPY])
+ if (target == const0_rtx && builtin_decl_implicit_p (BUILT_IN_MEMCPY))
{
- tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
tree result = build_call_nofold_loc (UNKNOWN_LOCATION, fn, 3,
dest, src, len);
return expand_expr (result, target, mode, EXPAND_NORMAL);
adjust it. */
if (endp == 1)
{
- rtx tem = plus_constant (gen_lowpart (GET_MODE (target), target), 1);
+ rtx tem = plus_constant (GET_MODE (target),
+ gen_lowpart (GET_MODE (target), target), 1);
emit_move_insn (target, force_operand (tem, NULL_RTX));
}
}
src = CALL_EXPR_ARG (exp, 1);
/* If return value is ignored, transform stpcpy into strcpy. */
- if (target == const0_rtx && implicit_built_in_decls[BUILT_IN_STRCPY])
+ if (target == const0_rtx && builtin_decl_implicit (BUILT_IN_STRCPY))
{
- tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
tree result = build_call_nofold_loc (loc, fn, 2, dst, src);
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
if (GET_MODE (target) != GET_MODE (ret))
ret = gen_lowpart (GET_MODE (target), ret);
- ret = plus_constant (ret, INTVAL (len_rtx));
+ ret = plus_constant (GET_MODE (ret), ret, INTVAL (len_rtx));
ret = emit_move_insn (target, force_operand (ret, NULL_RTX));
gcc_assert (ret);
if (INDIRECT_REF_P (type))
type = TREE_TYPE (type);
- else if (POINTER_TYPE_P (type) && POINTER_TYPE_P (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;
return const0_rtx;
}
-/* The "standard" implementation of va_arg: read the value from the
- current (padded) address and increment by the (padded) size. */
-
-tree
-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;
- bool indirect;
-
-#ifdef ARGS_GROW_DOWNWARD
- /* All of the alignment and movement below is for args-grow-up machines.
- As of 2004, there are only 3 ARGS_GROW_DOWNWARD targets, and they all
- implement their own specialized gimplify_va_arg_expr routines. */
- gcc_unreachable ();
-#endif
-
- indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false);
- if (indirect)
- type = build_pointer_type (type);
-
- align = PARM_BOUNDARY / BITS_PER_UNIT;
- boundary = targetm.calls.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);
-
- /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually
- requires greater alignment, we must perform dynamic alignment. */
- if (boundary > align
- && !integer_zerop (TYPE_SIZE (type)))
- {
- t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
- fold_build_pointer_plus_hwi (valist_tmp, boundary - 1));
- gimplify_and_add (t, pre_p);
-
- t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
- fold_build2 (BIT_AND_EXPR, TREE_TYPE (valist),
- valist_tmp,
- build_int_cst (TREE_TYPE (valist), -boundary)));
- gimplify_and_add (t, pre_p);
- }
- else
- boundary = align;
-
- /* 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 dereferencing the pointer. */
- boundary *= BITS_PER_UNIT;
- if (boundary < TYPE_ALIGN (type))
- {
- type = build_variant_type_copy (type);
- TYPE_ALIGN (type) = boundary;
- }
-
- /* Compute the rounded size of the type. */
- type_size = size_in_bytes (type);
- rounded_size = round_up (type_size, align);
-
- /* Reduce rounded_size so it's sharable with the postqueue. */
- gimplify_expr (&rounded_size, pre_p, post_p, is_gimple_val, fb_rvalue);
-
- /* Get AP. */
- addr = valist_tmp;
- if (PAD_VARARGS_DOWN && !integer_zerop (rounded_size))
- {
- /* Small args are padded downward. */
- t = fold_build2_loc (input_location, GT_EXPR, sizetype,
- rounded_size, size_int (align));
- t = fold_build3 (COND_EXPR, sizetype, t, size_zero_node,
- size_binop (MINUS_EXPR, rounded_size, type_size));
- addr = fold_build_pointer_plus (addr, t);
- }
-
- /* Compute new value for AP. */
- t = fold_build_pointer_plus (valist_tmp, rounded_size);
- t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
- gimplify_and_add (t, pre_p);
-
- addr = fold_convert (build_pointer_type (type), addr);
-
- if (indirect)
- addr = build_va_arg_indirect_ref (addr);
-
- return build_va_arg_indirect_ref (addr);
-}
-
-/* Build an indirect-ref expression over the given TREE, which represents a
- piece of a va_arg() expansion. */
-tree
-build_va_arg_indirect_ref (tree addr)
-{
- addr = build_simple_mem_ref_loc (EXPR_LOCATION (addr), addr);
-
- if (flag_mudflap) /* Don't instrument va_arg INDIRECT_REF. */
- mf_mark (addr);
-
- return addr;
-}
-
-/* Return a dummy expression of type TYPE in order to keep going after an
- error. */
-
-static tree
-dummy_object (tree type)
-{
- tree t = build_int_cst (build_pointer_type (type), 0);
- return build2 (MEM_REF, type, t, t);
-}
-
-/* Gimplify __builtin_va_arg, aka VA_ARG_EXPR, which is not really a
- builtin function, but a very special sort of operator. */
-
-enum gimplify_status
-gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
-{
- tree promoted_type, have_va_type;
- tree valist = TREE_OPERAND (*expr_p, 0);
- tree type = TREE_TYPE (*expr_p);
- tree t;
- location_t loc = EXPR_LOCATION (*expr_p);
-
- /* Verify that valist is of the proper type. */
- 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 (have_va_type == NULL_TREE)
- {
- error_at (loc, "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. */
- 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. */
- warned = warning_at (loc, 0,
- "%qT is promoted to %qT when passed through %<...%>",
- type, promoted_type);
- if (!gave_help && warned)
- {
- gave_help = true;
- inform (loc, "(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. */
- if (warned)
- inform (loc, "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_loc (loc,
- implicit_built_in_decls[BUILT_IN_TRAP], 0);
- 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. */
- *expr_p = dummy_object (type);
- return GS_ALL_DONE;
- }
- else
- {
- /* Make it easier for the backends by protecting the valist argument
- from multiple evaluations. */
- if (TREE_CODE (have_va_type) == ARRAY_TYPE)
- {
- /* For this case, the backends will be expecting a pointer to
- 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 (have_va_type));
- valist = fold_convert_loc (loc, p1,
- build_fold_addr_expr_loc (loc, valist));
- }
-
- 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
- assert this is non-null. */
- return GS_ALL_DONE;
-
- *expr_p = targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p);
- return GS_OK;
- }
-}
-
/* Expand EXP, a call to __builtin_va_end. */
static rtx
if (!REG_P (tem)
&& ! CONSTANT_P (tem))
- tem = copy_to_mode_reg (Pmode, tem);
+ tem = copy_addr_to_reg (tem);
return tem;
}
}
{
rtx op0;
rtx result;
+ bool valid_arglist;
+ unsigned int align;
+ bool alloca_with_align = (DECL_FUNCTION_CODE (get_callee_fndecl (exp))
+ == BUILT_IN_ALLOCA_WITH_ALIGN);
- /* Emit normal call if marked not-inlineable. */
- if (CALL_CANNOT_INLINE_P (exp))
- return NULL_RTX;
+ valid_arglist
+ = (alloca_with_align
+ ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)
+ : validate_arglist (exp, INTEGER_TYPE, VOID_TYPE));
- if (!validate_arglist (exp, INTEGER_TYPE, VOID_TYPE))
+ if (!valid_arglist)
return NULL_RTX;
/* Compute the argument. */
op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
+ /* Compute the alignment. */
+ align = (alloca_with_align
+ ? TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 1))
+ : BIGGEST_ALIGNMENT);
+
/* Allocate the desired space. */
- result = allocate_dynamic_stack_space (op0, 0, BIGGEST_ALIGNMENT,
- cannot_accumulate);
+ result = allocate_dynamic_stack_space (op0, 0, align, cannot_accumulate);
result = convert_memory_address (ptr_mode, result);
return result;
}
-/* Expand a call to a bswap builtin with argument ARG0. MODE
- is the mode to expand with. */
+/* Expand a call to bswap builtin in EXP.
+ Return NULL_RTX if a normal call should be emitted rather than expanding the
+ function in-line. If convenient, the result should be placed in TARGET.
+ SUBTARGET may be used as the target for computing one of EXP's operands. */
static rtx
-expand_builtin_bswap (tree exp, rtx target, rtx subtarget)
+expand_builtin_bswap (enum machine_mode target_mode, tree exp, rtx target,
+ rtx subtarget)
{
- enum machine_mode mode;
tree arg;
rtx op0;
return NULL_RTX;
arg = CALL_EXPR_ARG (exp, 0);
- mode = TYPE_MODE (TREE_TYPE (arg));
- op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
+ op0 = expand_expr (arg,
+ subtarget && GET_MODE (subtarget) == target_mode
+ ? subtarget : NULL_RTX,
+ target_mode, EXPAND_NORMAL);
+ if (GET_MODE (op0) != target_mode)
+ op0 = convert_to_mode (target_mode, op0, 1);
- target = expand_unop (mode, bswap_optab, op0, target, 1);
+ target = expand_unop (target_mode, bswap_optab, op0, target, 1);
gcc_assert (target);
- return convert_to_mode (mode, target, 0);
+ return convert_to_mode (target_mode, target, 1);
}
/* Expand a call to a unary builtin in EXP.
{
#ifdef HAVE_trap
if (HAVE_trap)
- emit_insn (gen_trap ());
+ {
+ rtx insn = emit_insn (gen_trap ());
+ /* For trap insns when not accumulating outgoing args force
+ REG_ARGS_SIZE note to prevent crossjumping of calls with
+ different args sizes. */
+ if (!ACCUMULATE_OUTGOING_ARGS)
+ add_reg_note (insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta));
+ }
else
#endif
emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0);
/* Round address up to desired boundary. */
temp = gen_reg_rtx (Pmode);
- addend = GEN_INT (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1);
- mask = GEN_INT (-TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT);
+ addend = gen_int_mode (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1, Pmode);
+ mask = gen_int_mode (-TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT, Pmode);
temp = expand_simple_binop (Pmode, PLUS, tramp, addend,
temp, 0, OPTAB_LIB_WIDEN);
}
static rtx
-expand_builtin_init_trampoline (tree exp)
+expand_builtin_init_trampoline (tree exp, bool onstack)
{
tree t_tramp, t_func, t_chain;
rtx m_tramp, r_tramp, r_chain, tmp;
m_tramp = gen_rtx_MEM (BLKmode, r_tramp);
MEM_NOTRAP_P (m_tramp) = 1;
- /* The TRAMP argument should be the address of a field within the
- local function's FRAME decl. Let's see if we can fill in the
- to fill in the MEM_ATTRs for this memory. */
+ /* If ONSTACK, the TRAMP argument should be the address of a field
+ within the local function's FRAME decl. Either way, let's see if
+ we can fill in the MEM_ATTRs for this memory. */
if (TREE_CODE (t_tramp) == ADDR_EXPR)
- set_mem_attributes_minus_bitpos (m_tramp, TREE_OPERAND (t_tramp, 0),
- true, 0);
+ set_mem_attributes (m_tramp, TREE_OPERAND (t_tramp, 0), true);
+ /* Creator of a heap trampoline is responsible for making sure the
+ address is aligned to at least STACK_BOUNDARY. Normally malloc
+ will ensure this anyhow. */
tmp = round_trampoline_addr (r_tramp);
if (tmp != r_tramp)
{
/* Generate insns to initialize the trampoline. */
targetm.calls.trampoline_init (m_tramp, t_func, r_chain);
- trampolines_created = 1;
+ if (onstack)
+ {
+ trampolines_created = 1;
- warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines,
- "trampoline generated for nested function %qD", t_func);
+ warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines,
+ "trampoline generated for nested function %qD", t_func);
+ }
return const0_rtx;
}
if (bitpos < GET_MODE_BITSIZE (rmode))
{
- double_int mask = double_int_setbit (double_int_zero, bitpos);
+ double_int mask = double_int_zero.set_bit (bitpos);
if (GET_MODE_SIZE (imode) > GET_MODE_SIZE (rmode))
temp = gen_lowpart (rmode, temp);
return mem;
}
+/* Make sure an argument is in the right mode.
+ EXP is the tree argument.
+ MODE is the mode it should be in. */
+
+static rtx
+expand_expr_force_mode (tree exp, enum machine_mode mode)
+{
+ rtx val;
+ enum machine_mode old_mode;
+
+ val = expand_expr (exp, NULL_RTX, mode, EXPAND_NORMAL);
+ /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
+ of CONST_INTs, where we know the old_mode only from the call argument. */
+
+ old_mode = GET_MODE (val);
+ if (old_mode == VOIDmode)
+ old_mode = TYPE_MODE (TREE_TYPE (exp));
+ val = convert_modes (mode, old_mode, val, 1);
+ return val;
+}
+
+
/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics.
EXP is the CALL_EXPR. CODE is the rtx code
that corresponds to the arithmetic or logical operation from the name;
an exception here is that NOT actually means NAND. TARGET is an optional
place for us to store the results; AFTER is true if this is the
- fetch_and_xxx form. IGNORE is true if we don't actually care about
- the result of the operation at all. */
+ fetch_and_xxx form. */
static rtx
expand_builtin_sync_operation (enum machine_mode mode, tree exp,
enum rtx_code code, bool after,
- rtx target, bool ignore)
+ rtx target)
{
rtx val, mem;
- enum machine_mode old_mode;
location_t loc = EXPR_LOCATION (exp);
if (code == NOT && warn_sync_nand)
case BUILT_IN_SYNC_FETCH_AND_NAND_4:
case BUILT_IN_SYNC_FETCH_AND_NAND_8:
case BUILT_IN_SYNC_FETCH_AND_NAND_16:
-
if (warned_f_a_n)
break;
- fndecl = implicit_built_in_decls[BUILT_IN_SYNC_FETCH_AND_NAND_N];
+ fndecl = builtin_decl_implicit (BUILT_IN_SYNC_FETCH_AND_NAND_N);
inform (loc, "%qD changed semantics in GCC 4.4", fndecl);
warned_f_a_n = true;
break;
case BUILT_IN_SYNC_NAND_AND_FETCH_4:
case BUILT_IN_SYNC_NAND_AND_FETCH_8:
case BUILT_IN_SYNC_NAND_AND_FETCH_16:
-
if (warned_n_a_f)
break;
- fndecl = implicit_built_in_decls[BUILT_IN_SYNC_NAND_AND_FETCH_N];
+ fndecl = builtin_decl_implicit (BUILT_IN_SYNC_NAND_AND_FETCH_N);
inform (loc, "%qD changed semantics in GCC 4.4", fndecl);
warned_n_a_f = true;
break;
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+ val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
- val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL_RTX, mode, EXPAND_NORMAL);
- /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
- of CONST_INTs, where we know the old_mode only from the call argument. */
- old_mode = GET_MODE (val);
- if (old_mode == VOIDmode)
- old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1)));
- val = convert_modes (mode, old_mode, val, 1);
-
- if (ignore)
- return expand_sync_operation (mem, val, code);
- else
- return expand_sync_fetch_operation (mem, val, code, after, target);
+ return expand_atomic_fetch_op (target, mem, val, code, MEMMODEL_SEQ_CST,
+ after);
}
/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap
bool is_bool, rtx target)
{
rtx old_val, new_val, mem;
- enum machine_mode old_mode;
+ rtx *pbool, *poval;
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+ old_val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+ new_val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 2), mode);
+ pbool = poval = NULL;
+ if (target != const0_rtx)
+ {
+ if (is_bool)
+ pbool = ⌖
+ else
+ poval = ⌖
+ }
+ if (!expand_atomic_compare_and_swap (pbool, poval, mem, old_val, new_val,
+ false, MEMMODEL_SEQ_CST,
+ MEMMODEL_SEQ_CST))
+ return NULL_RTX;
- old_val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL_RTX,
- mode, EXPAND_NORMAL);
- /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
- of CONST_INTs, where we know the old_mode only from the call argument. */
- old_mode = GET_MODE (old_val);
- if (old_mode == VOIDmode)
- old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1)));
- old_val = convert_modes (mode, old_mode, old_val, 1);
-
- new_val = expand_expr (CALL_EXPR_ARG (exp, 2), NULL_RTX,
- mode, EXPAND_NORMAL);
- /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
- of CONST_INTs, where we know the old_mode only from the call argument. */
- old_mode = GET_MODE (new_val);
- if (old_mode == VOIDmode)
- old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 2)));
- new_val = convert_modes (mode, old_mode, new_val, 1);
-
- if (is_bool)
- return expand_bool_compare_and_swap (mem, old_val, new_val, target);
- else
- return expand_val_compare_and_swap (mem, old_val, new_val, target);
+ return target;
}
/* Expand the __sync_lock_test_and_set intrinsic. Note that the most
static rtx
expand_builtin_sync_lock_test_and_set (enum machine_mode mode, tree exp,
- rtx target)
+ rtx target)
{
rtx val, mem;
- enum machine_mode old_mode;
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
- val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL_RTX, mode, EXPAND_NORMAL);
- /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
- of CONST_INTs, where we know the old_mode only from the call argument. */
- old_mode = GET_MODE (val);
- if (old_mode == VOIDmode)
- old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1)));
- val = convert_modes (mode, old_mode, val, 1);
+ val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
- return expand_sync_lock_test_and_set (mem, val, target);
+ return expand_sync_lock_test_and_set (target, mem, val);
}
-/* Expand the __sync_synchronize intrinsic. */
+/* Expand the __sync_lock_release intrinsic. EXP is the CALL_EXPR. */
static void
-expand_builtin_sync_synchronize (void)
+expand_builtin_sync_lock_release (enum machine_mode mode, tree exp)
{
- gimple x;
- VEC (tree, gc) *v_clobbers;
+ rtx mem;
-#ifdef HAVE_memory_barrier
- if (HAVE_memory_barrier)
+ /* Expand the operands. */
+ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+
+ expand_atomic_store (mem, const0_rtx, MEMMODEL_RELEASE, true);
+}
+
+/* Given an integer representing an ``enum memmodel'', verify its
+ correctness and return the memory model enum. */
+
+static enum memmodel
+get_memmodel (tree exp)
+{
+ rtx op;
+ unsigned HOST_WIDE_INT val;
+
+ /* If the parameter is not a constant, it's a run time value so we'll just
+ convert it to MEMMODEL_SEQ_CST to avoid annoying runtime checking. */
+ if (TREE_CODE (exp) != INTEGER_CST)
+ return MEMMODEL_SEQ_CST;
+
+ op = expand_normal (exp);
+
+ val = INTVAL (op);
+ if (targetm.memmodel_check)
+ val = targetm.memmodel_check (val);
+ else if (val & ~MEMMODEL_MASK)
{
- emit_insn (gen_memory_barrier ());
- return;
+ warning (OPT_Winvalid_memory_model,
+ "Unknown architecture specifier in memory model to builtin.");
+ return MEMMODEL_SEQ_CST;
}
-#endif
- if (synchronize_libfunc != NULL_RTX)
+ if ((INTVAL (op) & MEMMODEL_MASK) >= MEMMODEL_LAST)
{
- emit_library_call (synchronize_libfunc, LCT_NORMAL, VOIDmode, 0);
- return;
+ warning (OPT_Winvalid_memory_model,
+ "invalid memory model argument to builtin");
+ return MEMMODEL_SEQ_CST;
}
- /* If no explicit memory barrier instruction is available, create an
- empty asm stmt with a memory clobber. */
- v_clobbers = VEC_alloc (tree, gc, 1);
- VEC_quick_push (tree, v_clobbers,
- tree_cons (NULL, build_string (6, "memory"), NULL));
- x = gimple_build_asm_vec ("", NULL, NULL, v_clobbers, NULL);
- gimple_asm_set_volatile (x, true);
- expand_asm_stmt (x);
+ return (enum memmodel) val;
}
-/* Expand the __sync_lock_release intrinsic. EXP is the CALL_EXPR. */
+/* Expand the __atomic_exchange intrinsic:
+ TYPE __atomic_exchange (TYPE *object, TYPE desired, enum memmodel)
+ EXP is the CALL_EXPR.
+ TARGET is an optional place for us to store the results. */
-static void
-expand_builtin_sync_lock_release (enum machine_mode mode, tree exp)
+static rtx
+expand_builtin_atomic_exchange (enum machine_mode mode, tree exp, rtx target)
+{
+ rtx val, mem;
+ enum memmodel model;
+
+ model = get_memmodel (CALL_EXPR_ARG (exp, 2));
+ if ((model & MEMMODEL_MASK) == MEMMODEL_CONSUME)
+ {
+ error ("invalid memory model for %<__atomic_exchange%>");
+ return NULL_RTX;
+ }
+
+ if (!flag_inline_atomics)
+ return NULL_RTX;
+
+ /* Expand the operands. */
+ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+ val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+
+ return expand_atomic_exchange (target, mem, val, model);
+}
+
+/* Expand the __atomic_compare_exchange intrinsic:
+ bool __atomic_compare_exchange (TYPE *object, TYPE *expect,
+ TYPE desired, BOOL weak,
+ enum memmodel success,
+ enum memmodel failure)
+ EXP is the CALL_EXPR.
+ TARGET is an optional place for us to store the results. */
+
+static rtx
+expand_builtin_atomic_compare_exchange (enum machine_mode mode, tree exp,
+ rtx target)
+{
+ rtx expect, desired, mem, oldval;
+ enum memmodel success, failure;
+ tree weak;
+ bool is_weak;
+
+ success = get_memmodel (CALL_EXPR_ARG (exp, 4));
+ failure = get_memmodel (CALL_EXPR_ARG (exp, 5));
+
+ if ((failure & MEMMODEL_MASK) == MEMMODEL_RELEASE
+ || (failure & MEMMODEL_MASK) == MEMMODEL_ACQ_REL)
+ {
+ error ("invalid failure memory model for %<__atomic_compare_exchange%>");
+ return NULL_RTX;
+ }
+
+ if (failure > success)
+ {
+ error ("failure memory model cannot be stronger than success "
+ "memory model for %<__atomic_compare_exchange%>");
+ return NULL_RTX;
+ }
+
+ if (!flag_inline_atomics)
+ return NULL_RTX;
+
+ /* Expand the operands. */
+ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+
+ expect = expand_normal (CALL_EXPR_ARG (exp, 1));
+ expect = convert_memory_address (Pmode, expect);
+ expect = gen_rtx_MEM (mode, expect);
+ desired = expand_expr_force_mode (CALL_EXPR_ARG (exp, 2), mode);
+
+ weak = CALL_EXPR_ARG (exp, 3);
+ is_weak = false;
+ if (host_integerp (weak, 0) && tree_low_cst (weak, 0) != 0)
+ is_weak = true;
+
+ oldval = expect;
+ if (!expand_atomic_compare_and_swap ((target == const0_rtx ? NULL : &target),
+ &oldval, mem, oldval, desired,
+ is_weak, success, failure))
+ return NULL_RTX;
+
+ if (oldval != expect)
+ emit_move_insn (expect, oldval);
+
+ return target;
+}
+
+/* Expand the __atomic_load intrinsic:
+ TYPE __atomic_load (TYPE *object, enum memmodel)
+ EXP is the CALL_EXPR.
+ TARGET is an optional place for us to store the results. */
+
+static rtx
+expand_builtin_atomic_load (enum machine_mode mode, tree exp, rtx target)
{
- struct expand_operand ops[2];
- enum insn_code icode;
rtx mem;
+ enum memmodel model;
+
+ model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+ if ((model & MEMMODEL_MASK) == MEMMODEL_RELEASE
+ || (model & MEMMODEL_MASK) == MEMMODEL_ACQ_REL)
+ {
+ error ("invalid memory model for %<__atomic_load%>");
+ return NULL_RTX;
+ }
+
+ if (!flag_inline_atomics)
+ return NULL_RTX;
+
+ /* Expand the operand. */
+ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+
+ return expand_atomic_load (target, mem, model);
+}
+
+
+/* Expand the __atomic_store intrinsic:
+ void __atomic_store (TYPE *object, TYPE desired, enum memmodel)
+ EXP is the CALL_EXPR.
+ TARGET is an optional place for us to store the results. */
+
+static rtx
+expand_builtin_atomic_store (enum machine_mode mode, tree exp)
+{
+ rtx mem, val;
+ enum memmodel model;
+
+ model = get_memmodel (CALL_EXPR_ARG (exp, 2));
+ if ((model & MEMMODEL_MASK) != MEMMODEL_RELAXED
+ && (model & MEMMODEL_MASK) != MEMMODEL_SEQ_CST
+ && (model & MEMMODEL_MASK) != MEMMODEL_RELEASE)
+ {
+ error ("invalid memory model for %<__atomic_store%>");
+ return NULL_RTX;
+ }
+
+ if (!flag_inline_atomics)
+ return NULL_RTX;
+
+ /* Expand the operands. */
+ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+ val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+
+ return expand_atomic_store (mem, val, model, false);
+}
+
+/* Expand the __atomic_fetch_XXX intrinsic:
+ TYPE __atomic_fetch_XXX (TYPE *object, TYPE val, enum memmodel)
+ EXP is the CALL_EXPR.
+ TARGET is an optional place for us to store the results.
+ CODE is the operation, PLUS, MINUS, ADD, XOR, or IOR.
+ FETCH_AFTER is true if returning the result of the operation.
+ FETCH_AFTER is false if returning the value before the operation.
+ IGNORE is true if the result is not used.
+ EXT_CALL is the correct builtin for an external call if this cannot be
+ resolved to an instruction sequence. */
+
+static rtx
+expand_builtin_atomic_fetch_op (enum machine_mode mode, tree exp, rtx target,
+ enum rtx_code code, bool fetch_after,
+ bool ignore, enum built_in_function ext_call)
+{
+ rtx val, mem, ret;
+ enum memmodel model;
+ tree fndecl;
+ tree addr;
+
+ model = get_memmodel (CALL_EXPR_ARG (exp, 2));
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+ val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+
+ /* Only try generating instructions if inlining is turned on. */
+ if (flag_inline_atomics)
+ {
+ ret = expand_atomic_fetch_op (target, mem, val, code, model, fetch_after);
+ if (ret)
+ return ret;
+ }
+
+ /* Return if a different routine isn't needed for the library call. */
+ if (ext_call == BUILT_IN_NONE)
+ return NULL_RTX;
+
+ /* Change the call to the specified function. */
+ fndecl = get_callee_fndecl (exp);
+ addr = CALL_EXPR_FN (exp);
+ STRIP_NOPS (addr);
+
+ gcc_assert (TREE_OPERAND (addr, 0) == fndecl);
+ TREE_OPERAND (addr, 0) = builtin_decl_explicit (ext_call);
+
+ /* Expand the call here so we can emit trailing code. */
+ ret = expand_call (exp, target, ignore);
+
+ /* Replace the original function just in case it matters. */
+ TREE_OPERAND (addr, 0) = fndecl;
+
+ /* Then issue the arithmetic correction to return the right result. */
+ if (!ignore)
+ {
+ if (code == NOT)
+ {
+ ret = expand_simple_binop (mode, AND, ret, val, NULL_RTX, true,
+ OPTAB_LIB_WIDEN);
+ ret = expand_simple_unop (mode, NOT, ret, target, true);
+ }
+ else
+ ret = expand_simple_binop (mode, code, ret, val, target, true,
+ OPTAB_LIB_WIDEN);
+ }
+ return ret;
+}
+
+
+#ifndef HAVE_atomic_clear
+# define HAVE_atomic_clear 0
+# define gen_atomic_clear(x,y) (gcc_unreachable (), NULL_RTX)
+#endif
+
+/* Expand an atomic clear operation.
+ void _atomic_clear (BOOL *obj, enum memmodel)
+ EXP is the call expression. */
+
+static rtx
+expand_builtin_atomic_clear (tree exp)
+{
+ enum machine_mode mode;
+ rtx mem, ret;
+ enum memmodel model;
+
+ mode = mode_for_size (BOOL_TYPE_SIZE, MODE_INT, 0);
+ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+ model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+
+ if ((model & MEMMODEL_MASK) == MEMMODEL_ACQUIRE
+ || (model & MEMMODEL_MASK) == MEMMODEL_ACQ_REL)
+ {
+ error ("invalid memory model for %<__atomic_store%>");
+ return const0_rtx;
+ }
+
+ if (HAVE_atomic_clear)
+ {
+ emit_insn (gen_atomic_clear (mem, model));
+ return const0_rtx;
+ }
+
+ /* Try issuing an __atomic_store, and allow fallback to __sync_lock_release.
+ Failing that, a store is issued by __atomic_store. The only way this can
+ fail is if the bool type is larger than a word size. Unlikely, but
+ handle it anyway for completeness. Assume a single threaded model since
+ there is no atomic support in this case, and no barriers are required. */
+ ret = expand_atomic_store (mem, const0_rtx, model, true);
+ if (!ret)
+ emit_move_insn (mem, const0_rtx);
+ return const0_rtx;
+}
+
+/* Expand an atomic test_and_set operation.
+ bool _atomic_test_and_set (BOOL *obj, enum memmodel)
+ EXP is the call expression. */
+
+static rtx
+expand_builtin_atomic_test_and_set (tree exp, rtx target)
+{
+ rtx mem;
+ enum memmodel model;
+ enum machine_mode mode;
+
+ mode = mode_for_size (BOOL_TYPE_SIZE, MODE_INT, 0);
+ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+ model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+
+ return expand_atomic_test_and_set (target, mem, model);
+}
- /* If there is an explicit operation in the md file, use it. */
- icode = direct_optab_handler (sync_lock_release_optab, mode);
+
+/* Return true if (optional) argument ARG1 of size ARG0 is always lock free on
+ this architecture. If ARG1 is NULL, use typical alignment for size ARG0. */
+
+static tree
+fold_builtin_atomic_always_lock_free (tree arg0, tree arg1)
+{
+ int size;
+ enum machine_mode mode;
+ unsigned int mode_align, type_align;
+
+ if (TREE_CODE (arg0) != INTEGER_CST)
+ return NULL_TREE;
+
+ size = INTVAL (expand_normal (arg0)) * BITS_PER_UNIT;
+ mode = mode_for_size (size, MODE_INT, 0);
+ mode_align = GET_MODE_ALIGNMENT (mode);
+
+ if (TREE_CODE (arg1) == INTEGER_CST && INTVAL (expand_normal (arg1)) == 0)
+ type_align = mode_align;
+ else
+ {
+ tree ttype = TREE_TYPE (arg1);
+
+ /* This function is usually invoked and folded immediately by the front
+ end before anything else has a chance to look at it. The pointer
+ parameter at this point is usually cast to a void *, so check for that
+ and look past the cast. */
+ if (TREE_CODE (arg1) == NOP_EXPR && POINTER_TYPE_P (ttype)
+ && VOID_TYPE_P (TREE_TYPE (ttype)))
+ arg1 = TREE_OPERAND (arg1, 0);
+
+ ttype = TREE_TYPE (arg1);
+ gcc_assert (POINTER_TYPE_P (ttype));
+
+ /* Get the underlying type of the object. */
+ ttype = TREE_TYPE (ttype);
+ type_align = TYPE_ALIGN (ttype);
+ }
+
+ /* If the object has smaller alignment, the the lock free routines cannot
+ be used. */
+ if (type_align < mode_align)
+ return boolean_false_node;
+
+ /* Check if a compare_and_swap pattern exists for the mode which represents
+ the required size. The pattern is not allowed to fail, so the existence
+ of the pattern indicates support is present. */
+ if (can_compare_and_swap_p (mode, true))
+ return boolean_true_node;
+ else
+ return boolean_false_node;
+}
+
+/* Return true if the parameters to call EXP represent an object which will
+ always generate lock free instructions. The first argument represents the
+ size of the object, and the second parameter is a pointer to the object
+ itself. If NULL is passed for the object, then the result is based on
+ typical alignment for an object of the specified size. Otherwise return
+ false. */
+
+static rtx
+expand_builtin_atomic_always_lock_free (tree exp)
+{
+ tree size;
+ tree arg0 = CALL_EXPR_ARG (exp, 0);
+ tree arg1 = CALL_EXPR_ARG (exp, 1);
+
+ if (TREE_CODE (arg0) != INTEGER_CST)
+ {
+ error ("non-constant argument 1 to __atomic_always_lock_free");
+ return const0_rtx;
+ }
+
+ size = fold_builtin_atomic_always_lock_free (arg0, arg1);
+ if (size == boolean_true_node)
+ return const1_rtx;
+ return const0_rtx;
+}
+
+/* Return a one or zero if it can be determined that object ARG1 of size ARG
+ is lock free on this architecture. */
+
+static tree
+fold_builtin_atomic_is_lock_free (tree arg0, tree arg1)
+{
+ if (!flag_inline_atomics)
+ return NULL_TREE;
+
+ /* If it isn't always lock free, don't generate a result. */
+ if (fold_builtin_atomic_always_lock_free (arg0, arg1) == boolean_true_node)
+ return boolean_true_node;
+
+ return NULL_TREE;
+}
+
+/* Return true if the parameters to call EXP represent an object which will
+ always generate lock free instructions. The first argument represents the
+ size of the object, and the second parameter is a pointer to the object
+ itself. If NULL is passed for the object, then the result is based on
+ typical alignment for an object of the specified size. Otherwise return
+ NULL*/
+
+static rtx
+expand_builtin_atomic_is_lock_free (tree exp)
+{
+ tree size;
+ tree arg0 = CALL_EXPR_ARG (exp, 0);
+ tree arg1 = CALL_EXPR_ARG (exp, 1);
+
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (arg0)))
+ {
+ error ("non-integer argument 1 to __atomic_is_lock_free");
+ return NULL_RTX;
+ }
+
+ if (!flag_inline_atomics)
+ return NULL_RTX;
+
+ /* If the value is known at compile time, return the RTX for it. */
+ size = fold_builtin_atomic_is_lock_free (arg0, arg1);
+ if (size == boolean_true_node)
+ return const1_rtx;
+
+ return NULL_RTX;
+}
+
+/* Expand the __atomic_thread_fence intrinsic:
+ void __atomic_thread_fence (enum memmodel)
+ EXP is the CALL_EXPR. */
+
+static void
+expand_builtin_atomic_thread_fence (tree exp)
+{
+ enum memmodel model = get_memmodel (CALL_EXPR_ARG (exp, 0));
+ expand_mem_thread_fence (model);
+}
+
+/* Expand the __atomic_signal_fence intrinsic:
+ void __atomic_signal_fence (enum memmodel)
+ EXP is the CALL_EXPR. */
+
+static void
+expand_builtin_atomic_signal_fence (tree exp)
+{
+ enum memmodel model = get_memmodel (CALL_EXPR_ARG (exp, 0));
+ expand_mem_signal_fence (model);
+}
+
+/* Expand the __sync_synchronize intrinsic. */
+
+static void
+expand_builtin_sync_synchronize (void)
+{
+ expand_mem_thread_fence (MEMMODEL_SEQ_CST);
+}
+
+static rtx
+expand_builtin_thread_pointer (tree exp, rtx target)
+{
+ enum insn_code icode;
+ if (!validate_arglist (exp, VOID_TYPE))
+ return const0_rtx;
+ icode = direct_optab_handler (get_thread_pointer_optab, Pmode);
if (icode != CODE_FOR_nothing)
{
- create_fixed_operand (&ops[0], mem);
- create_input_operand (&ops[1], const0_rtx, mode);
- if (maybe_expand_insn (icode, 2, ops))
- return;
+ struct expand_operand op;
+ if (!REG_P (target) || GET_MODE (target) != Pmode)
+ target = gen_reg_rtx (Pmode);
+ create_output_operand (&op, target, Pmode);
+ expand_insn (icode, 1, &op);
+ return target;
}
+ error ("__builtin_thread_pointer is not supported on this target");
+ return const0_rtx;
+}
- /* Otherwise we can implement this operation by emitting a barrier
- followed by a store of zero. */
- expand_builtin_sync_synchronize ();
- emit_move_insn (mem, const0_rtx);
+static void
+expand_builtin_set_thread_pointer (tree exp)
+{
+ enum insn_code icode;
+ if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
+ return;
+ icode = direct_optab_handler (set_thread_pointer_optab, Pmode);
+ if (icode != CODE_FOR_nothing)
+ {
+ struct expand_operand op;
+ rtx val = expand_expr (CALL_EXPR_ARG (exp, 0), NULL_RTX,
+ Pmode, EXPAND_NORMAL);
+ create_input_operand (&op, val, Pmode);
+ expand_insn (icode, 1, &op);
+ return;
+ }
+ error ("__builtin_set_thread_pointer is not supported on this target");
}
+
\f
+/* Emit code to restore the current value of stack. */
+
+static void
+expand_stack_restore (tree var)
+{
+ rtx prev, sa = expand_normal (var);
+
+ sa = convert_memory_address (Pmode, sa);
+
+ prev = get_last_insn ();
+ emit_stack_restore (SAVE_BLOCK, sa);
+ fixup_args_size_notes (prev, get_last_insn (), 0);
+}
+
+
+/* Emit code to save the current value of stack. */
+
+static rtx
+expand_stack_save (void)
+{
+ rtx ret = NULL_RTX;
+
+ do_pending_stack_adjust ();
+ emit_stack_save (SAVE_BLOCK, &ret);
+ return ret;
+}
+
/* Expand an expression EXP that calls a built-in function,
with result going to TARGET if that's convenient
(and in mode MODE if that's convenient).
set of builtins. */
if (!optimize
&& !called_as_built_in (fndecl)
- && DECL_ASSEMBLER_NAME_SET_P (fndecl)
+ && fcode != BUILT_IN_FORK
+ && fcode != BUILT_IN_EXECL
+ && fcode != BUILT_IN_EXECV
+ && fcode != BUILT_IN_EXECLP
+ && fcode != BUILT_IN_EXECLE
+ && fcode != BUILT_IN_EXECVP
+ && fcode != BUILT_IN_EXECVE
&& fcode != BUILT_IN_ALLOCA
- && fcode != BUILT_IN_FREE)
+ && fcode != BUILT_IN_ALLOCA_WITH_ALIGN
+ && fcode != BUILT_IN_FREE
+ && fcode != BUILT_IN_CHKP_SET_PTR_BOUNDS
+ && fcode != BUILT_IN_CHKP_INIT_PTR_BOUNDS
+ && fcode != BUILT_IN_CHKP_NULL_PTR_BOUNDS
+ && fcode != BUILT_IN_CHKP_COPY_PTR_BOUNDS
+ && fcode != BUILT_IN_CHKP_NARROW_PTR_BOUNDS
+ && fcode != BUILT_IN_CHKP_STORE_PTR_BOUNDS
+ && fcode != BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
+ && fcode != BUILT_IN_CHKP_CHECK_PTR_UBOUNDS
+ && fcode != BUILT_IN_CHKP_CHECK_PTR_BOUNDS
+ && fcode != BUILT_IN_CHKP_GET_PTR_LBOUND
+ && fcode != BUILT_IN_CHKP_GET_PTR_UBOUND)
return expand_call (exp, target, ignore);
/* The built-in function expanders test for target == const0_rtx
switch (fcode)
{
CASE_FLT_FN (BUILT_IN_FABS):
+ case BUILT_IN_FABSD32:
+ case BUILT_IN_FABSD64:
+ case BUILT_IN_FABSD128:
target = expand_builtin_fabs (exp, target, subtarget);
if (target)
return target;
return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
case BUILT_IN_ALLOCA:
+ case BUILT_IN_ALLOCA_WITH_ALIGN:
/* If the allocation stems from the declaration of a variable-sized
object, it cannot accumulate. */
target = expand_builtin_alloca (exp, CALL_ALLOCA_FOR_VAR_P (exp));
expand_stack_restore (CALL_EXPR_ARG (exp, 0));
return const0_rtx;
+ case BUILT_IN_BSWAP16:
case BUILT_IN_BSWAP32:
case BUILT_IN_BSWAP64:
- target = expand_builtin_bswap (exp, target, subtarget);
-
+ target = expand_builtin_bswap (target_mode, exp, target, subtarget);
if (target)
return target;
break;
CASE_INT_FN (BUILT_IN_FFS):
- case BUILT_IN_FFSIMAX:
target = expand_builtin_unop (target_mode, exp, target,
subtarget, ffs_optab);
if (target)
break;
CASE_INT_FN (BUILT_IN_CLZ):
- case BUILT_IN_CLZIMAX:
target = expand_builtin_unop (target_mode, exp, target,
subtarget, clz_optab);
if (target)
break;
CASE_INT_FN (BUILT_IN_CTZ):
- case BUILT_IN_CTZIMAX:
target = expand_builtin_unop (target_mode, exp, target,
subtarget, ctz_optab);
if (target)
break;
CASE_INT_FN (BUILT_IN_CLRSB):
- case BUILT_IN_CLRSBIMAX:
target = expand_builtin_unop (target_mode, exp, target,
subtarget, clrsb_optab);
if (target)
break;
CASE_INT_FN (BUILT_IN_POPCOUNT):
- case BUILT_IN_POPCOUNTIMAX:
target = expand_builtin_unop (target_mode, exp, target,
subtarget, popcount_optab);
if (target)
break;
CASE_INT_FN (BUILT_IN_PARITY):
- case BUILT_IN_PARITYIMAX:
target = expand_builtin_unop (target_mode, exp, target,
subtarget, parity_optab);
if (target)
return const0_rtx;
case BUILT_IN_INIT_TRAMPOLINE:
- return expand_builtin_init_trampoline (exp);
+ return expand_builtin_init_trampoline (exp, true);
+ case BUILT_IN_INIT_HEAP_TRAMPOLINE:
+ return expand_builtin_init_trampoline (exp, false);
case BUILT_IN_ADJUST_TRAMPOLINE:
return expand_builtin_adjust_trampoline (exp);
case BUILT_IN_SYNC_FETCH_AND_ADD_8:
case BUILT_IN_SYNC_FETCH_AND_ADD_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_ADD_1);
- target = expand_builtin_sync_operation (mode, exp, PLUS,
- false, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, PLUS, false, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_FETCH_AND_SUB_8:
case BUILT_IN_SYNC_FETCH_AND_SUB_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_SUB_1);
- target = expand_builtin_sync_operation (mode, exp, MINUS,
- false, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, MINUS, false, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_FETCH_AND_OR_8:
case BUILT_IN_SYNC_FETCH_AND_OR_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_OR_1);
- target = expand_builtin_sync_operation (mode, exp, IOR,
- false, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, IOR, false, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_FETCH_AND_AND_8:
case BUILT_IN_SYNC_FETCH_AND_AND_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_AND_1);
- target = expand_builtin_sync_operation (mode, exp, AND,
- false, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, AND, false, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_FETCH_AND_XOR_8:
case BUILT_IN_SYNC_FETCH_AND_XOR_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_XOR_1);
- target = expand_builtin_sync_operation (mode, exp, XOR,
- false, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, XOR, false, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_FETCH_AND_NAND_8:
case BUILT_IN_SYNC_FETCH_AND_NAND_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_NAND_1);
- target = expand_builtin_sync_operation (mode, exp, NOT,
- false, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, NOT, false, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_ADD_AND_FETCH_8:
case BUILT_IN_SYNC_ADD_AND_FETCH_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_ADD_AND_FETCH_1);
- target = expand_builtin_sync_operation (mode, exp, PLUS,
- true, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, PLUS, true, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_SUB_AND_FETCH_8:
case BUILT_IN_SYNC_SUB_AND_FETCH_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_SUB_AND_FETCH_1);
- target = expand_builtin_sync_operation (mode, exp, MINUS,
- true, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, MINUS, true, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_OR_AND_FETCH_8:
case BUILT_IN_SYNC_OR_AND_FETCH_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_OR_AND_FETCH_1);
- target = expand_builtin_sync_operation (mode, exp, IOR,
- true, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, IOR, true, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_AND_AND_FETCH_8:
case BUILT_IN_SYNC_AND_AND_FETCH_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_AND_AND_FETCH_1);
- target = expand_builtin_sync_operation (mode, exp, AND,
- true, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, AND, true, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_XOR_AND_FETCH_8:
case BUILT_IN_SYNC_XOR_AND_FETCH_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_XOR_AND_FETCH_1);
- target = expand_builtin_sync_operation (mode, exp, XOR,
- true, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, XOR, true, target);
if (target)
return target;
break;
case BUILT_IN_SYNC_NAND_AND_FETCH_8:
case BUILT_IN_SYNC_NAND_AND_FETCH_16:
mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_NAND_AND_FETCH_1);
- target = expand_builtin_sync_operation (mode, exp, NOT,
- true, target, ignore);
+ target = expand_builtin_sync_operation (mode, exp, NOT, true, target);
if (target)
return target;
break;
expand_builtin_sync_lock_release (mode, exp);
return const0_rtx;
- case BUILT_IN_SYNC_SYNCHRONIZE:
- expand_builtin_sync_synchronize ();
+ case BUILT_IN_SYNC_SYNCHRONIZE:
+ expand_builtin_sync_synchronize ();
+ return const0_rtx;
+
+ case BUILT_IN_ATOMIC_EXCHANGE_1:
+ case BUILT_IN_ATOMIC_EXCHANGE_2:
+ case BUILT_IN_ATOMIC_EXCHANGE_4:
+ case BUILT_IN_ATOMIC_EXCHANGE_8:
+ case BUILT_IN_ATOMIC_EXCHANGE_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_EXCHANGE_1);
+ target = expand_builtin_atomic_exchange (mode, exp, target);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1:
+ case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_2:
+ case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4:
+ case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8:
+ case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_16:
+ {
+ unsigned int nargs, z;
+ vec<tree, va_gc> *vec;
+
+ mode =
+ get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1);
+ target = expand_builtin_atomic_compare_exchange (mode, exp, target);
+ if (target)
+ return target;
+
+ /* If this is turned into an external library call, the weak parameter
+ must be dropped to match the expected parameter list. */
+ nargs = call_expr_nargs (exp);
+ vec_alloc (vec, nargs - 1);
+ for (z = 0; z < 3; z++)
+ vec->quick_push (CALL_EXPR_ARG (exp, z));
+ /* Skip the boolean weak parameter. */
+ for (z = 4; z < 6; z++)
+ vec->quick_push (CALL_EXPR_ARG (exp, z));
+ exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), vec);
+ break;
+ }
+
+ case BUILT_IN_ATOMIC_LOAD_1:
+ case BUILT_IN_ATOMIC_LOAD_2:
+ case BUILT_IN_ATOMIC_LOAD_4:
+ case BUILT_IN_ATOMIC_LOAD_8:
+ case BUILT_IN_ATOMIC_LOAD_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_LOAD_1);
+ target = expand_builtin_atomic_load (mode, exp, target);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_STORE_1:
+ case BUILT_IN_ATOMIC_STORE_2:
+ case BUILT_IN_ATOMIC_STORE_4:
+ case BUILT_IN_ATOMIC_STORE_8:
+ case BUILT_IN_ATOMIC_STORE_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_STORE_1);
+ target = expand_builtin_atomic_store (mode, exp);
+ if (target)
+ return const0_rtx;
+ break;
+
+ case BUILT_IN_ATOMIC_ADD_FETCH_1:
+ case BUILT_IN_ATOMIC_ADD_FETCH_2:
+ case BUILT_IN_ATOMIC_ADD_FETCH_4:
+ case BUILT_IN_ATOMIC_ADD_FETCH_8:
+ case BUILT_IN_ATOMIC_ADD_FETCH_16:
+ {
+ enum built_in_function lib;
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_ADD_FETCH_1);
+ lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_ADD_1 +
+ (fcode - BUILT_IN_ATOMIC_ADD_FETCH_1));
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, PLUS, true,
+ ignore, lib);
+ if (target)
+ return target;
+ break;
+ }
+ case BUILT_IN_ATOMIC_SUB_FETCH_1:
+ case BUILT_IN_ATOMIC_SUB_FETCH_2:
+ case BUILT_IN_ATOMIC_SUB_FETCH_4:
+ case BUILT_IN_ATOMIC_SUB_FETCH_8:
+ case BUILT_IN_ATOMIC_SUB_FETCH_16:
+ {
+ enum built_in_function lib;
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_SUB_FETCH_1);
+ lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_SUB_1 +
+ (fcode - BUILT_IN_ATOMIC_SUB_FETCH_1));
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, MINUS, true,
+ ignore, lib);
+ if (target)
+ return target;
+ break;
+ }
+ case BUILT_IN_ATOMIC_AND_FETCH_1:
+ case BUILT_IN_ATOMIC_AND_FETCH_2:
+ case BUILT_IN_ATOMIC_AND_FETCH_4:
+ case BUILT_IN_ATOMIC_AND_FETCH_8:
+ case BUILT_IN_ATOMIC_AND_FETCH_16:
+ {
+ enum built_in_function lib;
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_AND_FETCH_1);
+ lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_AND_1 +
+ (fcode - BUILT_IN_ATOMIC_AND_FETCH_1));
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, AND, true,
+ ignore, lib);
+ if (target)
+ return target;
+ break;
+ }
+ case BUILT_IN_ATOMIC_NAND_FETCH_1:
+ case BUILT_IN_ATOMIC_NAND_FETCH_2:
+ case BUILT_IN_ATOMIC_NAND_FETCH_4:
+ case BUILT_IN_ATOMIC_NAND_FETCH_8:
+ case BUILT_IN_ATOMIC_NAND_FETCH_16:
+ {
+ enum built_in_function lib;
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_NAND_FETCH_1);
+ lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_NAND_1 +
+ (fcode - BUILT_IN_ATOMIC_NAND_FETCH_1));
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, NOT, true,
+ ignore, lib);
+ if (target)
+ return target;
+ break;
+ }
+ case BUILT_IN_ATOMIC_XOR_FETCH_1:
+ case BUILT_IN_ATOMIC_XOR_FETCH_2:
+ case BUILT_IN_ATOMIC_XOR_FETCH_4:
+ case BUILT_IN_ATOMIC_XOR_FETCH_8:
+ case BUILT_IN_ATOMIC_XOR_FETCH_16:
+ {
+ enum built_in_function lib;
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_XOR_FETCH_1);
+ lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_XOR_1 +
+ (fcode - BUILT_IN_ATOMIC_XOR_FETCH_1));
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, XOR, true,
+ ignore, lib);
+ if (target)
+ return target;
+ break;
+ }
+ case BUILT_IN_ATOMIC_OR_FETCH_1:
+ case BUILT_IN_ATOMIC_OR_FETCH_2:
+ case BUILT_IN_ATOMIC_OR_FETCH_4:
+ case BUILT_IN_ATOMIC_OR_FETCH_8:
+ case BUILT_IN_ATOMIC_OR_FETCH_16:
+ {
+ enum built_in_function lib;
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_OR_FETCH_1);
+ lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_OR_1 +
+ (fcode - BUILT_IN_ATOMIC_OR_FETCH_1));
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, IOR, true,
+ ignore, lib);
+ if (target)
+ return target;
+ break;
+ }
+ case BUILT_IN_ATOMIC_FETCH_ADD_1:
+ case BUILT_IN_ATOMIC_FETCH_ADD_2:
+ case BUILT_IN_ATOMIC_FETCH_ADD_4:
+ case BUILT_IN_ATOMIC_FETCH_ADD_8:
+ case BUILT_IN_ATOMIC_FETCH_ADD_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_ADD_1);
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, PLUS, false,
+ ignore, BUILT_IN_NONE);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_FETCH_SUB_1:
+ case BUILT_IN_ATOMIC_FETCH_SUB_2:
+ case BUILT_IN_ATOMIC_FETCH_SUB_4:
+ case BUILT_IN_ATOMIC_FETCH_SUB_8:
+ case BUILT_IN_ATOMIC_FETCH_SUB_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_SUB_1);
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, MINUS, false,
+ ignore, BUILT_IN_NONE);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_FETCH_AND_1:
+ case BUILT_IN_ATOMIC_FETCH_AND_2:
+ case BUILT_IN_ATOMIC_FETCH_AND_4:
+ case BUILT_IN_ATOMIC_FETCH_AND_8:
+ case BUILT_IN_ATOMIC_FETCH_AND_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_AND_1);
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, AND, false,
+ ignore, BUILT_IN_NONE);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_FETCH_NAND_1:
+ case BUILT_IN_ATOMIC_FETCH_NAND_2:
+ case BUILT_IN_ATOMIC_FETCH_NAND_4:
+ case BUILT_IN_ATOMIC_FETCH_NAND_8:
+ case BUILT_IN_ATOMIC_FETCH_NAND_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_NAND_1);
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, NOT, false,
+ ignore, BUILT_IN_NONE);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_FETCH_XOR_1:
+ case BUILT_IN_ATOMIC_FETCH_XOR_2:
+ case BUILT_IN_ATOMIC_FETCH_XOR_4:
+ case BUILT_IN_ATOMIC_FETCH_XOR_8:
+ case BUILT_IN_ATOMIC_FETCH_XOR_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_XOR_1);
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, XOR, false,
+ ignore, BUILT_IN_NONE);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_FETCH_OR_1:
+ case BUILT_IN_ATOMIC_FETCH_OR_2:
+ case BUILT_IN_ATOMIC_FETCH_OR_4:
+ case BUILT_IN_ATOMIC_FETCH_OR_8:
+ case BUILT_IN_ATOMIC_FETCH_OR_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_OR_1);
+ target = expand_builtin_atomic_fetch_op (mode, exp, target, IOR, false,
+ ignore, BUILT_IN_NONE);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_TEST_AND_SET:
+ return expand_builtin_atomic_test_and_set (exp, target);
+
+ case BUILT_IN_ATOMIC_CLEAR:
+ return expand_builtin_atomic_clear (exp);
+
+ case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
+ return expand_builtin_atomic_always_lock_free (exp);
+
+ case BUILT_IN_ATOMIC_IS_LOCK_FREE:
+ target = expand_builtin_atomic_is_lock_free (exp);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATOMIC_THREAD_FENCE:
+ expand_builtin_atomic_thread_fence (exp);
+ return const0_rtx;
+
+ case BUILT_IN_ATOMIC_SIGNAL_FENCE:
+ expand_builtin_atomic_signal_fence (exp);
return const0_rtx;
case BUILT_IN_OBJECT_SIZE:
case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STPCPY_CHK:
case BUILT_IN_STRNCPY_CHK:
+ case BUILT_IN_STPNCPY_CHK:
case BUILT_IN_STRCAT_CHK:
case BUILT_IN_STRNCAT_CHK:
case BUILT_IN_SNPRINTF_CHK:
break;
case BUILT_IN_FREE:
- maybe_emit_free_warning (exp);
+ if (warn_free_nonheap_object)
+ maybe_emit_free_warning (exp);
+ break;
+
+ case BUILT_IN_THREAD_POINTER:
+ return expand_builtin_thread_pointer (exp, target);
+
+ case BUILT_IN_SET_THREAD_POINTER:
+ expand_builtin_set_thread_pointer (exp);
+ return const0_rtx;
+
+ case BUILT_IN_CILK_DETACH:
+ expand_builtin_cilk_detach (exp);
+ return const0_rtx;
+
+ case BUILT_IN_CILK_POP_FRAME:
+ expand_builtin_cilk_pop_frame (exp);
+ return const0_rtx;
+
+ case BUILT_IN_CHKP_INIT_PTR_BOUNDS:
+ case BUILT_IN_CHKP_NULL_PTR_BOUNDS:
+ case BUILT_IN_CHKP_COPY_PTR_BOUNDS:
+ return expand_normal (CALL_EXPR_ARG (exp, 0));
+
+ case BUILT_IN_CHKP_CHECK_PTR_LBOUNDS:
+ case BUILT_IN_CHKP_CHECK_PTR_UBOUNDS:
+ case BUILT_IN_CHKP_CHECK_PTR_BOUNDS:
+ case BUILT_IN_CHKP_SET_PTR_BOUNDS:
+ case BUILT_IN_CHKP_NARROW_PTR_BOUNDS:
+ case BUILT_IN_CHKP_STORE_PTR_BOUNDS:
+ case BUILT_IN_CHKP_GET_PTR_LBOUND:
+ case BUILT_IN_CHKP_GET_PTR_UBOUND:
+ /* We allow user CHKP builtins if Pointer Bounds
+ Checker is off. */
+ if (!flag_check_pointer_bounds)
+ {
+ if (fcode == BUILT_IN_CHKP_SET_PTR_BOUNDS
+ || fcode == BUILT_IN_CHKP_NARROW_PTR_BOUNDS)
+ return expand_normal (CALL_EXPR_ARG (exp, 0));
+ else if (fcode == BUILT_IN_CHKP_GET_PTR_LBOUND)
+ return expand_normal (size_zero_node);
+ else if (fcode == BUILT_IN_CHKP_GET_PTR_UBOUND)
+ return expand_normal (size_int (-1));
+ else
+ return const0_rtx;
+ }
+ /* FALLTHROUGH */
+
+ case BUILT_IN_CHKP_BNDMK:
+ case BUILT_IN_CHKP_BNDSTX:
+ case BUILT_IN_CHKP_BNDCL:
+ case BUILT_IN_CHKP_BNDCU:
+ case BUILT_IN_CHKP_BNDLDX:
+ case BUILT_IN_CHKP_BNDRET:
+ case BUILT_IN_CHKP_INTERSECT:
+ case BUILT_IN_CHKP_ARG_BND:
+ case BUILT_IN_CHKP_NARROW:
+ case BUILT_IN_CHKP_EXTRACT_LOWER:
+ case BUILT_IN_CHKP_EXTRACT_UPPER:
+ /* Software implementation of pointers checker is NYI.
+ Target support is required. */
+ error ("Your target platform does not support -fcheck-pointers");
break;
default: /* just do library call, if unknown builtin */
|| AGGREGATE_TYPE_P (TREE_TYPE (arg))
|| POINTER_TYPE_P (TREE_TYPE (arg))
|| cfun == 0
- || folding_initializer)
+ || folding_initializer
+ || force_folding_builtin_constant_p)
return integer_zero_node;
return NULL_TREE;
{
tree fn, arg_types, pred_type, expected_type, call_expr, ret_type;
- fn = built_in_decls[BUILT_IN_EXPECT];
+ fn = builtin_decl_explicit (BUILT_IN_EXPECT);
arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
ret_type = TREE_TYPE (TREE_TYPE (fn));
pred_type = TREE_VALUE (arg_types);
return res;
/* Canonicalize sincos to cexpi. */
- if (!TARGET_C99_FUNCTIONS)
+ if (!targetm.libc_has_function (function_c99_math_complex))
return NULL_TREE;
fn = mathfn_built_in (type, BUILT_IN_CEXPI);
if (!fn)
/* In case we can figure out the real part of arg0 and it is constant zero
fold to cexpi. */
- if (!TARGET_C99_FUNCTIONS)
+ if (!targetm.libc_has_function (function_c99_math_complex))
return NULL_TREE;
ifn = mathfn_built_in (rtype, BUILT_IN_CEXPI);
if (!ifn)
if (width > HOST_BITS_PER_WIDE_INT)
{
hi = TREE_INT_CST_HIGH (arg);
- if (width < 2 * HOST_BITS_PER_WIDE_INT)
- hi &= ~((unsigned HOST_WIDE_INT) (-1)
- << (width - HOST_BITS_PER_WIDE_INT));
+ if (width < HOST_BITS_PER_DOUBLE_INT)
+ hi &= ~(HOST_WIDE_INT_M1U << (width - HOST_BITS_PER_WIDE_INT));
}
else
{
hi = 0;
if (width < HOST_BITS_PER_WIDE_INT)
- lo &= ~((unsigned HOST_WIDE_INT) (-1) << width);
+ lo &= ~(HOST_WIDE_INT_M1U << width);
}
switch (DECL_FUNCTION_CODE (fndecl))
break;
CASE_INT_FN (BUILT_IN_CLRSB):
+ if (width > 2 * HOST_BITS_PER_WIDE_INT)
+ return NULL_TREE;
if (width > HOST_BITS_PER_WIDE_INT
&& (hi & ((unsigned HOST_WIDE_INT) 1
<< (width - HOST_BITS_PER_WIDE_INT - 1))) != 0)
{
- hi = ~hi & ~((unsigned HOST_WIDE_INT) (-1)
+ hi = ~hi & ~(HOST_WIDE_INT_M1U
<< (width - HOST_BITS_PER_WIDE_INT - 1));
lo = ~lo;
}
else if (width <= HOST_BITS_PER_WIDE_INT
&& (lo & ((unsigned HOST_WIDE_INT) 1 << (width - 1))) != 0)
- lo = ~lo & ~((unsigned HOST_WIDE_INT) (-1) << (width - 1));
+ lo = ~lo & ~(HOST_WIDE_INT_M1U << (width - 1));
if (hi != 0)
result = width - floor_log2 (hi) - 2 - HOST_BITS_PER_WIDE_INT;
else if (lo != 0)
return NULL_TREE;
}
-/* Fold function call to builtin_bswap and the long and long long
+/* Fold function call to builtin_bswap and the short, long and long long
variants. Return NULL_TREE if no simplification can be made. */
static tree
fold_builtin_bswap (tree fndecl, tree arg)
{
HOST_WIDE_INT hi, width, r_hi = 0;
unsigned HOST_WIDE_INT lo, r_lo = 0;
- tree type;
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
- type = TREE_TYPE (arg);
width = TYPE_PRECISION (type);
lo = TREE_INT_CST_LOW (arg);
hi = TREE_INT_CST_HIGH (arg);
switch (DECL_FUNCTION_CODE (fndecl))
{
+ case BUILT_IN_BSWAP16:
case BUILT_IN_BSWAP32:
case BUILT_IN_BSWAP64:
{
}
if (width < HOST_BITS_PER_WIDE_INT)
- return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), r_lo);
+ return build_int_cst (type, r_lo);
else
- return build_int_cst_wide (TREE_TYPE (TREE_TYPE (fndecl)), r_lo, r_hi);
+ return build_int_cst_wide (type, r_lo, r_hi);
}
return NULL_TREE;
&& (MIN (src_align, dest_align) / BITS_PER_UNIT
>= (unsigned HOST_WIDE_INT) tree_low_cst (len, 1))))
{
- tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
if (!fn)
return NULL_TREE;
return build_call_expr_loc (loc, fn, 3, dest, src, len);
if (! operand_equal_p (TREE_OPERAND (src_base, 0),
TREE_OPERAND (dest_base, 0), 0))
return NULL_TREE;
- off = double_int_add (mem_ref_offset (src_base),
- shwi_to_double_int (src_offset));
- if (!double_int_fits_in_shwi_p (off))
+ off = mem_ref_offset (src_base) +
+ double_int::from_shwi (src_offset);
+ if (!off.fits_shwi ())
return NULL_TREE;
src_offset = off.low;
- off = double_int_add (mem_ref_offset (dest_base),
- shwi_to_double_int (dest_offset));
- if (!double_int_fits_in_shwi_p (off))
+ off = mem_ref_offset (dest_base) +
+ double_int::from_shwi (dest_offset);
+ if (!off.fits_shwi ())
return NULL_TREE;
dest_offset = off.low;
if (ranges_overlap_p (src_offset, maxsize,
else
return NULL_TREE;
- fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
if (!fn)
return NULL_TREE;
return build_call_expr_loc (loc, fn, 3, dest, src, len);
if (!refs_may_alias_p_1 (&destr, &srcr, false))
{
tree fn;
- fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
if (!fn)
return NULL_TREE;
return build_call_expr_loc (loc, fn, 3, dest, src, len);
if (optimize_function_for_size_p (cfun))
return NULL_TREE;
- fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
if (!fn)
return NULL_TREE;
return NULL_TREE;
}
- len = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
+ len = fold_convert_loc (loc, size_type_node, len);
+ len = size_binop_loc (loc, PLUS_EXPR, len, build_int_cst (size_type_node, 1));
return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)),
build_call_expr_loc (loc, fn, 3, dest, src, len));
}
&& !integer_zerop (len))
return NULL_TREE;
- fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
if (!fn)
return NULL_TREE;
- lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
+ lenp1 = size_binop_loc (loc, PLUS_EXPR,
+ fold_convert_loc (loc, size_type_node, len),
+ build_int_cst (size_type_node, 1));
/* We use dest twice in building our expression. Save it from
multiple expansions. */
dest = builtin_save_expr (dest);
return NULL_TREE;
/* OK transform into builtin memcpy. */
- fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
if (!fn)
return NULL_TREE;
+
+ len = fold_convert_loc (loc, size_type_node, len);
return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)),
build_call_expr_loc (loc, fn, 3, dest, src, len));
}
if (target_char_cast (arg2, &c))
return NULL_TREE;
- r = (char *) memchr (p1, c, tree_low_cst (len, 1));
+ r = (const char *) memchr (p1, c, tree_low_cst (len, 1));
if (r == NULL)
return build_int_cst (TREE_TYPE (arg1), 0);
fold_builtin_fma (location_t loc, tree arg0, tree arg1, tree arg2, tree type)
{
if (validate_arg (arg0, REAL_TYPE)
- && validate_arg(arg1, REAL_TYPE)
- && validate_arg(arg2, REAL_TYPE))
+ && validate_arg (arg1, REAL_TYPE)
+ && validate_arg (arg2, REAL_TYPE))
{
tree tem = fold_fma (loc, type, arg0, arg1, arg2);
if (tem)
case rvc_inf:
/* If arg is Inf or NaN and we're logb, return it. */
if (TREE_CODE (rettype) == REAL_TYPE)
- return fold_convert_loc (loc, rettype, arg);
+ {
+ /* For logb(-Inf) we have to return +Inf. */
+ if (real_isinf (value) && real_isneg (value))
+ {
+ REAL_VALUE_TYPE tem;
+ real_inf (&tem);
+ return build_real (rettype, tem);
+ }
+ return fold_convert_loc (loc, rettype, arg);
+ }
/* Fall through... */
case rvc_zero:
/* Zero may set errno and/or raise an exception for logb, also
CASE_FLT_FN (BUILT_IN_ISINF):
{
/* isinf(x) -> isgreater(fabs(x),DBL_MAX). */
- tree const isgr_fn = built_in_decls[BUILT_IN_ISGREATER];
+ tree const isgr_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
tree const type = TREE_TYPE (arg);
REAL_VALUE_TYPE r;
char buf[128];
case BUILT_IN_ISFINITE:
{
/* isfinite(x) -> islessequal(fabs(x),DBL_MAX). */
- tree const isle_fn = built_in_decls[BUILT_IN_ISLESSEQUAL];
+ tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
tree const type = TREE_TYPE (arg);
REAL_VALUE_TYPE r;
char buf[128];
{
/* isnormal(x) -> isgreaterequal(fabs(x),DBL_MIN) &
islessequal(fabs(x),DBL_MAX). */
- tree const isle_fn = built_in_decls[BUILT_IN_ISLESSEQUAL];
- tree const isge_fn = built_in_decls[BUILT_IN_ISGREATEREQUAL];
+ tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
+ tree const isge_fn = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
tree const type = TREE_TYPE (arg);
REAL_VALUE_TYPE rmax, rmin;
char buf[128];
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 isinf_fn = builtin_decl_explicit (BUILT_IN_ISINF);
tree tmp = NULL_TREE;
arg = builtin_save_expr (arg);
case BUILT_IN_CLASSIFY_TYPE:
return fold_builtin_classify_type (NULL_TREE);
+ case BUILT_IN_UNREACHABLE:
+ if (flag_sanitize & SANITIZE_UNREACHABLE
+ && (current_function_decl == NULL
+ || !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (current_function_decl))))
+ return ubsan_instrument_unreachable (loc);
+ break;
+
default:
break;
}
return fold_builtin_strlen (loc, type, arg0);
CASE_FLT_FN (BUILT_IN_FABS):
+ case BUILT_IN_FABSD32:
+ case BUILT_IN_FABSD64:
+ case BUILT_IN_FABSD128:
return fold_builtin_fabs (loc, arg0, type);
case BUILT_IN_ABS:
break;
CASE_FLT_FN (BUILT_IN_CCOS):
- return fold_builtin_ccos(loc, arg0, type, fndecl, /*hyper=*/ false);
+ return fold_builtin_ccos (loc, arg0, type, fndecl, /*hyper=*/ false);
CASE_FLT_FN (BUILT_IN_CCOSH):
- return fold_builtin_ccos(loc, arg0, type, fndecl, /*hyper=*/ true);
+ return fold_builtin_ccos (loc, arg0, type, fndecl, /*hyper=*/ true);
CASE_FLT_FN (BUILT_IN_CPROJ):
- return fold_builtin_cproj(loc, arg0, type);
+ return fold_builtin_cproj (loc, arg0, type);
CASE_FLT_FN (BUILT_IN_CSIN):
if (validate_arg (arg0, COMPLEX_TYPE)
CASE_FLT_FN (BUILT_IN_LLRINT):
return fold_fixed_mathfn (loc, fndecl, arg0);
+ case BUILT_IN_BSWAP16:
case BUILT_IN_BSWAP32:
case BUILT_IN_BSWAP64:
return fold_builtin_bswap (fndecl, arg0);
CASE_FLT_FN (BUILT_IN_DREM):
CASE_FLT_FN (BUILT_IN_REMAINDER):
if (validate_arg (arg0, REAL_TYPE)
- && validate_arg(arg1, REAL_TYPE))
+ && validate_arg (arg1, REAL_TYPE))
return do_mpfr_arg2 (arg0, arg1, type, mpfr_remainder);
break;
CASE_FLT_FN_REENT (BUILT_IN_GAMMA): /* GAMMA_R */
CASE_FLT_FN_REENT (BUILT_IN_LGAMMA): /* LGAMMA_R */
if (validate_arg (arg0, REAL_TYPE)
- && validate_arg(arg1, POINTER_TYPE))
+ && validate_arg (arg1, POINTER_TYPE))
return do_mpfr_lgamma_r (arg0, arg1, type);
break;
CASE_FLT_FN (BUILT_IN_ATAN2):
if (validate_arg (arg0, REAL_TYPE)
- && validate_arg(arg1, REAL_TYPE))
+ && validate_arg (arg1, REAL_TYPE))
return do_mpfr_arg2 (arg0, arg1, type, mpfr_atan2);
break;
CASE_FLT_FN (BUILT_IN_FDIM):
if (validate_arg (arg0, REAL_TYPE)
- && validate_arg(arg1, REAL_TYPE))
+ && validate_arg (arg1, REAL_TYPE))
return do_mpfr_arg2 (arg0, arg1, type, mpfr_dim);
break;
case BUILT_IN_STPCPY:
if (ignore)
{
- tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
if (!fn)
break;
return fold_builtin_fprintf (loc, fndecl, arg0, arg1, NULL_TREE,
ignore, fcode);
+ case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
+ return fold_builtin_atomic_always_lock_free (arg0, arg1);
+
+ case BUILT_IN_ATOMIC_IS_LOCK_FREE:
+ return fold_builtin_atomic_is_lock_free (arg0, arg1);
+
default:
break;
}
CASE_FLT_FN (BUILT_IN_REMQUO):
if (validate_arg (arg0, REAL_TYPE)
- && validate_arg(arg1, REAL_TYPE)
- && validate_arg(arg2, POINTER_TYPE))
+ && validate_arg (arg1, REAL_TYPE)
+ && validate_arg (arg2, POINTER_TYPE))
return do_mpfr_remquo (arg0, arg1, arg2);
break;
DECL_FUNCTION_CODE (fndecl));
case BUILT_IN_STRNCPY_CHK:
- return fold_builtin_strncpy_chk (loc, arg0, arg1, arg2, arg3, NULL_TREE);
+ case BUILT_IN_STPNCPY_CHK:
+ return fold_builtin_stxncpy_chk (loc, arg0, arg1, arg2, arg3, NULL_TREE,
+ ignore, fcode);
case BUILT_IN_STRNCAT_CHK:
return fold_builtin_strncat_chk (loc, fndecl, arg0, arg1, arg2, arg3);
return NULL_TREE;
}
-/* 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. */
+/* Construct a new CALL_EXPR to FNDECL using the tail of the argument
+ list ARGS along with N new arguments in NEWARGS. SKIP is the number
+ of arguments in ARGS to be omitted. OLDNARGS is the number of
+ elements in ARGS. */
static tree
-fold_builtin_varargs (location_t loc, tree fndecl, tree exp,
- bool ignore ATTRIBUTE_UNUSED)
+rewrite_call_expr_valist (location_t loc, int oldnargs, tree *args,
+ int skip, tree fndecl, int n, va_list newargs)
{
- enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
- tree ret = NULL_TREE;
+ int nargs = oldnargs - skip + n;
+ tree *buffer;
- switch (fcode)
+ if (n > 0)
{
- case BUILT_IN_SPRINTF_CHK:
- case BUILT_IN_VSPRINTF_CHK:
- ret = fold_builtin_sprintf_chk (loc, exp, fcode);
- break;
+ int i, j;
- case BUILT_IN_SNPRINTF_CHK:
- case BUILT_IN_VSNPRINTF_CHK:
- ret = fold_builtin_snprintf_chk (loc, exp, NULL_TREE, fcode);
- break;
+ buffer = XALLOCAVEC (tree, nargs);
+ for (i = 0; i < n; i++)
+ buffer[i] = va_arg (newargs, tree);
+ for (j = skip; j < oldnargs; j++, i++)
+ buffer[i] = args[j];
+ }
+ else
+ buffer = args + skip;
- case BUILT_IN_FPCLASSIFY:
- ret = fold_builtin_fpclassify (loc, exp);
- break;
+ return build_call_expr_loc_array (loc, fndecl, nargs, buffer);
+}
- default:
- break;
- }
- if (ret)
- {
- ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
- SET_EXPR_LOCATION (ret, loc);
- TREE_NO_WARNING (ret) = 1;
- return ret;
- }
- return NULL_TREE;
+/* Construct a new CALL_EXPR to FNDECL using the tail of the argument
+ list ARGS along with N new arguments specified as the "..."
+ parameters. SKIP is the number of arguments in ARGS to be omitted.
+ OLDNARGS is the number of elements in ARGS. */
+
+static tree
+rewrite_call_expr_array (location_t loc, int oldnargs, tree *args,
+ int skip, tree fndecl, int n, ...)
+{
+ va_list ap;
+ tree t;
+
+ va_start (ap, n);
+ t = rewrite_call_expr_valist (loc, oldnargs, args, skip, fndecl, n, ap);
+ va_end (ap);
+
+ return t;
}
/* Return true if FNDECL shouldn't be folded right now.
been inlined, otherwise e.g. -D_FORTIFY_SOURCE checking
might not be performed. */
-static bool
+bool
avoid_folding_inline_builtin (tree fndecl)
{
return (DECL_DECLARED_INLINE_P (fndecl)
VEC. */
tree
-build_call_expr_loc_vec (location_t loc, tree fndecl, VEC(tree,gc) *vec)
+build_call_expr_loc_vec (location_t loc, tree fndecl, vec<tree, va_gc> *vec)
{
- return build_call_expr_loc_array (loc, fndecl, VEC_length (tree, vec),
- VEC_address (tree, vec));
+ return build_call_expr_loc_array (loc, fndecl, vec_safe_length (vec),
+ vec_safe_address (vec));
}
return build_call_array_loc (loc, type, fn, n, argarray);
}
-/* Construct a new CALL_EXPR to FNDECL using the tail of the argument
- list ARGS along with N new arguments in NEWARGS. SKIP is the number
- of arguments in ARGS to be omitted. OLDNARGS is the number of
- elements in ARGS. */
-
-static tree
-rewrite_call_expr_valist (location_t loc, int oldnargs, tree *args,
- int skip, tree fndecl, int n, va_list newargs)
-{
- int nargs = oldnargs - skip + n;
- tree *buffer;
-
- if (n > 0)
- {
- int i, j;
-
- buffer = XALLOCAVEC (tree, nargs);
- for (i = 0; i < n; i++)
- buffer[i] = va_arg (newargs, tree);
- for (j = skip; j < oldnargs; j++, i++)
- buffer[i] = args[j];
- }
- else
- buffer = args + skip;
-
- return build_call_expr_loc_array (loc, fndecl, nargs, buffer);
-}
-
-/* Construct a new CALL_EXPR to FNDECL using the tail of the argument
- list ARGS along with N new arguments specified as the "..."
- parameters. SKIP is the number of arguments in ARGS to be omitted.
- OLDNARGS is the number of elements in ARGS. */
-
-static tree
-rewrite_call_expr_array (location_t loc, int oldnargs, tree *args,
- int skip, tree fndecl, int n, ...)
-{
- va_list ap;
- tree t;
-
- va_start (ap, n);
- t = rewrite_call_expr_valist (loc, oldnargs, args, skip, fndecl, n, ap);
- va_end (ap);
-
- return t;
-}
-
/* Construct a new CALL_EXPR using the tail of the argument list of EXP
along with N new arguments specified as the "..." parameters. SKIP
is the number of arguments in EXP to be omitted. This function is used
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 (const_tree callexpr, ...)
-{
- 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 = (enum tree_code) va_arg (ap, int);
- 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
if (p2[1] != '\0')
return NULL_TREE;
- fn = implicit_built_in_decls[BUILT_IN_STRCHR];
+ fn = builtin_decl_implicit (BUILT_IN_STRCHR);
if (!fn)
return NULL_TREE;
if (! integer_zerop (s2))
return NULL_TREE;
- fn = implicit_built_in_decls[BUILT_IN_STRCHR];
+ fn = builtin_decl_implicit (BUILT_IN_STRCHR);
if (!fn)
return NULL_TREE;
if (p2[1] != '\0')
return NULL_TREE; /* Really call strpbrk. */
- fn = implicit_built_in_decls[BUILT_IN_STRCHR];
+ fn = builtin_decl_implicit (BUILT_IN_STRCHR);
if (!fn)
return NULL_TREE;
{
/* See if we can store by pieces into (dst + strlen(dst)). */
tree newdst, call;
- tree strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN];
- tree strcpy_fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+ tree strcpy_fn = builtin_decl_implicit (BUILT_IN_STRCPY);
if (!strlen_fn || !strcpy_fn)
return NULL_TREE;
if (TREE_CODE (len) == INTEGER_CST && p
&& compare_tree_int (len, strlen (p)) >= 0)
{
- tree fn = implicit_built_in_decls[BUILT_IN_STRCAT];
+ tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
/* If the replacement _DECL isn't initialized, don't do the
transformation. */
if (p1 && p2)
{
const size_t r = strspn (p1, p2);
- return size_int (r);
+ return build_int_cst (size_type_node, r);
}
/* If either argument is "", return NULL_TREE. */
if (p1 && p2)
{
const size_t r = strcspn (p1, p2);
- return size_int (r);
+ return build_int_cst (size_type_node, r);
}
/* If the first argument is "", return NULL_TREE. */
/* If the second argument is "", return __builtin_strlen(s1). */
if (p2 && *p2 == '\0')
{
- tree fn = implicit_built_in_decls[BUILT_IN_STRLEN];
+ tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
/* If the replacement _DECL isn't initialized, don't do the
transformation. */
{
/* If we're using an unlocked function, assume the other unlocked
functions exist explicitly. */
- tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
- : implicit_built_in_decls[BUILT_IN_FPUTC];
- tree const fn_fwrite = unlocked ? built_in_decls[BUILT_IN_FWRITE_UNLOCKED]
- : implicit_built_in_decls[BUILT_IN_FWRITE];
+ tree const fn_fputc = (unlocked
+ ? builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED)
+ : builtin_decl_implicit (BUILT_IN_FPUTC));
+ tree const fn_fwrite = (unlocked
+ ? builtin_decl_explicit (BUILT_IN_FWRITE_UNLOCKED)
+ : builtin_decl_implicit (BUILT_IN_FWRITE));
/* If the return value is used, don't do the transformation. */
if (!ignore)
tree fntype = TREE_TYPE (current_function_decl);
int nargs = call_expr_nargs (exp);
tree arg;
+ /* There is good chance the current input_location points inside the
+ definition of the va_start macro (perhaps on the token for
+ builtin) in a system header, so warnings will not be emitted.
+ Use the location in real source code. */
+ source_location current_location =
+ linemap_unwind_to_first_non_reserved_loc (line_table, input_location,
+ NULL);
if (!stdarg_p (fntype))
{
{
/* Evidently an out of date version of <stdarg.h>; can't validate
va_start's second argument, but can still work as intended. */
- warning (0, "%<__builtin_next_arg%> called without an argument");
+ warning_at (current_location,
+ OPT_Wvarargs,
+ "%<__builtin_next_arg%> called without an argument");
return true;
}
else if (nargs > 1)
argument. We just warn and set the arg to be the last
argument so that we will get wrong-code because of
it. */
- warning (0, "second parameter of %<va_start%> not last named argument");
+ warning_at (current_location,
+ OPT_Wvarargs,
+ "second parameter of %<va_start%> not last named argument");
}
/* Undefined by C99 7.15.1.4p4 (va_start):
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");
+ {
+ warning_at (current_location,
+ OPT_Wvarargs,
+ "undefined behaviour when second parameter of "
+ "%<va_start%> is declared with %<register%> storage");
+ }
/* We want to verify the second parameter just once before the tree
optimizers are run and then avoid keeping it in the tree,
/* If the format doesn't contain % args or %%, use strcpy. */
if (strchr (fmt_str, target_percent) == NULL)
{
- tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
if (!fn)
return NULL_TREE;
else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0)
{
tree fn;
- fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ fn = builtin_decl_implicit (BUILT_IN_STRCPY);
if (!fn)
return NULL_TREE;
if (call && retval)
{
retval = fold_convert_loc
- (loc, TREE_TYPE (TREE_TYPE (implicit_built_in_decls[BUILT_IN_SPRINTF])),
+ (loc, TREE_TYPE (TREE_TYPE (builtin_decl_implicit (BUILT_IN_SPRINTF))),
retval);
return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval);
}
/* If the format doesn't contain % args or %%, use strcpy. */
if (strchr (fmt_str, target_percent) == NULL)
{
- tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
size_t len = strlen (fmt_str);
/* Don't optimize snprintf (buf, 4, "abc", ptr++). */
/* If the format is "%s", use strcpy if the result isn't used. */
else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0)
{
- tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
unsigned HOST_WIDE_INT origlen;
/* Don't crash on snprintf (str1, cst, "%s"). */
if (call && retval)
{
- tree fn = built_in_decls[BUILT_IN_SNPRINTF];
+ tree fn = builtin_decl_explicit (BUILT_IN_SNPRINTF);
retval = fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fn)), retval);
return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval);
}
switch (fcode)
{
case BUILT_IN_MEMCPY_CHK:
- fn = built_in_decls[BUILT_IN_MEMCPY];
+ fn = builtin_decl_explicit (BUILT_IN_MEMCPY);
break;
case BUILT_IN_MEMPCPY_CHK:
- fn = built_in_decls[BUILT_IN_MEMPCPY];
+ fn = builtin_decl_explicit (BUILT_IN_MEMPCPY);
break;
case BUILT_IN_MEMMOVE_CHK:
- fn = built_in_decls[BUILT_IN_MEMMOVE];
+ fn = builtin_decl_explicit (BUILT_IN_MEMMOVE);
break;
case BUILT_IN_MEMSET_CHK:
- fn = built_in_decls[BUILT_IN_MEMSET];
+ fn = builtin_decl_explicit (BUILT_IN_MEMSET);
break;
default:
break;
normal __memcpy_chk. */
if (readonly_data_expr (src))
{
- tree fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+ tree fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
if (!fn)
return NULL_RTX;
fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 4,
break;
case BUILT_IN_STRNCAT_CHK:
case BUILT_IN_STRNCPY_CHK:
+ case BUILT_IN_STPNCPY_CHK:
len = CALL_EXPR_ARG (exp, 2);
size = CALL_EXPR_ARG (exp, 3);
break;
return;
if (SSA_VAR_P (arg))
- warning_at (tree_nonartificial_location (exp),
- 0, "%Kattempt to free a non-heap object %qD", exp, arg);
+ warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object,
+ "%Kattempt to free a non-heap object %qD", exp, arg);
else
- warning_at (tree_nonartificial_location (exp),
- 0, "%Kattempt to free a non-heap object", exp);
+ warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object,
+ "%Kattempt to free a non-heap object", exp);
}
/* Fold a call to __builtin_object_size with arguments PTR and OST,
{
bytes = compute_builtin_object_size (ptr, object_size_type);
if (double_int_fits_to_tree_p (size_type_node,
- uhwi_to_double_int (bytes)))
+ double_int::from_uhwi (bytes)))
return build_int_cstu (size_type_node, bytes);
}
else if (TREE_CODE (ptr) == SSA_NAME)
bytes = compute_builtin_object_size (ptr, object_size_type);
if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 ? -1 : 0)
&& double_int_fits_to_tree_p (size_type_node,
- uhwi_to_double_int (bytes)))
+ double_int::from_uhwi (bytes)))
return build_int_cstu (size_type_node, bytes);
}
{
/* (void) __mempcpy_chk () can be optimized into
(void) __memcpy_chk (). */
- fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+ fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
if (!fn)
return NULL_TREE;
switch (fcode)
{
case BUILT_IN_MEMCPY_CHK:
- fn = built_in_decls[BUILT_IN_MEMCPY];
+ fn = builtin_decl_explicit (BUILT_IN_MEMCPY);
break;
case BUILT_IN_MEMPCPY_CHK:
- fn = built_in_decls[BUILT_IN_MEMPCPY];
+ fn = builtin_decl_explicit (BUILT_IN_MEMPCPY);
break;
case BUILT_IN_MEMMOVE_CHK:
- fn = built_in_decls[BUILT_IN_MEMMOVE];
+ fn = builtin_decl_explicit (BUILT_IN_MEMMOVE);
break;
case BUILT_IN_MEMSET_CHK:
- fn = built_in_decls[BUILT_IN_MEMSET];
+ fn = builtin_decl_explicit (BUILT_IN_MEMSET);
break;
default:
break;
/* If return value of __stpcpy_chk is ignored,
optimize into __strcpy_chk. */
- fn = built_in_decls[BUILT_IN_STRCPY_CHK];
+ fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK);
if (!fn)
return NULL_TREE;
/* If c_strlen returned something, but not a constant,
transform __strcpy_chk into __memcpy_chk. */
- fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+ fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
if (!fn)
return NULL_TREE;
- len = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
+ len = fold_convert_loc (loc, size_type_node, len);
+ len = size_binop_loc (loc, PLUS_EXPR, len,
+ build_int_cst (size_type_node, 1));
return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)),
build_call_expr_loc (loc, fn, 4,
dest, src, len, size));
}
/* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */
- fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK
- ? BUILT_IN_STPCPY : BUILT_IN_STRCPY];
+ fn = builtin_decl_explicit (fcode == BUILT_IN_STPCPY_CHK
+ ? BUILT_IN_STPCPY : BUILT_IN_STRCPY);
if (!fn)
return NULL_TREE;
return build_call_expr_loc (loc, fn, 2, dest, src);
}
-/* Fold a call to the __strncpy_chk builtin. DEST, SRC, LEN, and SIZE
+/* Fold a call to the __st{r,p}ncpy_chk builtin. DEST, SRC, LEN, and SIZE
are the arguments to the call. If MAXLEN is not NULL, it is maximum
- length passed as third argument. */
+ length passed as third argument. IGNORE is true if return value can be
+ ignored. FCODE is the BUILT_IN_* code of the builtin. */
tree
-fold_builtin_strncpy_chk (location_t loc, tree dest, tree src,
- tree len, tree size, tree maxlen)
+fold_builtin_stxncpy_chk (location_t loc, tree dest, tree src,
+ tree len, tree size, tree maxlen, bool ignore,
+ enum built_in_function fcode)
{
tree fn;
|| !validate_arg (size, INTEGER_TYPE))
return NULL_TREE;
+ if (fcode == BUILT_IN_STPNCPY_CHK && ignore)
+ {
+ /* If return value of __stpncpy_chk is ignored,
+ optimize into __strncpy_chk. */
+ fn = builtin_decl_explicit (BUILT_IN_STRNCPY_CHK);
+ if (fn)
+ return build_call_expr_loc (loc, fn, 4, dest, src, len, size);
+ }
+
if (! host_integerp (size, 1))
return NULL_TREE;
return NULL_TREE;
}
- /* If __builtin_strncpy_chk is used, assume strncpy is available. */
- fn = built_in_decls[BUILT_IN_STRNCPY];
+ /* If __builtin_st{r,p}ncpy_chk is used, assume st{r,p}ncpy is available. */
+ fn = builtin_decl_explicit (fcode == BUILT_IN_STPNCPY_CHK
+ ? BUILT_IN_STPNCPY : BUILT_IN_STRNCPY);
if (!fn)
return NULL_TREE;
return NULL_TREE;
/* If __builtin_strcat_chk is used, assume strcat is available. */
- fn = built_in_decls[BUILT_IN_STRCAT];
+ fn = builtin_decl_explicit (BUILT_IN_STRCAT);
if (!fn)
return NULL_TREE;
&& ! tree_int_cst_lt (len, src_len))
{
/* If LEN >= strlen (SRC), optimize into __strcat_chk. */
- fn = built_in_decls[BUILT_IN_STRCAT_CHK];
+ fn = builtin_decl_explicit (BUILT_IN_STRCAT_CHK);
if (!fn)
return NULL_TREE;
}
/* If __builtin_strncat_chk is used, assume strncat is available. */
- fn = built_in_decls[BUILT_IN_STRNCAT];
+ fn = builtin_decl_explicit (BUILT_IN_STRNCAT);
if (!fn)
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];
+ fn = builtin_decl_explicit (fcode == BUILT_IN_VSPRINTF_CHK
+ ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF);
if (!fn)
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];
+ fn = builtin_decl_explicit (fcode == BUILT_IN_VSNPRINTF_CHK
+ ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF);
if (!fn)
return NULL_TREE;
BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length
passed as second argument. */
-tree
+static tree
fold_builtin_snprintf_chk (location_t loc, tree exp, tree maxlen,
enum built_in_function fcode)
{
CALL_EXPR_ARGP (exp), maxlen, fcode);
}
+/* Builtins with folding operations that operate on "..." arguments
+ need special handling; we need to store the arguments in a convenient
+ data structure before attempting any folding. Fortunately there are
+ only a few builtins that fall into this category. FNDECL is the
+ function, EXP is the CALL_EXPR for the call, and IGNORE is true if the
+ result of the function call is ignored. */
+
+static tree
+fold_builtin_varargs (location_t loc, tree fndecl, tree exp,
+ 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 = fold_builtin_sprintf_chk (loc, exp, fcode);
+ break;
+
+ case BUILT_IN_SNPRINTF_CHK:
+ case BUILT_IN_VSNPRINTF_CHK:
+ ret = fold_builtin_snprintf_chk (loc, exp, NULL_TREE, fcode);
+ break;
+
+ case BUILT_IN_FPCLASSIFY:
+ ret = fold_builtin_fpclassify (loc, exp);
+ break;
+
+ default:
+ break;
+ }
+ if (ret)
+ {
+ ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
+ SET_EXPR_LOCATION (ret, loc);
+ TREE_NO_WARNING (ret) = 1;
+ return ret;
+ }
+ return NULL_TREE;
+}
+
/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins.
FMT and ARG are the arguments to the call; we don't fold cases with
more than 2 arguments, and ARG may be null if this is a 1-argument case.
{
/* If we're using an unlocked function, assume the other
unlocked functions exist explicitly. */
- fn_putchar = built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED];
- fn_puts = built_in_decls[BUILT_IN_PUTS_UNLOCKED];
+ fn_putchar = builtin_decl_explicit (BUILT_IN_PUTCHAR_UNLOCKED);
+ fn_puts = builtin_decl_explicit (BUILT_IN_PUTS_UNLOCKED);
}
else
{
- fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR];
- fn_puts = implicit_built_in_decls[BUILT_IN_PUTS];
+ fn_putchar = builtin_decl_implicit (BUILT_IN_PUTCHAR);
+ fn_puts = builtin_decl_implicit (BUILT_IN_PUTS);
}
if (!init_target_chars ())
{
/* If we're using an unlocked function, assume the other
unlocked functions exist explicitly. */
- fn_fputc = built_in_decls[BUILT_IN_FPUTC_UNLOCKED];
- fn_fputs = built_in_decls[BUILT_IN_FPUTS_UNLOCKED];
+ fn_fputc = builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED);
+ fn_fputs = builtin_decl_explicit (BUILT_IN_FPUTS_UNLOCKED);
}
else
{
- fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC];
- fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS];
+ fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC);
+ fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS);
}
if (!init_target_chars ())
&& host_integerp (arg1, 0)
&& TREE_CODE (arg2) == REAL_CST && !TREE_OVERFLOW (arg2))
{
- const HOST_WIDE_INT n = tree_low_cst(arg1, 0);
+ const HOST_WIDE_INT n = tree_low_cst (arg1, 0);
const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg2);
if (n == (long)n
negative integer. */
if (real_isfinite (ra)
&& ra->cl != rvc_zero
- && !(real_isneg(ra) && real_isinteger(ra, TYPE_MODE (type))))
+ && !(real_isneg (ra) && real_isinteger (ra, TYPE_MODE (type))))
{
const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
const int prec = fmt->p;
mpc_t m;
mpc_init2 (m, prec);
- mpfr_from_real (mpc_realref(m), re, rnd);
- mpfr_from_real (mpc_imagref(m), im, rnd);
+ mpfr_from_real (mpc_realref (m), re, rnd);
+ mpfr_from_real (mpc_imagref (m), im, rnd);
mpfr_clear_flags ();
inexact = func (m, m, crnd);
result = do_mpc_ckconv (m, type, inexact, /*force_convert=*/ 0);
mpc_init2 (m0, prec);
mpc_init2 (m1, prec);
- mpfr_from_real (mpc_realref(m0), re0, rnd);
- mpfr_from_real (mpc_imagref(m0), im0, rnd);
- mpfr_from_real (mpc_realref(m1), re1, rnd);
- mpfr_from_real (mpc_imagref(m1), im1, rnd);
+ mpfr_from_real (mpc_realref (m0), re0, rnd);
+ mpfr_from_real (mpc_imagref (m0), im0, rnd);
+ mpfr_from_real (mpc_realref (m1), re1, rnd);
+ mpfr_from_real (mpc_imagref (m1), im1, rnd);
mpfr_clear_flags ();
inexact = func (m0, m0, m1, crnd);
result = do_mpc_ckconv (m0, type, inexact, do_nonfinite);
return NULL_TREE;
}
-/* Look up the function in built_in_decls that corresponds to DECL
+/* Look up the function in builtin_decl that corresponds to DECL
and set ASMSPEC as its user assembler name. DECL must be a
function decl that declares a builtin. */
&& DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
&& asmspec != 0);
- builtin = built_in_decls [DECL_FUNCTION_CODE (decl)];
+ builtin = builtin_decl_explicit (DECL_FUNCTION_CODE (decl));
set_user_assembler_name (builtin, asmspec);
switch (DECL_FUNCTION_CODE (decl))
{
{
case BUILT_IN_ABS:
case BUILT_IN_ALLOCA:
+ case BUILT_IN_ALLOCA_WITH_ALIGN:
+ case BUILT_IN_BSWAP16:
case BUILT_IN_BSWAP32:
case BUILT_IN_BSWAP64:
case BUILT_IN_CLZ: